diff options
Diffstat (limited to 'wpa_supplicant')
119 files changed, 34515 insertions, 6999 deletions
diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog index 8abafb24d1262..1ac79b4ae5f91 100644 --- a/wpa_supplicant/ChangeLog +++ b/wpa_supplicant/ChangeLog @@ -1,5 +1,377 @@ ChangeLog for wpa_supplicant +2015-03-15 - v2.4 + * allow OpenSSL cipher configuration to be set for internal EAP server + (openssl_ciphers parameter) + * fixed number of small issues based on hwsim test case failures and + static analyzer reports + * P2P: + - add new=<0/1> flag to P2P-DEVICE-FOUND events + - add passive channels in invitation response from P2P Client + - enable nl80211 P2P_DEVICE support by default + - fix regresssion in disallow_freq preventing search on social + channels + - fix regressions in P2P SD query processing + - try to re-invite with social operating channel if no common channels + in invitation + - allow cross connection on parent interface (this fixes number of + use cases with nl80211) + - add support for P2P services (P2PS) + - add p2p_go_ctwindow configuration parameter to allow GO CTWindow to + be configured + * increase postponing of EAPOL-Start by one second with AP/GO that + supports WPS 2.0 (this makes it less likely to trigger extra roundtrip + of identity frames) + * add support for PMKSA caching with SAE + * add support for control mesh BSS (IEEE 802.11s) operations + * fixed number of issues with D-Bus P2P commands + * fixed regression in ap_scan=2 special case for WPS + * fixed macsec_validate configuration + * add a workaround for incorrectly behaving APs that try to use + EAPOL-Key descriptor version 3 when the station supports PMF even if + PMF is not enabled on the AP + * allow TLS v1.1 and v1.2 to be negotiated by default; previous behavior + of disabling these can be configured to work around issues with broken + servers with phase1="tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1" + * add support for Suite B (128-bit and 192-bit level) key management and + cipher suites + * add WMM-AC support (WMM_AC_ADDTS/WMM_AC_DELTS) + * improved BSS Transition Management processing + * add support for neighbor report + * add support for link measurement + * fixed expiration of BSS entry with all-zeros BSSID + * add optional LAST_ID=x argument to LIST_NETWORK to allow all + configured networks to be listed even with huge number of network + profiles + * add support for EAP Re-Authentication Protocol (ERP) + * fixed EAP-IKEv2 fragmentation reassembly + * improved PKCS#11 configuration for OpenSSL + * set stdout to be line-buffered + * add TDLS channel switch configuration + * add support for MAC address randomization in scans with nl80211 + * enable HT for IBSS if supported by the driver + * add BSSID black and white lists (bssid_blacklist, bssid_whitelist) + * add support for domain_suffix_match with GnuTLS + * add OCSP stapling client support with GnuTLS + * include peer certificate in EAP events even without a separate probe + operation; old behavior can be restored with cert_in_cb=0 + * add peer ceritficate alt subject name to EAP events + (CTRL-EVENT-EAP-PEER-ALT) + * add domain_match network profile parameter (similar to + domain_suffix_match, but full match is required) + * enable AP/GO mode HT Tx STBC automatically based on driver support + * add ANQP-QUERY-DONE event to provide information on ANQP parsing + status + * allow passive scanning to be forced with passive_scan=1 + * add a workaround for Linux packet socket behavior when interface is in + bridge + * increase 5 GHz band preference in BSS selection (estimate SNR, if info + not available from driver; estimate maximum throughput based on common + HT/VHT/specific TX rate support) + * add INTERWORKING_ADD_NETWORK ctrl_iface command; this can be used to + implement Interworking network selection behavior in upper layers + software components + * add optional reassoc_same_bss_optim=1 (disabled by default) + optimization to avoid unnecessary Authentication frame exchange + * extend TDLS frame padding workaround to cover all packets + * allow wpa_supplicant to recover nl80211 functionality if the cfg80211 + module gets removed and reloaded without restarting wpa_supplicant + * allow hostapd DFS implementation to be used in wpa_supplicant AP mode + +2014-10-09 - v2.3 + * fixed number of minor issues identified in static analyzer warnings + * fixed wfd_dev_info to be more careful and not read beyond the buffer + when parsing invalid information for P2P-DEVICE-FOUND + * extended P2P and GAS query operations to support drivers that have + maximum remain-on-channel time below 1000 ms (500 ms is the current + minimum supported value) + * added p2p_search_delay parameter to make the default p2p_find delay + configurable + * improved P2P operating channel selection for various multi-channel + concurrency cases + * fixed some TDLS failure cases to clean up driver state + * fixed dynamic interface addition cases with nl80211 to avoid adding + ifindex values to incorrect interface to skip foreign interface events + properly + * added TDLS workaround for some APs that may add extra data to the + end of a short frame + * fixed EAP-AKA' message parser with multiple AT_KDF attributes + * added configuration option (p2p_passphrase_len) to allow longer + passphrases to be generated for P2P groups + * fixed IBSS channel configuration in some corner cases + * improved HT/VHT/QoS parameter setup for TDLS + * modified D-Bus interface for P2P peers/groups + * started to use constant time comparison for various password and hash + values to reduce possibility of any externally measurable timing + differences + * extended explicit clearing of freed memory and expired keys to avoid + keeping private data in memory longer than necessary + * added optional scan_id parameter to the SCAN command to allow manual + scan requests for active scans for specific configured SSIDs + * fixed CTRL-EVENT-REGDOM-CHANGE event init parameter value + * added option to set Hotspot 2.0 Rel 2 update_identifier in network + configuration to support external configuration + * modified Android PNO functionality to send Probe Request frames only + for hidden SSIDs (based on scan_ssid=1) + * added generic mechanism for adding vendor elements into frames at + runtime (VENDOR_ELEM_ADD, VENDOR_ELEM_GET, VENDOR_ELEM_REMOVE) + * added fields to show unrecognized vendor elements in P2P_PEER + * removed EAP-TTLS/MSCHAPv2 interoperability workaround so that + MS-CHAP2-Success is required to be present regardless of + eap_workaround configuration + * modified EAP fast session resumption to allow results to be used only + with the same network block that generated them + * extended freq_list configuration to apply for sched_scan as well as + normal scan + * modified WPS to merge mixed-WPA/WPA2 credentials from a single session + * fixed nl80211/RTM_DELLINK processing when a P2P GO interface is + removed from a bridge + * fixed number of small P2P issues to make negotiations more robust in + corner cases + * added experimental support for using temporary, random local MAC + address (mac_addr and preassoc_mac_addr parameters); this is disabled + by default (i.e., previous behavior of using permanent address is + maintained if configuration is not changed) + * added D-Bus interface for setting/clearing WFD IEs + * fixed TDLS AID configuration for VHT + * modified -m<conf> configuration file to be used only for the P2P + non-netdev management device and do not load this for the default + station interface or load the station interface configuration for + the P2P management interface + * fixed external MAC address changes while wpa_supplicant is running + * started to enable HT (if supported by the driver) for IBSS + * fixed wpa_cli action script execution to use more robust mechanism + (CVE-2014-3686) + +2014-06-04 - v2.2 + * added DFS indicator to get_capability freq + * added/fixed nl80211 functionality + - BSSID/frequency hint for driver-based BSS selection + - fix tearing down WDS STA interfaces + - support vendor specific driver command + (VENDOR <vendor id> <sub command id> [<hex formatted data>]) + - GO interface teardown optimization + - allow beacon interval to be configured for IBSS + - add SHA256-based AKM suites to CONNECT/ASSOCIATE commands + * removed unused NFC_RX_HANDOVER_REQ and NFC_RX_HANDOVER_SEL control + interface commands (the more generic NFC_REPORT_HANDOVER is now used) + * fixed MSCHAP UTF-8 to UCS-2 conversion for three-byte encoding; + this fixes password with include UTF-8 characters that use + three-byte encoding EAP methods that use NtPasswordHash + * fixed couple of sequencies where radio work items could get stuck, + e.g., when rfkill blocking happens during scanning or when + scan-for-auth workaround is used + * P2P enhancements/fixes + - enable enable U-APSD on GO automatically if the driver indicates + support for this + - fixed some service discovery cases with broadcast queries not being + sent to all stations + - fixed Probe Request frame triggering invitation to trigger only a + single invitation instance even if multiple Probe Request frames are + received + - fixed a potential NULL pointer dereference crash when processing an + invalid Invitation Request frame + - add optional configuration file for the P2P_DEVICE parameters + - optimize scan for GO during persistent group invocation + - fix possible segmentation fault when PBC overlap is detected while + using a separate P2P group interface + - improve GO Negotiation robustness by allowing GO Negotiation + Confirmation to be retransmitted + - do use freed memory on device found event when P2P NFC + * added phase1 network parameter options for disabling TLS v1.1 and v1.2 + to allow workarounds with misbehaving AAA servers + (tls_disable_tlsv1_1=1 and tls_disable_tlsv1_2=1) + * added support for OCSP stapling to validate AAA server certificate + during TLS exchange + * Interworking/Hotspot 2.0 enhancements + - prefer the last added network in Interworking connection to make the + behavior more consistent with likely user expectation + - roaming partner configuration (roaming_partner within a cred block) + - support Hotspot 2.0 Release 2 + * "hs20_anqp_get <BSSID> 8" to request OSU Providers list + * "hs20_icon_request <BSSID> <icon filename>" to request icon files + * "fetch_osu" and "cancel_osu_fetch" to start/stop full OSU provider + search (all suitable APs in scan results) + * OSEN network for online signup connection + * min_{dl,ul}_bandwidth_{home,roaming} cred parameters + * max_bss_load cred parameter + * req_conn_capab cred parameter + * sp_priority cred parameter + * ocsp cred parameter + * slow down automatic connection attempts on EAP failure to meet + required behavior (no more than 10 retries within a 10-minute + interval) + * sample implementation of online signup client (both SPP and + OMA-DM protocols) (hs20/client/*) + - fixed GAS indication for additional comeback delay with status + code 95 + - extend ANQP_GET to accept Hotspot 2.0 subtypes + ANQP_GET <addr> <info id>[,<info id>]... + [,hs20:<subtype>][...,hs20:<subtype>] + - add control interface events CRED-ADDED <id>, + CRED-MODIFIED <id> <field>, CRED-REMOVED <id> + - add "GET_CRED <id> <field>" command + - enable FT for the connection automatically if the AP advertises + support for this + - fix a case where auto_interworking=1 could end up stopping scanning + * fixed TDLS interoperability issues with supported operating class in + some deployed stations + * internal TLS implementation enhancements/fixes + - add SHA256-based cipher suites + - add DHE-RSA cipher suites + - fix X.509 validation of PKCS#1 signature to check for extra data + * fixed PTK derivation for CCMP-256 and GCMP-256 + * added "reattach" command for fast reassociate-back-to-same-BSS + * allow PMF to be enabled for AP mode operation with the ieee80211w + parameter + * added "get_capability tdls" command + * added option to set config blobs through control interface with + "SET blob <name> <hexdump>" + * D-Bus interface extensions/fixes + - make p2p_no_group_iface configurable + - declare ServiceDiscoveryRequest method properly + - export peer's device address as a property + - make reassociate command behave like the control interface one, + i.e., to allow connection from disconnected state + * added optional "freq=<channel ranges>" parameter to SET pno + * added optional "freq=<channel ranges>" parameter to SELECT_NETWORK + * fixed OBSS scan result processing for 20/40 MHz co-ex report + * remove WPS 1.0 only support, i.e., WSC 2.0 support is now enabled + whenever CONFIG_WPS=y is set + * fixed regression in parsing of WNM Sleep Mode exit key data + * fixed potential segmentation fault and memory leaks in WNM neighbor + report processing + * EAP-pwd fixes + - fragmentation of PWD-Confirm-Resp + - fix memory leak when fragmentation is used + - fix possible segmentation fault on EAP method deinit if an invalid + group is negotiated + * added MACsec/IEEE Std 802.1X-2010 PAE implementation (currently + available only with the macsec_qca driver wrapper) + * fixed EAP-SIM counter-too-small message + * added 'dup_network <id_s> <id_d> <name>' command; this can be used to + clone the psk field without having toextract it from wpa_supplicant + * fixed GSM authentication on USIM + * added support for usin epoll in eloop (CONFIG_ELOOP_EPOLL=y) + * fixed some concurrent virtual interface cases with dedicated P2P + management interface to not catch events from removed interface (this + could result in the management interface getting disabled) + * fixed a memory leak in SAE random number generation + * fixed off-by-one bounds checking in printf_encode() + - this could result in some control interface ATTACH command cases + terminating wpa_supplicant + * fixed EAPOL-Key exchange when GCMP is used with SHA256-based AKM + * various bug fixes + +2014-02-04 - v2.1 + * added support for simultaneous authentication of equals (SAE) for + stronger password-based authentication with WPA2-Personal + * improved P2P negotiation and group formation robustness + - avoid unnecessary Dialog Token value changes during retries + - avoid more concurrent scanning cases during full group formation + sequence + - do not use potentially obsolete scan result data from driver + cache for peer discovery/updates + - avoid undesired re-starting of GO negotiation based on Probe + Request frames + - increase GO Negotiation and Invitation timeouts to address busy + environments and peers that take long time to react to messages, + e.g., due to power saving + - P2P Device interface type + * improved P2P channel selection (use more peer information and allow + more local options) + * added support for optional per-device PSK assignment by P2P GO + (wpa_cli p2p_set per_sta_psk <0/1>) + * added P2P_REMOVE_CLIENT for removing a client from P2P groups + (including persistent groups); this can be used to securely remove + a client from a group if per-device PSKs are used + * added more configuration flexibility for allowed P2P GO/client + channels (p2p_no_go_freq list and p2p_add_cli_chan=0/1) + * added nl80211 functionality + - VHT configuration for nl80211 + - MFP (IEEE 802.11w) information for nl80211 command API + - support split wiphy dump + - FT (IEEE 802.11r) with driver-based SME + - use advertised number of supported concurrent channels + - QoS Mapping configuration + * improved TDLS negotiation robustness + * added more TDLS peer parameters to be configured to the driver + * optimized connection time by allowing recently received scan results + to be used instead of having to run through a new scan + * fixed ctrl_iface BSS command iteration with RANGE argument and no + exact matches; also fixed argument parsing for some cases with + multiple arguments + * added 'SCAN TYPE=ONLY' ctrl_iface command to request manual scan + without executing roaming/network re-selection on scan results + * added Session-Id derivation for EAP peer methods + * added fully automated regression testing with mac80211_hwsim + * changed configuration parser to reject invalid integer values + * allow AP/Enrollee to be specified with BSSID instead of UUID for + WPS ER operations + * disable network block temporarily on repeated connection failures + * changed the default driver interface from wext to nl80211 if both are + included in the build + * remove duplicate networks if WPS provisioning is run multiple times + * remove duplicate networks when Interworking network selection uses the + same network + * added global freq_list configuration to allow scan frequencies to be + limited for all cases instead of just for a specific network block + * added support for BSS Transition Management + * added option to use "IFNAME=<ifname> " prefix to use the global + control interface connection to perform per-interface commands; + similarly, allow global control interface to be used as a monitor + interface to receive events from all interfaces + * fixed OKC-based PMKSA cache entry clearing + * fixed TKIP group key configuration with FT + * added support for using OCSP stapling to validate server certificate + (ocsp=1 as optional and ocsp=2 as mandatory) + * added EAP-EKE peer + * added peer restart detection for IBSS RSN + * added domain_suffix_match (and domain_suffix_match2 for Phase 2 + EAP-TLS) to specify additional constraint for the server certificate + domain name + * added support for external SIM/USIM processing in EAP-SIM, EAP-AKA, + and EAP-AKA' (CTRL-REQ-SIM and CTRL-RSP-SIM commands over control + interface) + * added global bgscan configuration option as a default for all network + blocks that do not specify their own bgscan parameters + * added D-Bus methods for TDLS + * added more control to scan requests + - "SCAN freq=<freq list>" can be used to specify which channels are + scanned (comma-separated frequency ranges in MHz) + - "SCAN passive=1" can be used to request a passive scan (no Probe + Request frames are sent) + - "SCAN use_id" can be used to request a scan id to be returned and + included in event messages related to this specific scan operation + - "SCAN only_new=1" can be used to request the driver/cfg80211 to + report only BSS entries that have been updated during this scan + round + - these optional arguments to the SCAN command can be combined with + each other + * modified behavior on externally triggered scans + - avoid concurrent operations requiring full control of the radio when + an externally triggered scan is detected + - do not use results for internal roaming decision + * added a new cred block parameter 'temporary' to allow credential + blocks to be stored separately even if wpa_supplicant configuration + file is used to maintain other network information + * added "radio work" framework to schedule exclusive radio operations + for off-channel functionality + - reduce issues with concurrent operations that try to control which + channel is used + - allow external programs to request exclusive radio control in a way + that avoids conflicts with wpa_supplicant + * added support for using Protected Dual of Public Action frames for + GAS/ANQP exchanges when associated with PMF + * added support for WPS+NFC updates and P2P+NFC + - improved protocol for WPS + - P2P group formation/join based on NFC connection handover + - new IPv4 address assignment for P2P groups (ip_addr_* configuration + parameters on the GO) to replace DHCP + - option to fetch and report alternative carrier records for external + NFC operations + * various bug fixes + 2013-01-12 - v2.0 * removed Qt3-based wpa_gui (obsoleted by wpa_qui-qt4) * removed unmaintained driver wrappers broadcom, iphone, osx, ralink, diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 65fef41652be6..0f82af9d76ab5 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -10,11 +10,18 @@ export LIBDIR ?= /usr/local/lib/ export BINDIR ?= /usr/local/sbin/ PKG_CONFIG ?= pkg-config -CFLAGS += -I../src -CFLAGS += -I../src/utils +CFLAGS += $(EXTRA_CFLAGS) +CFLAGS += -I$(abspath ../src) +CFLAGS += -I$(abspath ../src/utils) -include .config +ifdef CONFIG_TESTING_OPTIONS +CFLAGS += -DCONFIG_TESTING_OPTIONS +CONFIG_WPS_TESTING=y +CONFIG_TDLS_TESTING=y +endif + BINALL=wpa_supplicant wpa_cli ifndef CONFIG_NO_WPA_PASSPHRASE @@ -74,6 +81,7 @@ OBJS_p += ../src/utils/wpabuf.o OBJS_c = wpa_cli.o ../src/common/wpa_ctrl.o OBJS_c += ../src/utils/wpa_debug.o OBJS_c += ../src/utils/common.o +OBJS += wmm_ac.o ifndef CONFIG_OS ifdef CONFIG_NATIVE_WINDOWS @@ -97,13 +105,14 @@ OBJS += ../src/utils/trace.o OBJS_p += ../src/utils/trace.o OBJS_c += ../src/utils/trace.o OBJS_priv += ../src/utils/trace.o +LIBCTRL += ../src/utils/trace.o LDFLAGS += -rdynamic CFLAGS += -funwind-tables ifdef CONFIG_WPA_TRACE_BFD -CFLAGS += -DWPA_TRACE_BFD -LIBS += -lbfd -LIBS_p += -lbfd -LIBS_c += -lbfd +CFLAGS += -DPACKAGE="wpa_supplicant" -DWPA_TRACE_BFD +LIBS += -lbfd -ldl -liberty -lz +LIBS_p += -lbfd -ldl -liberty -lz +LIBS_c += -lbfd -ldl -liberty -lz endif endif @@ -113,19 +122,40 @@ endif OBJS += ../src/utils/$(CONFIG_ELOOP).o OBJS_c += ../src/utils/$(CONFIG_ELOOP).o +ifeq ($(CONFIG_ELOOP), eloop) +# Using glibc < 2.17 requires -lrt for clock_gettime() +LIBS += -lrt +LIBS_c += -lrt +LIBS_p += -lrt +endif + ifdef CONFIG_ELOOP_POLL CFLAGS += -DCONFIG_ELOOP_POLL endif +ifdef CONFIG_ELOOP_EPOLL +CFLAGS += -DCONFIG_ELOOP_EPOLL +endif ifdef CONFIG_EAPOL_TEST CFLAGS += -Werror -DEAPOL_TEST endif +ifdef CONFIG_CODE_COVERAGE +CFLAGS += -O0 -fprofile-arcs -ftest-coverage +LIBS += -lgcov +LIBS_c += -lgcov +LIBS_p += -lgcov +endif + ifdef CONFIG_HT_OVERRIDES CFLAGS += -DCONFIG_HT_OVERRIDES endif +ifdef CONFIG_VHT_OVERRIDES +CFLAGS += -DCONFIG_VHT_OVERRIDES +endif + ifndef CONFIG_BACKEND CONFIG_BACKEND=file endif @@ -158,6 +188,17 @@ ifdef CONFIG_NO_SCAN_PROCESSING CFLAGS += -DCONFIG_NO_SCAN_PROCESSING endif +ifdef CONFIG_SUITEB +CFLAGS += -DCONFIG_SUITEB +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_SUITEB192 +CFLAGS += -DCONFIG_SUITEB192 +NEED_SHA384=y +endif + ifdef CONFIG_IEEE80211W CFLAGS += -DCONFIG_IEEE80211W NEED_SHA256=y @@ -167,13 +208,29 @@ endif ifdef CONFIG_IEEE80211R CFLAGS += -DCONFIG_IEEE80211R OBJS += ../src/rsn_supp/wpa_ft.o +NEED_SHA256=y +NEED_AES_OMAC1=y +endif + +ifdef CONFIG_MESH NEED_80211_COMMON=y NEED_SHA256=y +NEED_AES_SIV=y NEED_AES_OMAC1=y +NEED_AES_CTR=y +CONFIG_SAE=y +CONFIG_AP=y +CFLAGS += -DCONFIG_MESH +OBJS += mesh.o +OBJS += mesh_mpm.o +OBJS += mesh_rsn.o endif ifdef CONFIG_SAE CFLAGS += -DCONFIG_SAE +OBJS += ../src/common/sae.o +NEED_ECC=y +NEED_DH_GROUPS=y endif ifdef CONFIG_WNM @@ -208,7 +265,7 @@ NEED_SHA1=y NEED_MD5=y NEED_RC4=y else -CFLAGS += -DCONFIG_NO_WPA -DCONFIG_NO_WPA2 +CFLAGS += -DCONFIG_NO_WPA endif ifdef CONFIG_IBSS_RSN @@ -230,10 +287,10 @@ 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 +OBJS += ../src/utils/bitfield.o CFLAGS += -DCONFIG_P2P NEED_GAS=y NEED_OFFCHANNEL=y -NEED_80211_COMMON=y CONFIG_WPS=y CONFIG_AP=y ifdef CONFIG_P2P_STRICT @@ -250,6 +307,7 @@ ifdef CONFIG_HS20 OBJS += hs20_supplicant.o CFLAGS += -DCONFIG_HS20 CONFIG_INTERWORKING=y +NEED_AES_OMAC1=y endif ifdef CONFIG_INTERWORKING @@ -258,10 +316,6 @@ CFLAGS += -DCONFIG_INTERWORKING NEED_GAS=y endif -ifdef CONFIG_NO_WPA2 -CFLAGS += -DCONFIG_NO_WPA2 -endif - include ../src/drivers/drivers.mak ifdef CONFIG_AP OBJS_d += $(DRV_BOTH_OBJS) @@ -301,6 +355,12 @@ ifeq ($(CONFIG_L2_PACKET), freebsd) LIBS += -lpcap endif +ifdef CONFIG_ERP +CFLAGS += -DCONFIG_ERP +NEED_SHA256=y +NEED_HMAC_SHA256_KDF=y +endif + ifdef CONFIG_EAP_TLS # EAP-TLS ifeq ($(CONFIG_EAP_TLS), dyn) @@ -479,6 +539,13 @@ CONFIG_EAP_SIM_COMMON=y NEED_AES_CBC=y endif +ifdef CONFIG_EAP_PROXY +CFLAGS += -DCONFIG_EAP_PROXY +OBJS += ../src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).o +include eap_proxy_$(CONFIG_EAP_PROXY).mak +CONFIG_IEEE8021X_EAPOL=y +endif + ifdef CONFIG_EAP_AKA_PRIME # EAP-AKA' ifeq ($(CONFIG_EAP_AKA_PRIME), dyn) @@ -560,16 +627,28 @@ 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 +OBJS_h += ../src/eap_server/eap_server_pwd.o CONFIG_IEEE8021X_EAPOL=y NEED_SHA256=y endif -ifdef CONFIG_WPS -ifdef CONFIG_WPS2 -CFLAGS += -DCONFIG_WPS2 +ifdef CONFIG_EAP_EKE +# EAP-EKE +ifeq ($(CONFIG_EAP_EKE), dyn) +CFLAGS += -DEAP_EKE_DYNAMIC +EAPDYN += ../src/eap_peer/eap_eke.so +else +CFLAGS += -DEAP_EKE +OBJS += ../src/eap_peer/eap_eke.o ../src/eap_common/eap_eke_common.o +OBJS_h += ../src/eap_server/eap_server_eke.o +endif +CONFIG_IEEE8021X_EAPOL=y +NEED_DH_GROUPS=y +NEED_DH_GROUPS_ALL=y +NEED_SHA256=y endif +ifdef CONFIG_WPS # EAP-WSC CFLAGS += -DCONFIG_WPS -DEAP_WSC OBJS += wps_supplicant.o @@ -588,7 +667,6 @@ 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 @@ -696,8 +774,20 @@ LIBS += -ldl -rdynamic endif endif +ifdef CONFIG_MACSEC +CFLAGS += -DCONFIG_MACSEC +NEED_AES_ENCBLOCK=y +NEED_AES_UNWRAP=y +NEED_AES_WRAP=y +NEED_AES_OMAC1=y +OBJS += wpas_kay.o +OBJS += ../src/pae/ieee802_1x_cp.o +OBJS += ../src/pae/ieee802_1x_kay.o +OBJS += ../src/pae/ieee802_1x_key.o +OBJS += ../src/pae/ieee802_1x_secy_ops.o +endif + ifdef CONFIG_AP -NEED_80211_COMMON=y NEED_EAP_COMMON=y NEED_RSN_AUTHENTICATOR=y CFLAGS += -DCONFIG_AP @@ -721,9 +811,13 @@ 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 +OBJS += ../src/ap/bss_load.o OBJS += ../src/ap/eap_user_db.o ifdef CONFIG_IEEE80211N OBJS += ../src/ap/ieee802_11_ht.o +ifdef CONFIG_IEEE80211AC +OBJS += ../src/ap/ieee802_11_vht.o +endif endif ifdef CONFIG_WNM OBJS += ../src/ap/wnm_ap.o @@ -739,6 +833,9 @@ OBJS += ../src/eap_server/eap_server_methods.o ifdef CONFIG_IEEE80211N CFLAGS += -DCONFIG_IEEE80211N +ifdef CONFIG_IEEE80211AC +CFLAGS += -DCONFIG_IEEE80211AC +endif endif ifdef NEED_AP_MLME @@ -746,6 +843,7 @@ OBJS += ../src/ap/wmm.o OBJS += ../src/ap/ap_list.o OBJS += ../src/ap/ieee802_11.o OBJS += ../src/ap/hw_features.o +OBJS += ../src/ap/dfs.o CFLAGS += -DNEED_AP_MLME endif ifdef CONFIG_WPS @@ -904,7 +1002,8 @@ endif OBJS += ../src/crypto/crypto_gnutls.o OBJS_p += ../src/crypto/crypto_gnutls.o ifdef NEED_FIPS186_2_PRF -OBJS += ../src/crypto/fips_prf_gnutls.o +OBJS += ../src/crypto/fips_prf_internal.o +SHA1OBJS += ../src/crypto/sha1-internal.o endif LIBS += -lgcrypt LIBS_p += -lgcrypt @@ -920,29 +1019,14 @@ endif OBJS += ../src/crypto/crypto_cryptoapi.o OBJS_p += ../src/crypto/crypto_cryptoapi.o ifdef NEED_FIPS186_2_PRF -OBJS += ../src/crypto/fips_prf_cryptoapi.o +OBJS += ../src/crypto/fips_prf_internal.o +SHA1OBJS += ../src/crypto/sha1-internal.o 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.o -LIBS += -lssl3 -endif -OBJS += ../src/crypto/crypto_nss.o -OBJS_p += ../src/crypto/crypto_nss.o -ifdef NEED_FIPS186_2_PRF -OBJS += ../src/crypto/fips_prf_nss.o -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 @@ -1060,7 +1144,9 @@ ifdef CONFIG_INTERNAL_AES AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-dec.o endif +ifneq ($(CONFIG_TLS), openssl) AESOBJS += ../src/crypto/aes-unwrap.o +endif ifdef NEED_AES_EAX AESOBJS += ../src/crypto/aes-eax.o NEED_AES_CTR=y @@ -1079,10 +1165,15 @@ else AESOBJS += ../src/crypto/aes-omac1.o endif endif +ifdef NEED_AES_SIV +AESOBJS += ../src/crypto/aes-siv.o +endif ifdef NEED_AES_WRAP NEED_AES_ENC=y +ifneq ($(CONFIG_TLS), openssl) AESOBJS += ../src/crypto/aes-wrap.o endif +endif ifdef NEED_AES_CBC NEED_AES_ENC=y AESOBJS += ../src/crypto/aes-cbc.o @@ -1123,8 +1214,10 @@ endif endif ifndef CONFIG_FIPS +ifneq ($(CONFIG_TLS), openssl) MD5OBJS += ../src/crypto/md5.o endif +endif ifdef NEED_MD5 ifdef CONFIG_INTERNAL_MD5 MD5OBJS += ../src/crypto/md5-internal.o @@ -1165,8 +1258,14 @@ endif ifdef NEED_TLS_PRF_SHA256 SHA256OBJS += ../src/crypto/sha256-tlsprf.o endif +ifdef NEED_HMAC_SHA256_KDF +OBJS += ../src/crypto/sha256-kdf.o +endif OBJS += $(SHA256OBJS) endif +ifdef NEED_SHA384 +CFLAGS += -DCONFIG_SHA384 +endif ifdef NEED_DH_GROUPS OBJS += ../src/crypto/dh_groups.o @@ -1180,6 +1279,10 @@ OBJS += ../src/crypto/dh_group5.o endif endif +ifdef NEED_ECC +CFLAGS += -DCONFIG_ECC +endif + ifdef CONFIG_NO_RANDOM_POOL CFLAGS += -DCONFIG_NO_RANDOM_POOL else @@ -1201,6 +1304,11 @@ endif ifeq ($(CONFIG_CTRL_IFACE), udp) CFLAGS += -DCONFIG_CTRL_IFACE_UDP endif +ifeq ($(CONFIG_CTRL_IFACE), udp6) +CONFIG_CTRL_IFACE=udp +CFLAGS += -DCONFIG_CTRL_IFACE_UDP +CFLAGS += -DCONFIG_CTRL_IFACE_UDP_IPV6 +endif ifeq ($(CONFIG_CTRL_IFACE), named_pipe) CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE endif @@ -1209,6 +1317,12 @@ CONFIG_CTRL_IFACE=udp CFLAGS += -DCONFIG_CTRL_IFACE_UDP CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE endif +ifeq ($(CONFIG_CTRL_IFACE), udp6-remote) +CONFIG_CTRL_IFACE=udp +CFLAGS += -DCONFIG_CTRL_IFACE_UDP +CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE +CFLAGS += -DCONFIG_CTRL_IFACE_UDP_IPV6 +endif OBJS += ctrl_iface.o ctrl_iface_$(CONFIG_CTRL_IFACE).o endif @@ -1301,14 +1415,12 @@ OBJS += ../src/utils/base64.o endif ifdef NEED_SME -NEED_80211_COMMON=y OBJS += sme.o CFLAGS += -DCONFIG_SME endif -ifdef NEED_80211_COMMON OBJS += ../src/common/ieee802_11_common.o -endif +OBJS += ../src/common/hw_features_common.o ifdef NEED_EAP_COMMON OBJS += ../src/eap_common/eap_common.o @@ -1406,7 +1518,22 @@ OBJS += offchannel.o CFLAGS += -DCONFIG_OFFCHANNEL endif +ifdef CONFIG_MODULE_TESTS +CFLAGS += -DCONFIG_MODULE_TESTS +OBJS += wpas_module_tests.o +OBJS += ../src/utils/utils_module_tests.o +OBJS += ../src/common/common_module_tests.o +OBJS += ../src/crypto/crypto_module_tests.o +ifdef CONFIG_WPS +OBJS += ../src/wps/wps_module_tests.o +endif +ifndef CONFIG_P2P +OBJS += ../src/utils/bitfield.o +endif +endif + OBJS += ../src/drivers/driver_common.o +OBJS_priv += ../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 @@ -1440,26 +1567,6 @@ 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) -ifeq ($(CONFIG_TLS), openssl) -OBJS_priv += ../src/crypto/crypto_openssl.o -endif -ifeq ($(CONFIG_TLS), gnutls) -OBJS_priv += ../src/crypto/crypto_gnutls.o -endif -ifeq ($(CONFIG_TLS), nss) -OBJS_priv += ../src/crypto/crypto_nss.o -endif -ifeq ($(CONFIG_TLS), internal) -ifeq ($(CONFIG_CRYPTO), libtomcrypt) -OBJS_priv += ../src/crypto/crypto_libtomcrypt.o -else -OBJS_priv += ../src/crypto/crypto_internal.o -endif -endif -endif # CONFIG_DRIVER_TEST OBJS += ../src/l2_packet/l2_packet_privsep.o OBJS += ../src/drivers/driver_privsep.o EXTRA_progs += wpa_priv @@ -1489,6 +1596,10 @@ ifeq ($(V), 1) Q= E=true endif +ifeq ($(QUIET), 1) +Q=@ +E=true +endif dynamic_eap_methods: $(EAPDYN) @@ -1526,6 +1637,15 @@ wpa_cli: $(OBJS_c) $(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c) @$(E) " LD " $@ +LIBCTRL += ../src/common/wpa_ctrl.o +LIBCTRL += ../src/utils/os_$(CONFIG_OS).o +LIBCTRL += ../src/utils/wpa_debug.o + +libwpa_ctrl.a: $(LIBCTRL) + $(Q)rm -f $@ + $(Q)$(AR) crs $@ $? + @$(E) " AR " $@ + link_test: $(OBJS) $(OBJS_h) tests/link_test.o $(Q)$(LDO) $(LDFLAGS) -o link_test $(OBJS) $(OBJS_h) tests/link_test.o $(LIBS) @$(E) " LD " $@ @@ -1562,19 +1682,31 @@ eap_ikev2.so: ../src/eap_peer/eap_ikev2.c ../src/eap_peer/ikev2.c ../src/eap_com $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ -Deap_peer_ikev2_register=eap_peer_method_dynamic_init +eap_eke.so: ../src/eap_peer/eap_eke.c ../src/eap_common/eap_eke_common.c + $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \ + -Deap_peer_eke_register=eap_peer_method_dynamic_init + %.so: %.c $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $< \ -D$(*F:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init +ifdef CONFIG_CODE_COVERAGE +%.o: %.c + @$(E) " CC " $< + $(Q)cd $(dir $@); $(CC) -c -o $(notdir $@) $(CFLAGS) $(notdir $<) +else %.o: %.c $(Q)$(CC) -c -o $@ $(CFLAGS) $< @$(E) " CC " $< +endif %.service: %.service.in - sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ + $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ + @$(E) " sed" $< %@.service: %.service.arg.in - sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ + $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@ + @$(E) " sed" $< wpa_supplicant.exe: wpa_supplicant mv -f $< $@ @@ -1620,11 +1752,19 @@ FIPSLD=$(FIPSDIR)/bin/fipsld fips: $(MAKE) CC=$(FIPSLD) FIPSLD_CC="$(CC)" +lcov-html: wpa_supplicant.gcda + lcov -c -d .. > lcov.info + genhtml lcov.info --output-directory lcov-html + clean: $(MAKE) -C ../src clean $(MAKE) -C dbus clean - rm -f core *~ *.o *.d eap_*.so $(ALL) $(WINALL) eapol_test preauth_test + rm -f core *~ *.o *.d *.gcno *.gcda *.gcov + rm -f eap_*.so $(ALL) $(WINALL) eapol_test preauth_test rm -f wpa_priv rm -f nfc_pw_token + rm -f lcov.info + rm -rf lcov-html + rm -f libwpa_ctrl.a -include $(OBJS:%.o=%.d) diff --git a/wpa_supplicant/README b/wpa_supplicant/README index a06e5c1fafa52..f9c65d2e0ff5e 100644 --- a/wpa_supplicant/README +++ b/wpa_supplicant/README @@ -1,7 +1,7 @@ WPA Supplicant ============== -Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> and contributors +Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors All Rights Reserved. This program is licensed under the BSD license (the one with @@ -115,13 +115,15 @@ Current hardware/software requirements: - NetBSD-current - Microsoft Windows with WinPcap (at least WinXP, may work with other versions) - drivers: - Linux drivers that support WPA/WPA2 configuration with the generic - Linux wireless extensions (WE-18 or newer). Even though there are + Linux drivers that support cfg80211/nl80211. Even though there are number of driver specific interface included in wpa_supplicant, please - note that Linux drivers are moving to use generic wireless extensions - and driver_wext (-Dwext on wpa_supplicant command line) should be the - default option to start with before falling back to driver specific - interface. + note that Linux drivers are moving to use generic wireless configuration + interface driver_nl80211 (-Dnl80211 on wpa_supplicant command line) + should be the default option to start with before falling back to driver + specific interface. + + Linux drivers that support WPA/WPA2 configuration with the generic + Linux wireless extensions (WE-18 or newer). Obsoleted by nl80211. 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 @@ -408,9 +410,10 @@ Command line options usage: wpa_supplicant [-BddfhKLqqtuvwW] [-P<pid file>] [-g<global ctrl>] \ + [-G<group>] \ -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] [-p<driver_param>] \ [-b<br_ifname> [-N -i<ifname> -c<conf> [-C<ctrl>] [-D<driver>] \ - [-p<driver_param>] [-b<br_ifname>] ...] + [-p<driver_param>] [-b<br_ifname>] [-m<P2P Device config file>] ... options: -b = optional bridge interface name @@ -422,6 +425,7 @@ options: -D = driver name (can be multiple drivers: nl80211,wext) -f = Log output to default log location (normally /tmp) -g = global ctrl_interface + -G = global ctrl_interface group -K = include keys (passwords, etc.) in debug output -t = include timestamp in debug messages -h = show this help text @@ -434,8 +438,10 @@ options: -w = wait for interface to be added, if needed -W = wait for a control interface monitor before starting -N = start describing new interface + -m = Configuration file for the P2P Device drivers: + nl80211 = Linux nl80211/cfg80211 wext = Linux wireless extensions (generic) wired = wpa_supplicant wired Ethernet driver roboswitch = wpa_supplicant Broadcom switch driver @@ -477,7 +483,7 @@ If the interface is added in a Linux bridge (e.g., br0), the bridge interface needs to be configured to wpa_supplicant in addition to the main interface: -wpa_supplicant -cw.conf -Dwext -iwlan0 -bbr0 +wpa_supplicant -cw.conf -Dnl80211 -iwlan0 -bbr0 Configuration file @@ -869,10 +875,10 @@ network (SSID): # Start wpa_supplicant in the background wpa_supplicant -g/var/run/wpa_supplicant-global -B -# Add a new interface (wlan0, no configuration file, driver=wext, and +# Add a new interface (wlan0, no configuration file, driver=nl80211, and # enable control interface) wpa_cli -g/var/run/wpa_supplicant-global interface_add wlan0 \ - "" wext /var/run/wpa_supplicant + "" nl80211 /var/run/wpa_supplicant # Configure a network using the newly added network interface: wpa_cli -iwlan0 add_network @@ -933,7 +939,7 @@ Example configuration: chmod 0750 /var/run/wpa_priv - start wpa_priv as root (e.g., from system startup scripts) with the enabled interfaces configured on the command line: - wpa_priv -B -P /var/run/wpa_priv.pid wext:ath0 + wpa_priv -B -P /var/run/wpa_priv.pid nl80211:wlan0 - run wpa_supplicant as non-root with a user that is in wpapriv group: wpa_supplicant -i ath0 -c wpa_supplicant.conf @@ -944,3 +950,105 @@ can be started when an interface is added (hotplug/udev/etc. scripts). wpa_priv can control multiple interface with one process, but it is also possible to run multiple wpa_priv processes at the same time, if desired. + + +Linux capabilities instead of privileged process +------------------------------------------------ + +wpa_supplicant performs operations that need special permissions, e.g., +to control the network connection. Traditionally this has been achieved +by running wpa_supplicant as a privileged process with effective user id +0 (root). Linux capabilities can be used to provide restricted set of +capabilities to match the functions needed by wpa_supplicant. The +minimum set of capabilities needed for the operations is CAP_NET_ADMIN +and CAP_NET_RAW. + +setcap(8) can be used to set file capabilities. For example: + +sudo setcap cap_net_raw,cap_net_admin+ep wpa_supplicant + +Please note that this would give anyone being able to run that +wpa_supplicant binary access to the additional capabilities. This can +further be limited by file owner/group and mode bits. For example: + +sudo chown wpas wpa_supplicant +sudo chmod 0100 wpa_supplicant + +This combination of setcap, chown, and chmod commands would allow wpas +user to execute wpa_supplicant with additional network admin/raw +capabilities. + +Common way style of creating a control interface socket in +/var/run/wpa_supplicant could not be done by this user, but this +directory could be created before starting the wpa_supplicant and set to +suitable mode to allow wpa_supplicant to create sockets +there. Alternatively, other directory or abstract socket namespace could +be used for the control interface. + + +External requests for radio control +----------------------------------- + +External programs can request wpa_supplicant to not start offchannel +operations during other tasks that may need exclusive control of the +radio. The RADIO_WORK control interface command can be used for this. + +"RADIO_WORK add <name> [freq=<MHz>] [timeout=<seconds>]" command can be +used to reserve a slot for radio access. If freq is specified, other +radio work items on the same channel may be completed in +parallel. Otherwise, all other radio work items are blocked during +execution. Timeout is set to 10 seconds by default to avoid blocking +wpa_supplicant operations for excessive time. If a longer (or shorter) +safety timeout is needed, that can be specified with the optional +timeout parameter. This command returns an identifier for the radio work +item. + +Once the radio work item has been started, "EXT-RADIO-WORK-START <id>" +event message is indicated that the external processing can start. Once +the operation has been completed, "RADIO_WORK done <id>" is used to +indicate that to wpa_supplicant. This allows other radio works to be +performed. If this command is forgotten (e.g., due to the external +program terminating), wpa_supplicant will time out the radio owrk item +and send "EXT-RADIO-WORK-TIMEOUT <id>" event ot indicate that this has +happened. "RADIO_WORK done <id>" can also be used to cancel items that +have not yet been started. + +For example, in wpa_cli interactive mode: + +> radio_work add test +1 +<3>EXT-RADIO-WORK-START 1 +> radio_work show +ext:test@wlan0:0:1:2.487797 +> radio_work done 1 +OK +> radio_work show + + +> radio_work done 3 +OK +> radio_work show +ext:test freq=2412 timeout=30@wlan0:2412:1:28.583483 +<3>EXT-RADIO-WORK-TIMEOUT 2 + + +> radio_work add test2 freq=2412 timeout=60 +5 +<3>EXT-RADIO-WORK-START 5 +> radio_work add test3 +6 +> radio_work add test4 +7 +> radio_work show +ext:test2 freq=2412 timeout=60@wlan0:2412:1:9.751844 +ext:test3@wlan0:0:0:5.071812 +ext:test4@wlan0:0:0:3.143870 +> radio_work done 6 +OK +> radio_work show +ext:test2 freq=2412 timeout=60@wlan0:2412:1:16.287869 +ext:test4@wlan0:0:0:9.679895 +> radio_work done 5 +OK +<3>EXT-RADIO-WORK-START 7 +<3>EXT-RADIO-WORK-TIMEOUT 7 diff --git a/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20 index 5669c55c32d5e..161dc06a2ddd6 100644 --- a/wpa_supplicant/README-HS20 +++ b/wpa_supplicant/README-HS20 @@ -109,6 +109,8 @@ Credentials can be pre-configured for automatic network selection: # # credential fields: # +# temporary: Whether this credential is temporary and not to be saved +# # priority: Priority group # By default, all networks and credentials get the same priority group # (0). This field can be used to give higher priority for credentials @@ -166,9 +168,25 @@ Credentials can be pre-configured for automatic network selection: # milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN> # format # -# domain: Home service provider FQDN +# domain_suffix_match: Constraint for server domain name +# If set, this FQDN is used as a suffix match requirement for the AAA +# server certificate in SubjectAltName dNSName element(s). If a +# matching dNSName is found, this constraint is met. If no dNSName +# values are present, this constraint is matched against SubjectName CN +# using same suffix match comparison. Suffix match here means that the +# host/domain name is compared one label at a time starting from the +# top-level domain and all the labels in @domain_suffix_match shall be +# included in the certificate. The certificate may include additional +# sub-level labels in addition to the required labels. +# +# For example, domain_suffix_match=example.com would match +# test.example.com but would not match test-example.com. +# +# domain: Home service provider FQDN(s) # This is used to compare against the Domain Name List to figure out -# whether the AP is operated by the Home SP. +# whether the AP is operated by the Home SP. Multiple domain entries can +# be used to configure alternative FQDNs that will be considered home +# networks. # # roaming_consortium: Roaming Consortium OI # If roaming_consortium_len is non-zero, this field contains the @@ -195,6 +213,65 @@ Credentials can be pre-configured for automatic network selection: # matching with the network. Multiple entries can be used to specify more # than one SSID. # +# roaming_partner: Roaming partner information +# This optional field can be used to configure preferences between roaming +# partners. The field is a string in following format: +# <FQDN>,<0/1 exact match>,<priority>,<* or country code> +# (non-exact match means any subdomain matches the entry; priority is in +# 0..255 range with 0 being the highest priority) +# +# update_identifier: PPS MO ID +# (Hotspot 2.0 PerProviderSubscription/UpdateIdentifier) +# +# provisioning_sp: FQDN of the SP that provisioned the credential +# This optional field can be used to keep track of the SP that provisioned +# the credential to find the PPS MO (./Wi-Fi/<provisioning_sp>). +# +# sp_priority: Credential priority within a provisioning SP +# This is the priority of the credential among all credentials +# provisionined by the same SP (i.e., for entries that have identical +# provisioning_sp value). The range of this priority is 0-255 with 0 +# being the highest and 255 the lower priority. +# +# Minimum backhaul threshold (PPS/<X+>/Policy/MinBackhauldThreshold/*) +# These fields can be used to specify minimum download/upload backhaul +# bandwidth that is preferred for the credential. This constraint is +# ignored if the AP does not advertise WAN Metrics information or if the +# limit would prevent any connection. Values are in kilobits per second. +# min_dl_bandwidth_home +# min_ul_bandwidth_home +# min_dl_bandwidth_roaming +# min_ul_bandwidth_roaming +# +# max_bss_load: Maximum BSS Load Channel Utilization (1..255) +# (PPS/<X+>/Policy/MaximumBSSLoadValue) +# This value is used as the maximum channel utilization for network +# selection purposes for home networks. If the AP does not advertise +# BSS Load or if the limit would prevent any connection, this constraint +# will be ignored. +# +# req_conn_capab: Required connection capability +# (PPS/<X+>/Policy/RequiredProtoPortTuple) +# This value is used to configure set of required protocol/port pairs that +# a roaming network shall support (include explicitly in Connection +# Capability ANQP element). This constraint is ignored if the AP does not +# advertise Connection Capability or if this constraint would prevent any +# network connection. This policy is not used in home networks. +# Format: <protocol>[:<comma-separated list of ports] +# Multiple entries can be used to list multiple requirements. +# For example, number of common TCP protocols: +# req_conn_capab=6:22,80,443 +# For example, IPSec/IKE: +# req_conn_capab=17:500 +# req_conn_capab=50 +# +# ocsp: Whether to use/require OCSP to check server certificate +# 0 = do not use OCSP stapling (TLS certificate status extension) +# 1 = try to use OCSP stapling, but not require response +# 2 = require valid OCSP stapling response +# +# sim_num: Identifier for which SIM to use in multi-SIM devices +# # for example: # #cred={ @@ -203,6 +280,7 @@ Credentials can be pre-configured for automatic network selection: # password="password" # ca_cert="/etc/wpa_supplicant/ca.pem" # domain="example.com" +# domain_suffix_match="example.com" #} # #cred={ @@ -252,6 +330,8 @@ OK OK > set_cred 0 priority 1 OK +> set_cred 0 temporary 1 +OK Add a SIM credential using a simulated SIM/USIM card for testing: @@ -267,6 +347,17 @@ OK Note: the return value of add_cred is used as the first argument to the following set_cred commands. +Add a SIM credential using a external SIM/USIM processing: + +> set external_sim 1 +OK +> add_cred +1 +> set_cred 1 imsi "23456-0000000000" +OK +> set_cred 1 eap SIM +OK + Add a WPA2-Enterprise network: diff --git a/wpa_supplicant/README-P2P b/wpa_supplicant/README-P2P index 5e98c959f9476..6a5b032124a99 100644 --- a/wpa_supplicant/README-P2P +++ b/wpa_supplicant/README-P2P @@ -72,7 +72,8 @@ over the main control interface. Device Discovery p2p_find [timeout in seconds] [type=<social|progressive>] \ - [dev_id=<addr>] [delay=<search delay in ms>] + [dev_id=<addr>] [dev_type=<device type>] \ + [delay=<search delay in ms>] [seek=<service name>] [freq=<MHz>] 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 @@ -80,13 +81,37 @@ 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. +full scan. When the type parameter is not included (i.e., full scan), the +optional freq parameter can be used to override the first scan to use only +the specified channel after which only social channels are scanned. The optional dev_id option can be used to specify a single P2P peer to search for. The optional delay parameter can be used to request an extra delay to be used between search iterations (e.g., to free up radio resources for concurrent operations). +The optional dev_type option can be used to specify a single device type +(primary or secondary) to search for, e.g., +"p2p_find dev_type=1-0050F204-1". + + +With one or more seek arguments, the command sends Probe Request frames +for a P2PS service. For example, +p2p_find 5 dev_id=11:22:33:44:55:66 seek=alt.example.chat seek=alt.example.video + +Parameters description: + Timeout - Optional ASCII base-10-encoded u16. If missing, request will not + time out and must be canceled manually + dev_id - Optional to request responses from a single known remote device + Service Name - Mandatory UTF-8 string for ASP seeks + Service name must match the remote service being advertised exactly + (no prefix matching). + Service name may be empty, in which case all ASP services will be + returned, and may be filtered with p2p_serv_disc_req settings, and + p2p_serv_asp_resp results. + Multiple service names may be requested, but if it exceeds internal + limit, it will automatically revert to requesting all ASP services. + p2p_listen [timeout in seconds] Start Listen-only state (become discoverable without searching for @@ -123,9 +148,9 @@ parameter can be used to request wpa_supplicant to automatically figure out whether the peer device is operating as a GO and if so, use join-a-group style PD instead of GO Negotiation style PD. -p2p_connect <peer device address> <pbc|pin|PIN#> [display|keypad] +p2p_connect <peer device address> <pbc|pin|PIN#|p2ps> [display|keypad|p2ps] [persistent|persistent=<network id>] [join|auth] - [go_intent=<0..15>] [freq=<in MHz>] [ht40] [provdisc] + [go_intent=<0..15>] [freq=<in MHz>] [ht40] [vht] [provdisc] [auto] Start P2P group formation with a discovered P2P peer. This includes optional group owner negotiation, group interface setup, provisioning, @@ -166,7 +191,71 @@ used prior to starting GO Negotiation as a workaround with some deployed P2P implementations that require this to allow the user to accept the connection. -p2p_group_add [persistent|persistent=<network id>] [freq=<freq in MHz>] [ht40] +"auto" can be used to request wpa_supplicant to automatically figure +out whether the peer device is operating as a GO and if so, use +join-a-group operation rather than GO Negotiation. + +P2PS attribute changes to p2p_connect command: + +P2PS supports two WPS provisioning methods namely PIN method and P2PS default. +The remaining paramters hold same role as in legacy P2P. In case of P2PS default +config method "p2ps" keyword is added in p2p_connect command. + +For example: +p2p_connect 02:0a:f5:85:11:00 12345670 p2ps persistent join + (WPS Method = P2PS default) + +p2p_connect 02:0a:f5:85:11:00 45629034 keypad persistent + (WPS Method = PIN) + +p2p_asp_provision <peer MAC address> <adv_id=peer adv id> + <adv_mac=peer MAC address> [role=2|4|1] <session=session id> + <session_mac=initiator mac address> + [info='service info'] <method=Default|keypad|Display> + +This command starts provision discovery with the P2PS enabled peer device. + +For example, +p2p_asp_provision 00:11:22:33:44:55 adv_id=4d6fc7 adv_mac=00:55:44:33:22:11 role=1 session=12ab34 session_mac=00:11:22:33:44:55 info='name=john' method=1000 + +Parameter description: + MAC address - Mandatory + adv_id - Mandatory remote Advertising ID of service connection is being + established for + adv_mac - Mandatory MAC address that owns/registered the service + role - Optional + 2 (group client only) or 4 (group owner only) + if not present (or 1) role is negotiated by the two peers. + session - Mandatory Session ID of the first session to be established + session_mac - Mandatory MAC address that owns/initiated the session + method - Optional method to request for provisioning (1000 - P2PS Default, + 100 - Keypad(PIN), 8 - Display(PIN)) + info - Optional UTF-8 string. Hint for service to indicate possible usage + parameters - Escape single quote & backslash: + with a backslash 0x27 == ' == \', and 0x5c == \ == \\ + +p2p_asp_provision_resp <peer mac address> <adv_id= local adv id> + <adv_mac=local MAC address> <role=1|2|4> <status=0> + <session=session id> <session_mac=peer MAC address> + +This command sends a provision discovery response from responder side. + +For example, +p2p_asp_provision_resp 00:55:44:33:22:11 adv_id=4d6fc7 adv_mac=00:55:44:33:22:11 role=1 status=0 session=12ab34 session_mac=00:11:22:33:44:55 + +Parameters definition: + MAC address - Mandatory + adv_id - Mandatory local Advertising ID of service connection is being + established for + adv_mac - Mandatory MAC address that owns/registered the service + role - Optional 2 (group client only) or 4 (group owner only) + if not present (or 1) role is negotiated by the two peers. + status - Mandatory Acceptance/Rejection code of Provisioning + session - Mandatory Session ID of the first session to be established + session_mac - Mandatory MAC address that owns/initiated the session + +p2p_group_add [persistent|persistent=<network id>] [freq=<freq in MHz>] + [ht40] [vht] Set up a P2P group owner manually (i.e., without group owner negotiation with a specific peer). This is also known as autonomous @@ -199,8 +288,80 @@ P2P group interface (if one was used) that is in the WPS provisioning step. If the WPS provisioning step has been completed, the group is not terminated. +p2p_remove_client <peer's P2P Device Address|iface=<interface address>> + +This command can be used to remove the specified client from all groups +(operating and persistent) from the local GO. Note that the peer device +can rejoin the group if it is in possession of a valid key. See p2p_set +per_sta_psk command below for more details on how the peer can be +removed securely. + Service Discovery +p2p_service_add asp <auto accept> <adv id> <status 0/1> <Config Methods> + <Service name> [Service Information] [Response Info] + +This command can be used to search for a P2PS service which includes +Play, Send, Display, and Print service. The parameters for this command +are "asp" to identify the command as P2PS one, auto accept value, +advertisement id which uniquely identifies the service requests, state +of the service whether the service is available or not, config methods +which can be either P2PS method or PIN method, service name followed by +two optional parameters service information, and response info. + +For example, +p2p_service_add asp 1 4d6fc7 0 1108 alt.example.chat svc_info='name=john' rsp_info='enter PIN 1234' + +Parameters definition: + asp - Mandatory for ASP service registration + auto accept - Mandatory ASCII hex-encoded boolean (0 == no auto-accept, + 1 == auto-accept ANY role, 2 == auto-accept CLIENT role, + 4 == auto-accept GO role) + Advertisement ID - Mandatory non-zero ASCII hex-encoded u32 + (Must be unique/not yet exist in svc db) + State - Mandatory ASCII hex-encoded u8 (0 -- Svc not available, + 1 -- Svc available, 2-0xff Application defined) + Config Methods - Mandatory ASCII hex-encoded u16 (bitmask of WSC config + methods) + Service Name - Mandatory UTF-8 string + Service Information - Optional UTF-8 string + Escape single quote & backslash with a backslash: + 0x27 == ' == \', and 0x5c == \ == \\ + Session response information - Optional (used only if auto accept is TRUE) + UTF-8 string + Escape single quote & backslash with a backslash: + 0x27 == ' == \', and 0x5c == \ == \\ + +p2p_service_rep asp <auto accept> <adv id> <status 0/1> <Config Methods> + <Service name> [Service Information] [Response Info] + +This command can be used to replace the existing service request +attributes from the initiator side. The replacement is only allowed if +the advertisement id issued in the command matches with any one entry in +the list of existing SD queries. If advertisement id doesn't match the +command returns a failure. + +For example, +p2p_service_rep asp 1 4d6fc7 1 1108 alt.example.chat svc_info='name=john' rsp_info='enter PIN 1234' + +Parameters definition: + asp - Mandatory for ASP service registration + auto accept - Mandatory ASCII hex-encoded boolean (1 == true, 0 == false) + Advertisement ID - Mandatory non-zero ASCII hex-encoded u32 + (Must already exist in svc db) + State - Mandatory ASCII hex-encoded u8 (can be used to indicate svc + available or not available for instance) + Config Methods - Mandatory ASCII hex-encoded u16 (bitmask of WSC config + methods) + Service Name - Mandatory UTF-8 string (Must match existing string in svc db) + Service Information - Optional UTF-8 string + Escape single quote & backslash with a backslash: + 0x27 == ' == \', and 0x5c == \ == \\ + Session response information - Optional (used only if auto accept is TRUE) + UTF-8 string + Escape single quote & backslash with a backslash: + 0x27 == ' == \', and 0x5c == \ == \\ + p2p_serv_disc_req Schedule a P2P service discovery request. The parameters for this @@ -216,15 +377,27 @@ 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). +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. +Service Query TLV has following format: +Length (2 octets, little endian) - length of following data +Service Protocol Type (1 octet) - see the table below +Service Transaction ID (1 octet) - nonzero identifier for the TLV +Query Data (Length - 2 octets of data) - service protocol specific data + +Service Protocol Types: +0 = All service protocols +1 = Bonjour +2 = UPnP +3 = WS-Discovery +4 = Wi-Fi Display + 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): @@ -270,6 +443,27 @@ p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [sec-source] 2 p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source+sink] 2,3,4,5 p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source][pri-sink] 2,3,4,5 +p2p_serv_disc_req <Unicast|Broadcast mac address> asp <Transaction ID> + <Service Name> [Service Information] + +The command can be used for service discovery for P2PS enabled devices. + +For example: p2p_serv_disc_req 00:00:00:00:00:00 asp a1 alt.example 'john' + +Parameters definition: + MAC address - Mandatory Existing + asp - Mandatory for ASP queries + Transaction ID - Mandatory non-zero ASCII hex-encoded u8 for GAS + Service Name Prefix - Mandatory UTF-8 string. + Will match from beginning of remote Service Name + Service Information Substring - Optional UTF-8 string + If Service Information Substring is not included, all services matching + Service Name Prefix will be returned. + If Service Information Substring is included, both the Substring and the + Service Name Prefix must match for service to be returned. + If remote service has no Service Information, all Substring searches + will fail. + p2p_serv_disc_cancel_req <query identifier> Cancel a pending P2P service discovery request. This command takes a @@ -345,6 +539,11 @@ p2p_service_del upnp <version hex> <service> Remove a local UPnP service from internal SD query processing. +p2p_service_del asp <adv id> + +Removes the local asp service from internal SD query list. +For example: p2p_service_del asp 4d6fc7 + p2p_service_flush Remove all local services from internal SD query processing. @@ -352,7 +551,8 @@ Remove all local services from internal SD query processing. Invitation p2p_invite [persistent=<network id>|group=<group ifname>] [peer=address] - [go_dev_addr=address] [freq=<freq in MHz>] [ht40] + [go_dev_addr=address] [freq=<freq in MHz>] [ht40] [vht] + [pref=<MHz>] 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 @@ -361,7 +561,11 @@ 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). When reinvoking a persistent group, the GO device can specify -the frequency for the group with the freq parameter. +the frequency for the group with the freq parameter. When reinvoking a +persistent group, the P2P client device can use freq parameter to force +a specific operating channel (or invitation failure if GO rejects that) +or pref parameter to request a specific channel (while allowing GO to +select to use another channel, if needed). Group Operations @@ -391,9 +595,11 @@ p2p_presence_req [<duration> <interval>] [<duration> <interval>] 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 +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. +indicates which value would be acceptable. This command returns OK +immediately and the response from the GO is indicated in a +P2P-PRESENCE-RESPONSE event message. Parameters @@ -439,6 +645,20 @@ Set postfix string to be added to the automatically generated P2P SSID (DIRECT-<two random characters>). For example, postfix of "-testing" could result in the SSID becoming DIRECT-ab-testing. +p2p_set per_sta_psk <0/1> + +Disabled(default)/enables use of per-client PSK in the P2P groups. This +can be used to request GO to assign a unique PSK for each client during +WPS provisioning. When enabled, this allow clients to be removed from +the group securily with p2p_remove_client command since that client's +PSK is removed at the same time to prevent it from connecting back using +the old PSK. When per-client PSK is not used, the client can still be +disconnected, but it will be able to re-join the group since the PSK it +learned previously is still valid. It should be noted that the default +passphrase on the GO that is normally used to allow legacy stations to +connect through manual configuration does not change here, so if that is +shared, devices with knowledge of that passphrase can still connect. + set <field> <value> Set global configuration parameters which may also affect P2P @@ -507,6 +727,13 @@ set country <two character country code> Set country code (this is included in some P2P messages). +set p2p_search_delay <delay> + +Set p2p_search_delay which adds extra delay in milliseconds between +concurrent search iterations to make p2p_find friendlier to concurrent +operations by avoiding it from taking 100% of radio resources. The +default value is 500 ms. + Status p2p_peers [discovered] @@ -551,6 +778,63 @@ remove_network <network id> Remove a network entry from configuration. +P2PS Events/Responses: + +P2PS-PROV-START: This events gets triggered when provisioning is issued for +either seeker or advertiser. + +For example, +P2PS-PROV-START 00:55:44:33:22:11 adv_id=111 adv_mac=00:55:44:33:22:11 conncap=1 session=1234567 session_mac=00:11:22:33:44:55 info='xxxx' + +Parameters definition: + MAC address - always + adv_id - always ASCII hex-encoded u32 + adv_mac - always MAC address that owns/registered the service + conncap - always mask of 0x01 (new), 0x02 (group client), 0x04 (group owner) + bits + session - always Session ID of the first session to be established + session_mac - always MAC address that owns/initiated the session + info - if available, UTF-8 string + Escaped single quote & backslash with a backslash: + \' == 0x27 == ', and \\ == 0x5c == \ + +P2PS-PROV-DONE: When provisioning is completed then this event gets triggered. + +For example, +P2PS-PROV-DONE 00:11:22:33:44:55 status=0 adv_id=111 adv_mac=00:55:44:33:22:11 conncap=1 session=1234567 session_mac=00:11:22:33:44:55 [dev_passwd_id=8 | go=p2p-wlan0-0 | join=11:22:33:44:55:66 | persist=0] + +Parameters definition: + MAC address - always main device address of peer. May be different from MAC + ultimately connected to. + status - always ascii hex-encoded u8 (0 == success, 12 == deferred success) + adv_id - always ascii hex-encoded u32 + adv_mac - always MAC address that owns/registered the service + conncap - always One of: 1 (new), 2 (group client), 4 (group owner) bits + session - always Session ID of the first session to be established + session_mac - always MAC address that owns/initiated the session + dev_passwd_id - only if conncap value == 1 (New GO negotiation) + 8 - "p2ps" password must be passed in p2p_connect command + 1 - "display" password must be passed in p2p_connect command + 5 - "keypad" password must be passed in p2p_connect command + join only - if conncap value == 2 (Client Only). Display password and "join" + must be passed in p2p_connect and address must be the MAC specified + go only - if conncap value == 4 (GO Only). Interface name must be set with a + password + persist - only if previous persistent group existed between peers and shall + be re-used. Group is restarted by sending "p2p_group_add persistent=0" + where value is taken from P2P-PROV-DONE + +Extended Events/Response + +P2P-DEVICE-FOUND 00:11:22:33:44:55 p2p_dev_addr=00:11:22:33:44:55 pri_dev_type=0-00000000-0 name='' config_methods=0x108 dev_capab=0x21 group_capab=0x0 adv_id=111 asp_svc=alt.example.chat + +Parameters definition: + adv_id - if ASP ASCII hex-encoded u32. If it is reporting the + "wildcard service", this value will be 0 + asp_svc - if ASP this is the service string. If it is reporting the + "wildcard service", this value will be org.wi-fi.wfds + + wpa_cli action script --------------------- diff --git a/wpa_supplicant/README-WPS b/wpa_supplicant/README-WPS index 1ea98432f88f0..b884f67a2435b 100644 --- a/wpa_supplicant/README-WPS +++ b/wpa_supplicant/README-WPS @@ -60,7 +60,6 @@ driver interface: 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: @@ -259,16 +258,16 @@ wps_er_start [IP address] wps_er_stop - stop WPS ER functionality -wps_er_learn <UUID> <AP PIN> +wps_er_learn <UUID|BSSID> <AP PIN> - learn AP configuration -wps_er_set_config <UUID> <network id> +wps_er_set_config <UUID|BSSID> <network id> - 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 <UUID> <AP PIN> <new SSID> <auth> <encr> <new key> +wps_er_config <UUID|BSSID> <AP PIN> <new SSID> <auth> <encr> <new key> - 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 "" @@ -277,10 +276,10 @@ wps_er_config <UUID> <AP PIN> <new SSID> <auth> <encr> <new key> <encr> must be one of the following: NONE WEP TKIP CCMP -wps_er_pbc <Enrollee UUID> +wps_er_pbc <Enrollee UUID|MAC address> - accept an Enrollee PBC using External Registrar -wps_er_pin <Enrollee UUID> <PIN> [Enrollee MAC address] +wps_er_pin <Enrollee UUID|"any"|MAC address> <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 @@ -336,6 +335,20 @@ wps_nfc_dh_privkey, wps_nfc_dev_pw) or generated dynamically with tokens during manufacturing (each station needs to have its own random keys). +The "wps_nfc_config_token <WPS/NDEF>" command can be used to build an +NFC configuration token when wpa_supplicant is controlling an AP +interface (AP or P2P GO). The output value from this command is a +hexdump of the current AP configuration (WPS parameter requests this to +include only the WPS attributes; NDEF parameter requests additional NDEF +encapsulation to be included). This data needs to be written to an NFC +tag with an external program. Once written, the NFC configuration token +can be used to touch an NFC interface on a station to provision the +credentials needed to access the network. + +The "wps_nfc_config_token <WPS/NDEF> <network id>" command can be used +to build an NFC configuration token based on a locally configured +network. + If the station includes NFC interface and reads an NFC tag with a MIME media type "application/vnd.wfa.wsc", the NDEF message payload (with or without NDEF encapsulation) can be delivered to wpa_supplicant using the @@ -352,26 +365,35 @@ the ER functionality has been started (wps_er_start), the NFC password token is used to enable enrollment of a new station (that was the source of the NFC password token). -"nfc_get_handover_req <NDEF> <WPS>" command can be used to build the -contents of a Handover Request Message for connection handover. The -first argument selects the format of the output data and the second -argument selects which type of connection handover is requested (WPS = -Wi-Fi handover as specified in WSC 2.0). - -"nfc_get_handover_sel <NDEF> <WPS>" command can be used to build the -contents of a Handover Select Message for connection handover when this -does not depend on the contents of the Handover Request Message. The -first argument selects the format of the output data and the second -argument selects which type of connection handover is requested (WPS = -Wi-Fi handover as specified in WSC 2.0). - -"nfc_rx_handover_req <hexdump of payload>" is used to indicate receipt -of NFC connection handover request. The payload may include multiple -carriers the the applicable ones are matched based on the media -type. The reply data is contents for the Handover Select Message -(hexdump). - -"nfc_rx_handover_sel <hexdump of payload>" is used to indicate receipt -of NFC connection handover select. The payload may include multiple -carriers the the applicable ones are matched based on the media -type. +"nfc_get_handover_req <NDEF> <WPS-CR>" command can be used to build the +WPS carrier record for a Handover Request Message for connection +handover. The first argument selects the format of the output data and +the second argument selects which type of connection handover is +requested (WPS-CR = Wi-Fi handover as specified in WSC 2.0). + +"nfc_get_handover_sel <NDEF> <WPS> [UUID|BSSID]" command can be used to +build the contents of a Handover Select Message for connection handover +when this does not depend on the contents of the Handover Request +Message. The first argument selects the format of the output data and +the second argument selects which type of connection handover is +requested (WPS = Wi-Fi handover as specified in WSC 2.0). If the options +UUID|BSSID argument is included, this is a request to build the handover +message for the specified AP when wpa_supplicant is operating as a WPS +ER. + +"nfc_report_handover <INIT/RESP> WPS <carrier from handover request> +<carrier from handover select>" can be used as an alternative way for +reporting completed NFC connection handover. The first parameter +indicates whether the local device initiated or responded to the +connection handover and the carrier records are the selected carrier +from the handover request and select messages as a hexdump. + +The "wps_er_nfc_config_token <WPS/NDEF> <UUID|BSSID>" command can be +used to build an NFC configuration token for the specified AP when +wpa_supplicant is operating as a WPS ER. The output value from this +command is a hexdump of the selected AP configuration (WPS parameter +requests this to include only the WPS attributes; NDEF parameter +requests additional NDEF encapsulation to be included). This data needs +to be written to an NFC tag with an external program. Once written, the +NFC configuration token can be used to touch an NFC interface on a +station to provision the credentials needed to access the network. diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index c1e4acf5b8bd0..7ecf7a85c3086 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -14,6 +14,8 @@ #include "utils/uuid.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "crypto/dh_group5.h" #include "ap/hostapd.h" #include "ap/ap_config.h" #include "ap/ap_drv_ops.h" @@ -24,6 +26,7 @@ #include "ap/ieee802_1x.h" #include "ap/wps_hostapd.h" #include "ap/ctrl_iface_ap.h" +#include "ap/dfs.h" #include "wps/wps.h" #include "common/ieee802_11_defs.h" #include "config_ssid.h" @@ -41,38 +44,42 @@ 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, - struct wpa_ssid *ssid, - struct hostapd_config *conf) +#ifdef CONFIG_IEEE80211N +static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s, + struct hostapd_config *conf, + struct hostapd_hw_modes *mode) { - struct hostapd_bss_config *bss = &conf->bss[0]; - int pairwise; +#ifdef CONFIG_P2P + u8 center_chan = 0; + u8 channel = conf->channel; - conf->driver = wpa_s->driver; + if (!conf->secondary_channel) + goto no_vht; - os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface)); + center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel); + if (!center_chan) + goto no_vht; - if (ssid->frequency == 0) { - /* default channel 11 */ - conf->hw_mode = HOSTAPD_MODE_IEEE80211G; - conf->channel = 11; - } else if (ssid->frequency >= 2412 && ssid->frequency <= 2472) { - conf->hw_mode = HOSTAPD_MODE_IEEE80211G; - conf->channel = (ssid->frequency - 2407) / 5; - } else if ((ssid->frequency >= 5180 && ssid->frequency <= 5240) || - (ssid->frequency >= 5745 && ssid->frequency <= 5825)) { - conf->hw_mode = HOSTAPD_MODE_IEEE80211A; - conf->channel = (ssid->frequency - 5000) / 5; - } else if (ssid->frequency >= 56160 + 2160 * 1 && - ssid->frequency <= 56160 + 2160 * 4) { - conf->hw_mode = HOSTAPD_MODE_IEEE80211AD; - conf->channel = (ssid->frequency - 56160) / 2160; - } else { - wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz", - ssid->frequency); - return -1; - } + /* Use 80 MHz channel */ + conf->vht_oper_chwidth = 1; + conf->vht_oper_centr_freq_seg0_idx = center_chan; + return; +no_vht: + conf->vht_oper_centr_freq_seg0_idx = + channel + conf->secondary_channel * 2; +#else /* CONFIG_P2P */ + conf->vht_oper_centr_freq_seg0_idx = + conf->channel + conf->secondary_channel * 2; +#endif /* CONFIG_P2P */ +} +#endif /* CONFIG_IEEE80211N */ + + +void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct hostapd_config *conf) +{ /* TODO: enable HT40 if driver supports it; * drop to 11b if driver does not support 11g */ @@ -126,13 +133,50 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, HT_CAP_INFO_SHORT_GI20MHZ | HT_CAP_INFO_SHORT_GI40MHZ | HT_CAP_INFO_RX_STBC_MASK | + HT_CAP_INFO_TX_STBC | HT_CAP_INFO_MAX_AMSDU_SIZE); + + if (mode->vht_capab && ssid->vht) { + conf->ieee80211ac = 1; + wpas_conf_ap_vht(wpa_s, conf, mode); + } } } #endif /* CONFIG_IEEE80211N */ +} + + +static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct hostapd_config *conf) +{ + struct hostapd_bss_config *bss = conf->bss[0]; + + conf->driver = wpa_s->driver; + + os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface)); + + conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, + &conf->channel); + if (conf->hw_mode == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz", + ssid->frequency); + return -1; + } + + wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf); + + if (ieee80211_is_dfs(ssid->frequency) && wpa_s->conf->country[0]) { + conf->ieee80211h = 1; + conf->ieee80211d = 1; + conf->country[0] = wpa_s->conf->country[0]; + conf->country[1] = wpa_s->conf->country[1]; + } #ifdef CONFIG_P2P - if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) { + if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G && + (ssid->mode == WPAS_MODE_P2P_GO || + ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)) { /* Remove 802.11b rates from supported and basic rate sets */ int *list = os_malloc(4 * sizeof(int)); if (list) { @@ -159,6 +203,17 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, } bss->isolate = !wpa_s->conf->p2p_intra_bss; + bss->force_per_enrollee_psk = wpa_s->global->p2p_per_sta_psk; + + if (ssid->p2p_group) { + os_memcpy(bss->ip_addr_go, wpa_s->parent->conf->ip_addr_go, 4); + os_memcpy(bss->ip_addr_mask, wpa_s->parent->conf->ip_addr_mask, + 4); + os_memcpy(bss->ip_addr_start, + wpa_s->parent->conf->ip_addr_start, 4); + os_memcpy(bss->ip_addr_end, wpa_s->parent->conf->ip_addr_end, + 4); + } #endif /* CONFIG_P2P */ if (ssid->ssid_len == 0) { @@ -179,7 +234,7 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, bss->wpa_key_mgmt = ssid->key_mgmt; bss->wpa_pairwise = ssid->pairwise_cipher; if (ssid->psk_set) { - os_free(bss->ssid.wpa_psk); + bin_clear_free(bss->ssid.wpa_psk, sizeof(*bss->ssid.wpa_psk)); bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk)); if (bss->ssid.wpa_psk == NULL) return -1; @@ -210,23 +265,29 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, if (ssid->dtim_period) bss->dtim_period = ssid->dtim_period; + else if (wpa_s->conf->dtim_period) + bss->dtim_period = wpa_s->conf->dtim_period; + + if (ssid->beacon_int) + conf->beacon_int = ssid->beacon_int; + else if (wpa_s->conf->beacon_int) + conf->beacon_int = wpa_s->conf->beacon_int; - /* Select group cipher based on the enabled pairwise cipher suites */ - pairwise = 0; - if (bss->wpa & 1) - pairwise |= bss->wpa_pairwise; - if (bss->wpa & 2) { - if (bss->rsn_pairwise == 0) - bss->rsn_pairwise = bss->wpa_pairwise; - pairwise |= bss->rsn_pairwise; +#ifdef CONFIG_P2P + if (wpa_s->conf->p2p_go_ctwindow > conf->beacon_int) { + wpa_printf(MSG_INFO, + "CTWindow (%d) is bigger than beacon interval (%d) - avoid configuring it", + wpa_s->conf->p2p_go_ctwindow, conf->beacon_int); + conf->p2p_go_ctwindow = 0; + } else { + conf->p2p_go_ctwindow = wpa_s->conf->p2p_go_ctwindow; } - if (pairwise & WPA_CIPHER_TKIP) - bss->wpa_group = WPA_CIPHER_TKIP; - else if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) == - WPA_CIPHER_GCMP) - bss->wpa_group = WPA_CIPHER_GCMP; - else - bss->wpa_group = WPA_CIPHER_CCMP; +#endif /* CONFIG_P2P */ + + if ((bss->wpa & 2) && bss->rsn_pairwise == 0) + bss->rsn_pairwise = bss->wpa_pairwise; + bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise, + bss->rsn_pairwise); if (bss->wpa && bss->ieee802_1x) bss->ssid.security_policy = SECURITY_WPA; @@ -257,6 +318,23 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, bss->rsn_pairwise = WPA_CIPHER_NONE; } + if (bss->wpa_group_rekey < 86400 && (bss->wpa & 2) && + (bss->wpa_group == WPA_CIPHER_CCMP || + bss->wpa_group == WPA_CIPHER_GCMP || + bss->wpa_group == WPA_CIPHER_CCMP_256 || + bss->wpa_group == WPA_CIPHER_GCMP_256)) { + /* + * Strong ciphers do not need frequent rekeying, so increase + * the default GTK rekeying period to 24 hours. + */ + bss->wpa_group_rekey = 86400; + } + +#ifdef CONFIG_IEEE80211W + if (ssid->ieee80211w != MGMT_FRAME_PROTECTION_DEFAULT) + bss->ieee80211w = ssid->ieee80211w; +#endif /* CONFIG_IEEE80211W */ + #ifdef CONFIG_WPS /* * Enable WPS by default for open and WPA/WPA2-Personal network, but @@ -266,12 +344,11 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, 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))) + (!(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) || + !(bss->wpa & 2))) goto no_wps; /* WPS2 does not allow WPA/TKIP-only * configuration */ -#endif /* CONFIG_WPS2 */ bss->eap_server = 1; if (!ssid->ignore_broadcast_ssid) @@ -311,6 +388,11 @@ no_wps: bss->disassoc_low_ack = wpa_s->conf->disassoc_low_ack; + if (wpa_s->conf->ap_vendor_elements) { + bss->vendor_elements = + wpabuf_dup(wpa_s->conf->ap_vendor_elements); + } + return 0; } @@ -320,16 +402,16 @@ 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) + if (len < IEEE80211_HDRLEN + 1) + return; + if (mgmt->u.action.category != WLAN_ACTION_PUBLIC) 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); + buf + IEEE80211_HDRLEN + 1, + len - IEEE80211_HDRLEN - 1, freq); #endif /* CONFIG_P2P */ } @@ -367,21 +449,32 @@ static void ap_sta_authorized_cb(void *ctx, const u8 *mac_addr, } +#ifdef CONFIG_P2P +static void ap_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr, + const u8 *psk, size_t psk_len) +{ + + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s->ap_iface == NULL || wpa_s->current_ssid == NULL) + return; + wpas_p2p_new_psk_cb(wpa_s, mac_addr, p2p_dev_addr, psk, psk_len); +} +#endif /* CONFIG_P2P */ + + 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) + if (len < IEEE80211_HDRLEN + 1) 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); + buf + IEEE80211_HDRLEN + 1, + len - IEEE80211_HDRLEN - 1, freq); #endif /* CONFIG_P2P */ return 0; } @@ -391,23 +484,17 @@ static int ap_probe_req_rx(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid, const u8 *ie, size_t ie_len, int ssi_signal) { -#ifdef CONFIG_P2P struct wpa_supplicant *wpa_s = ctx; return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len, ssi_signal); -#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 */ } @@ -445,41 +532,33 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, params.ssid = ssid->ssid; params.ssid_len = ssid->ssid_len; switch (ssid->mode) { - case WPAS_MODE_INFRA: - params.mode = IEEE80211_MODE_INFRA; - break; - case WPAS_MODE_IBSS: - 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; + default: + return -1; } - params.freq = ssid->frequency; + if (ssid->frequency == 0) + ssid->frequency = 2462; /* default channel 11 */ + params.freq.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 wpa_s->key_mgmt = WPA_KEY_MGMT_NONE; - params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt); - - if (ssid->pairwise_cipher & WPA_CIPHER_CCMP) - wpa_s->pairwise_cipher = WPA_CIPHER_CCMP; - else if (ssid->pairwise_cipher & WPA_CIPHER_GCMP) - wpa_s->pairwise_cipher = WPA_CIPHER_GCMP; - else if (ssid->pairwise_cipher & WPA_CIPHER_TKIP) - wpa_s->pairwise_cipher = WPA_CIPHER_TKIP; - else if (ssid->pairwise_cipher & WPA_CIPHER_NONE) - wpa_s->pairwise_cipher = WPA_CIPHER_NONE; - else { + params.key_mgmt_suite = wpa_s->key_mgmt; + + wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher, + 1); + if (wpa_s->pairwise_cipher < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise " "cipher."); return -1; } - params.pairwise_suite = cipher_suite2driver(wpa_s->pairwise_cipher); + params.pairwise_suite = wpa_s->pairwise_cipher; params.group_suite = params.pairwise_suite; #ifdef CONFIG_P2P @@ -490,9 +569,14 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, if (wpa_s->parent->set_ap_uapsd) params.uapsd = wpa_s->parent->ap_uapsd; + else if (params.p2p && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD)) + params.uapsd = 1; /* mandatory for P2P GO */ else params.uapsd = -1; + if (ieee80211_is_dfs(params.freq.freq)) + params.freq.freq = 0; /* set channel after CAC */ + if (wpa_drv_associate(wpa_s, ¶ms) < 0) { wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality"); return -1; @@ -503,7 +587,11 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, return -1; hapd_iface->owner = wpa_s; hapd_iface->drv_flags = wpa_s->drv_flags; + hapd_iface->smps_modes = wpa_s->drv_smps_modes; hapd_iface->probe_resp_offloads = wpa_s->probe_resp_offloads; + hapd_iface->extended_capa = wpa_s->extended_capa; + hapd_iface->extended_capa_mask = wpa_s->extended_capa_mask; + hapd_iface->extended_capa_len = wpa_s->extended_capa_len; wpa_s->ap_iface->conf = conf = hostapd_config_defaults(); if (conf == NULL) { @@ -516,8 +604,8 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, sizeof(wpa_s->conf->wmm_ac_params)); if (params.uapsd > 0) { - conf->bss->wmm_enabled = 1; - conf->bss->wmm_uapsd = 1; + conf->bss[0]->wmm_enabled = 1; + conf->bss[0]->wmm_uapsd = 1; } if (wpa_supplicant_conf_ap(wpa_s, ssid, conf)) { @@ -528,9 +616,9 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, #ifdef CONFIG_P2P if (ssid->mode == WPAS_MODE_P2P_GO) - conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER; + 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 | + conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER | P2P_GROUP_FORMATION; #endif /* CONFIG_P2P */ @@ -545,7 +633,7 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, for (i = 0; i < conf->num_bss; i++) { hapd_iface->bss[i] = hostapd_alloc_bss_data(hapd_iface, conf, - &conf->bss[i]); + conf->bss[i]); if (hapd_iface->bss[i] == NULL) { wpa_supplicant_ap_deinit(wpa_s); return -1; @@ -566,12 +654,18 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *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]->new_psk_cb = ap_new_psk_cb; + hapd_iface->bss[i]->new_psk_cb_ctx = wpa_s; hapd_iface->bss[i]->p2p = wpa_s->global->p2p; hapd_iface->bss[i]->p2p_group = wpas_p2p_group_init(wpa_s, ssid); #endif /* CONFIG_P2P */ hapd_iface->bss[i]->setup_complete_cb = wpas_ap_configured_cb; hapd_iface->bss[i]->setup_complete_cb_ctx = wpa_s; +#ifdef CONFIG_TESTING_OPTIONS + hapd_iface->bss[i]->ext_eapol_frame_io = + wpa_s->ext_eapol_frame_io; +#endif /* CONFIG_TESTING_OPTIONS */ } os_memcpy(hapd_iface->bss[0]->own_addr, wpa_s->own_addr, ETH_ALEN); @@ -579,6 +673,7 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, hapd_iface->bss[0]->drv_priv = wpa_s->drv_priv; wpa_s->current_ssid = ssid; + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); os_memcpy(wpa_s->bssid, wpa_s->own_addr, ETH_ALEN); wpa_s->assoc_freq = ssid->frequency; @@ -602,16 +697,19 @@ void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s) return; wpa_s->current_ssid = NULL; + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); wpa_s->assoc_freq = 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 */ + wpas_p2p_ap_deinit(wpa_s); + wpa_s->ap_iface->driver_ap_teardown = + !!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT); + hostapd_interface_deinit(wpa_s->ap_iface); hostapd_interface_free(wpa_s->ap_iface); wpa_s->ap_iface = NULL; wpa_drv_deinit_ap(wpa_s); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR + " reason=%d locally_generated=1", + MAC2STR(wpa_s->own_addr), WLAN_REASON_DEAUTH_LEAVING); } @@ -630,6 +728,8 @@ void ap_eapol_tx_status(void *ctx, const u8 *dst, { #ifdef NEED_AP_MLME struct wpa_supplicant *wpa_s = ctx; + if (!wpa_s->ap_iface) + return; hostapd_tx_status(wpa_s->ap_iface->bss[0], dst, data, len, ack); #endif /* NEED_AP_MLME */ } @@ -738,9 +838,14 @@ 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, "%08d", rpin); + if (os_snprintf_error(buflen, ret_len)) + return -1; pin = buf; - } else + } else if (buf) { ret_len = os_snprintf(buf, buflen, "%s", pin); + if (os_snprintf_error(buflen, ret_len)) + return -1; + } ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], bssid, "any", pin, timeout); @@ -830,7 +935,7 @@ int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin, 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)) + if (os_snprintf_error(sizeof(pin_txt), ret)) return -1; os_free(hapd->conf->ap_pin); hapd->conf->ap_pin = os_strdup(pin_txt); @@ -866,6 +971,47 @@ void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s) hapd->conf->ap_pin = NULL; } + +#ifdef CONFIG_WPS_NFC + +struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s, + int ndef) +{ + struct hostapd_data *hapd; + + if (wpa_s->ap_iface == NULL) + return NULL; + hapd = wpa_s->ap_iface->bss[0]; + return hostapd_wps_nfc_config_token(hapd, ndef); +} + + +struct wpabuf * wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s, + int ndef) +{ + struct hostapd_data *hapd; + + if (wpa_s->ap_iface == NULL) + return NULL; + hapd = wpa_s->ap_iface->bss[0]; + return hostapd_wps_nfc_hs_cr(hapd, ndef); +} + + +int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, + const struct wpabuf *req, + const struct wpabuf *sel) +{ + struct hostapd_data *hapd; + + if (wpa_s->ap_iface == NULL) + return -1; + hapd = wpa_s->ap_iface->bss[0]; + return hostapd_wps_nfc_report_handover(hapd, req, sel); +} + +#endif /* CONFIG_WPS_NFC */ + #endif /* CONFIG_WPS */ @@ -874,30 +1020,45 @@ void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s) int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { - if (wpa_s->ap_iface == NULL) + struct hostapd_data *hapd; + + if (wpa_s->ap_iface) + hapd = wpa_s->ap_iface->bss[0]; + else if (wpa_s->ifmsh) + hapd = wpa_s->ifmsh->bss[0]; + else return -1; - return hostapd_ctrl_iface_sta_first(wpa_s->ap_iface->bss[0], - buf, buflen); + return hostapd_ctrl_iface_sta_first(hapd, buf, buflen); } int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr, char *buf, size_t buflen) { - if (wpa_s->ap_iface == NULL) + struct hostapd_data *hapd; + + if (wpa_s->ap_iface) + hapd = wpa_s->ap_iface->bss[0]; + else if (wpa_s->ifmsh) + hapd = wpa_s->ifmsh->bss[0]; + else return -1; - return hostapd_ctrl_iface_sta(wpa_s->ap_iface->bss[0], txtaddr, - buf, buflen); + return hostapd_ctrl_iface_sta(hapd, txtaddr, buf, buflen); } int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr, char *buf, size_t buflen) { - if (wpa_s->ap_iface == NULL) + struct hostapd_data *hapd; + + if (wpa_s->ap_iface) + hapd = wpa_s->ap_iface->bss[0]; + else if (wpa_s->ifmsh) + hapd = wpa_s->ifmsh->bss[0]; + else return -1; - return hostapd_ctrl_iface_sta_next(wpa_s->ap_iface->bss[0], txtaddr, - buf, buflen); + return hostapd_ctrl_iface_sta_next(hapd, txtaddr, buf, buflen); } @@ -943,7 +1104,7 @@ int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf, wpa_cipher_txt(conf->wpa_group), wpa_key_mgmt_txt(conf->wpa_key_mgmt, conf->wpa)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; return pos - buf; @@ -965,9 +1126,9 @@ int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s) #ifdef CONFIG_P2P if (ssid->mode == WPAS_MODE_P2P_GO) - iface->conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER; + 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 | + iface->conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER | P2P_GROUP_FORMATION; #endif /* CONFIG_P2P */ @@ -981,14 +1142,40 @@ int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s) } +int ap_switch_channel(struct wpa_supplicant *wpa_s, + struct csa_settings *settings) +{ +#ifdef NEED_AP_MLME + if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]) + return -1; + + return hostapd_switch_channel(wpa_s->ap_iface->bss[0], settings); +#else /* NEED_AP_MLME */ + return -1; +#endif /* NEED_AP_MLME */ +} + + +int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos) +{ + struct csa_settings settings; + int ret = hostapd_parse_csa_settings(pos, &settings); + + if (ret) + return ret; + + return ap_switch_channel(wpa_s, &settings); +} + + void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, - int offset) + int offset, int width, int cf1, int cf2) { if (!wpa_s->ap_iface) return; wpa_s->assoc_freq = freq; - hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset); + hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset, width, cf1, cf1); } @@ -1031,3 +1218,122 @@ int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s, return 0; } + + +#ifdef CONFIG_WPS_NFC +int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id, + const struct wpabuf *pw, const u8 *pubkey_hash) +{ + struct hostapd_data *hapd; + struct wps_context *wps; + + if (!wpa_s->ap_iface) + return -1; + hapd = wpa_s->ap_iface->bss[0]; + wps = hapd->wps; + + if (wpa_s->parent->conf->wps_nfc_dh_pubkey == NULL || + wpa_s->parent->conf->wps_nfc_dh_privkey == NULL) { + wpa_printf(MSG_DEBUG, "P2P: No NFC DH key known"); + return -1; + } + + dh5_free(wps->dh_ctx); + wpabuf_free(wps->dh_pubkey); + wpabuf_free(wps->dh_privkey); + wps->dh_privkey = wpabuf_dup( + wpa_s->parent->conf->wps_nfc_dh_privkey); + wps->dh_pubkey = wpabuf_dup( + wpa_s->parent->conf->wps_nfc_dh_pubkey); + if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) { + wps->dh_ctx = NULL; + wpabuf_free(wps->dh_pubkey); + wps->dh_pubkey = NULL; + wpabuf_free(wps->dh_privkey); + wps->dh_privkey = NULL; + return -1; + } + wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey); + if (wps->dh_ctx == NULL) + return -1; + + return wps_registrar_add_nfc_pw_token(hapd->wps->registrar, pubkey_hash, + pw_id, + pw ? wpabuf_head(pw) : NULL, + pw ? wpabuf_len(pw) : 0, 1); +} +#endif /* CONFIG_WPS_NFC */ + + +int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s) +{ + struct hostapd_data *hapd; + + if (!wpa_s->ap_iface) + return -1; + hapd = wpa_s->ap_iface->bss[0]; + return hostapd_ctrl_iface_stop_ap(hapd); +} + + +#ifdef NEED_AP_MLME +void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s, + struct dfs_event *radar) +{ + if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]) + return; + wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq); + hostapd_dfs_radar_detected(wpa_s->ap_iface, radar->freq, + radar->ht_enabled, radar->chan_offset, + radar->chan_width, + radar->cf1, radar->cf2); +} + + +void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s, + struct dfs_event *radar) +{ + if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]) + return; + wpa_printf(MSG_DEBUG, "DFS CAC started on %d MHz", radar->freq); + hostapd_dfs_start_cac(wpa_s->ap_iface, radar->freq, + radar->ht_enabled, radar->chan_offset, + radar->chan_width, radar->cf1, radar->cf2); +} + + +void wpas_event_dfs_cac_finished(struct wpa_supplicant *wpa_s, + struct dfs_event *radar) +{ + if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]) + return; + wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq); + hostapd_dfs_complete_cac(wpa_s->ap_iface, 1, radar->freq, + radar->ht_enabled, radar->chan_offset, + radar->chan_width, radar->cf1, radar->cf2); +} + + +void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s, + struct dfs_event *radar) +{ + if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]) + return; + wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq); + hostapd_dfs_complete_cac(wpa_s->ap_iface, 0, radar->freq, + radar->ht_enabled, radar->chan_offset, + radar->chan_width, radar->cf1, radar->cf2); +} + + +void wpas_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s, + struct dfs_event *radar) +{ + if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0]) + return; + wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq); + hostapd_dfs_nop_finished(wpa_s->ap_iface, radar->freq, + radar->ht_enabled, radar->chan_offset, + radar->chan_width, radar->cf1, radar->cf2); +} +#endif /* NEED_AP_MLME */ diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h index 536064f7c74f4..3f4151d8cb946 100644 --- a/wpa_supplicant/ap.h +++ b/wpa_supplicant/ap.h @@ -50,7 +50,47 @@ 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); +int ap_switch_channel(struct wpa_supplicant *wpa_s, + struct csa_settings *settings); +int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *txtaddr); void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht, - int offset); + int offset, int width, int cf1, int cf2); +struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s, + int ndef); +#ifdef CONFIG_AP +struct wpabuf * wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s, + int ndef); +#else /* CONFIG_AP */ +static inline struct wpabuf * +wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s, + int ndef) +{ + return NULL; +} +#endif /* CONFIG_AP */ + +int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, + const struct wpabuf *req, + const struct wpabuf *sel); +int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id, + const struct wpabuf *pw, const u8 *pubkey_hash); + +struct hostapd_config; +void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct hostapd_config *conf); + +int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s); + +void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s, + struct dfs_event *radar); +void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s, + struct dfs_event *radar); +void wpas_event_dfs_cac_finished(struct wpa_supplicant *wpa_s, + struct dfs_event *radar); +void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s, + struct dfs_event *radar); +void wpas_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s, + struct dfs_event *radar); #endif /* AP_H */ diff --git a/wpa_supplicant/bgscan.c b/wpa_supplicant/bgscan.c index 9a9bd5207a90e..f74cdbf24a450 100644 --- a/wpa_supplicant/bgscan.c +++ b/wpa_supplicant/bgscan.c @@ -31,9 +31,9 @@ static const struct bgscan_ops * bgscan_modules[] = { }; -int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) +int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + const char *name) { - const char *name = ssid->bgscan; const char *params; size_t nlen; int i; @@ -41,7 +41,7 @@ int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) bgscan_deinit(wpa_s); if (name == NULL) - return 0; + return -1; params = os_strchr(name, ':'); if (params == NULL) { diff --git a/wpa_supplicant/bgscan.h b/wpa_supplicant/bgscan.h index e9d15fc5cf533..9131e4ecddc05 100644 --- a/wpa_supplicant/bgscan.h +++ b/wpa_supplicant/bgscan.h @@ -29,7 +29,8 @@ struct bgscan_ops { #ifdef CONFIG_BGSCAN -int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + const char *name); void bgscan_deinit(struct wpa_supplicant *wpa_s); int bgscan_notify_scan(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); @@ -41,7 +42,7 @@ void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above, #else /* CONFIG_BGSCAN */ static inline int bgscan_init(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid) + struct wpa_ssid *ssid, const char name) { return 0; } diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c index 07d31e4cb623c..a320cc43068c9 100644 --- a/wpa_supplicant/bgscan_learn.c +++ b/wpa_supplicant/bgscan_learn.c @@ -34,7 +34,7 @@ struct bgscan_learn_data { int signal_threshold; int short_interval; /* use if signal < threshold */ int long_interval; /* use if signal > threshold */ - struct os_time last_bgscan; + struct os_reltime last_bgscan; char *fname; struct dl_list bss; int *supp_freqs; @@ -240,17 +240,14 @@ static int * bgscan_learn_get_probe_freq(struct bgscan_learn_data *data, if (data->supp_freqs == NULL) return freqs; - idx = data->probe_idx + 1; - while (idx != data->probe_idx) { - if (data->supp_freqs[idx] == 0) { - if (data->probe_idx == 0) - break; - idx = 0; - } + idx = data->probe_idx; + do { 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; + data->probe_idx = idx + 1; + if (data->supp_freqs[data->probe_idx] == 0) + data->probe_idx = 0; n = os_realloc_array(freqs, count + 2, sizeof(int)); if (n == NULL) return freqs; @@ -262,7 +259,9 @@ static int * bgscan_learn_get_probe_freq(struct bgscan_learn_data *data, } idx++; - } + if (data->supp_freqs[idx] == 0) + idx = 0; + } while (idx != data->probe_idx); return freqs; } @@ -295,7 +294,7 @@ static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx) int ret; ret = os_snprintf(pos, msg + sizeof(msg) - pos, " %d", freqs[i]); - if (ret < 0 || ret >= msg + sizeof(msg) - pos) + if (os_snprintf_error(msg + sizeof(msg) - pos, ret)) break; pos += ret; } @@ -311,7 +310,7 @@ static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx) eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout, data, NULL); } else - os_get_time(&data->last_bgscan); + os_get_reltime(&data->last_bgscan); os_free(freqs); } @@ -363,6 +362,9 @@ static int * bgscan_learn_get_supp_freqs(struct wpa_supplicant *wpa_s) for (j = 0; j < modes[i].num_channels; j++) { if (modes[i].channels[j].flag & HOSTAPD_CHAN_DISABLED) continue; + /* some hw modes (e.g. 11b & 11g) contain same freqs */ + if (in_array(freqs, modes[i].channels[j].freq)) + continue; n = os_realloc_array(freqs, count + 2, sizeof(int)); if (n == NULL) continue; @@ -419,6 +421,14 @@ static void * bgscan_learn_init(struct wpa_supplicant *wpa_s, data->supp_freqs = bgscan_learn_get_supp_freqs(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; + } + eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout, data, NULL); @@ -428,7 +438,7 @@ static void * bgscan_learn_init(struct wpa_supplicant *wpa_s, * us skip an immediate new scan in cases where the current signal * level is below the bgscan threshold. */ - os_get_time(&data->last_bgscan); + os_get_reltime(&data->last_bgscan); return data; } @@ -555,7 +565,7 @@ static void bgscan_learn_notify_signal_change(void *priv, int above, { struct bgscan_learn_data *data = priv; int scan = 0; - struct os_time now; + struct os_reltime now; if (data->short_interval == data->long_interval || data->signal_threshold == 0) @@ -569,7 +579,7 @@ static void bgscan_learn_notify_signal_change(void *priv, int above, wpa_printf(MSG_DEBUG, "bgscan learn: Start using short bgscan " "interval"); data->scan_interval = data->short_interval; - os_get_time(&now); + os_get_reltime(&now); if (now.sec > data->last_bgscan.sec + 1) scan = 1; } else if (data->scan_interval == data->short_interval && above) { @@ -584,7 +594,7 @@ static void bgscan_learn_notify_signal_change(void *priv, int above, * Signal dropped further 4 dB. Request a new scan if we have * not yet scanned in a while. */ - os_get_time(&now); + os_get_reltime(&now); if (now.sec > data->last_bgscan.sec + 10) scan = 1; } diff --git a/wpa_supplicant/bgscan_simple.c b/wpa_supplicant/bgscan_simple.c index 479f703ba9591..a467cc5b92719 100644 --- a/wpa_supplicant/bgscan_simple.c +++ b/wpa_supplicant/bgscan_simple.c @@ -26,7 +26,7 @@ struct bgscan_simple_data { int max_short_scans; /* maximum times we short-scan before back-off */ int short_interval; /* use if signal < threshold */ int long_interval; /* use if signal > threshold */ - struct os_time last_bgscan; + struct os_reltime last_bgscan; }; @@ -75,7 +75,7 @@ static void bgscan_simple_timeout(void *eloop_ctx, void *timeout_ctx) */ data->short_scan_count--; } - os_get_time(&data->last_bgscan); + os_get_reltime(&data->last_bgscan); } } @@ -159,7 +159,7 @@ static void * bgscan_simple_init(struct wpa_supplicant *wpa_s, * us skip an immediate new scan in cases where the current signal * level is below the bgscan threshold. */ - os_get_time(&data->last_bgscan); + os_get_reltime(&data->last_bgscan); return data; } @@ -211,7 +211,7 @@ static void bgscan_simple_notify_signal_change(void *priv, int above, { struct bgscan_simple_data *data = priv; int scan = 0; - struct os_time now; + struct os_reltime now; if (data->short_interval == data->long_interval || data->signal_threshold == 0) @@ -225,7 +225,7 @@ static void bgscan_simple_notify_signal_change(void *priv, int above, wpa_printf(MSG_DEBUG, "bgscan simple: Start using short " "bgscan interval"); data->scan_interval = data->short_interval; - os_get_time(&now); + os_get_reltime(&now); if (now.sec > data->last_bgscan.sec + 1 && data->short_scan_count <= data->max_short_scans) /* @@ -259,7 +259,7 @@ static void bgscan_simple_notify_signal_change(void *priv, int above, * Signal dropped further 4 dB. Request a new scan if we have * not yet scanned in a while. */ - os_get_time(&now); + os_get_reltime(&now); if (now.sec > data->last_bgscan.sec + 10) scan = 1; } diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c index 87b7db89a76cf..b4c47e21051d4 100644 --- a/wpa_supplicant/bss.c +++ b/wpa_supplicant/bss.c @@ -1,6 +1,6 @@ /* * BSS table - * Copyright (c) 2009-2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -85,6 +85,7 @@ static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp) #define ANQP_DUP(f) if (anqp->f) n->f = wpabuf_dup(anqp->f) #ifdef CONFIG_INTERWORKING + ANQP_DUP(capability_list); ANQP_DUP(venue_name); ANQP_DUP(network_auth_type); ANQP_DUP(roaming_consortium); @@ -94,10 +95,12 @@ static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp) ANQP_DUP(domain_name); #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_HS20 + ANQP_DUP(hs20_capability_list); ANQP_DUP(hs20_operator_friendly_name); ANQP_DUP(hs20_wan_metrics); ANQP_DUP(hs20_connection_capability); ANQP_DUP(hs20_operating_class); + ANQP_DUP(hs20_osu_providers_list); #endif /* CONFIG_HS20 */ #undef ANQP_DUP @@ -153,6 +156,7 @@ static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp) } #ifdef CONFIG_INTERWORKING + wpabuf_free(anqp->capability_list); wpabuf_free(anqp->venue_name); wpabuf_free(anqp->network_auth_type); wpabuf_free(anqp->roaming_consortium); @@ -162,16 +166,43 @@ static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp) wpabuf_free(anqp->domain_name); #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_HS20 + wpabuf_free(anqp->hs20_capability_list); wpabuf_free(anqp->hs20_operator_friendly_name); wpabuf_free(anqp->hs20_wan_metrics); wpabuf_free(anqp->hs20_connection_capability); wpabuf_free(anqp->hs20_operating_class); + wpabuf_free(anqp->hs20_osu_providers_list); #endif /* CONFIG_HS20 */ os_free(anqp); } +static void wpa_bss_update_pending_connect(struct wpa_supplicant *wpa_s, + struct wpa_bss *old_bss, + struct wpa_bss *new_bss) +{ + struct wpa_radio_work *work; + struct wpa_connect_work *cwork; + + work = radio_work_pending(wpa_s, "sme-connect"); + if (!work) + work = radio_work_pending(wpa_s, "connect"); + if (!work) + return; + + cwork = work->ctx; + if (cwork->bss != old_bss) + return; + + wpa_printf(MSG_DEBUG, + "Update BSS pointer for the pending connect radio work"); + cwork->bss = new_bss; + if (!new_bss) + cwork->bss_removed = 1; +} + + static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, const char *reason) { @@ -188,6 +219,7 @@ static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, } } } + wpa_bss_update_pending_connect(wpa_s, bss, NULL); dl_list_del(&bss->list); dl_list_del(&bss->list_id); wpa_s->num_bss--; @@ -224,10 +256,27 @@ struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid, } -static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src) +static void calculate_update_time(const struct os_reltime *fetch_time, + unsigned int age_ms, + struct os_reltime *update_time) { os_time_t usec; + update_time->sec = fetch_time->sec; + update_time->usec = fetch_time->usec; + update_time->sec -= age_ms / 1000; + usec = (age_ms % 1000) * 1000; + if (update_time->usec < usec) { + update_time->sec--; + update_time->usec += 1000000; + } + update_time->usec -= usec; +} + + +static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src, + struct os_reltime *fetch_time) +{ dst->flags = src->flags; os_memcpy(dst->bssid, src->bssid, ETH_ALEN); dst->freq = src->freq; @@ -237,15 +286,10 @@ static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src) dst->noise = src->noise; dst->level = src->level; dst->tsf = src->tsf; + dst->est_throughput = src->est_throughput; + dst->snr = src->snr; - os_get_time(&dst->last_update); - dst->last_update.sec -= src->age / 1000; - usec = (src->age % 1000) * 1000; - if (dst->last_update.usec < usec) { - dst->last_update.sec--; - dst->last_update.usec += 1000000; - } - dst->last_update.usec -= usec; + calculate_update_time(fetch_time, src->age, &dst->last_update); } @@ -268,8 +312,9 @@ static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { return bss == wpa_s->current_bss || - os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 || - os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0; + (!is_zero_ether_addr(bss->bssid) && + (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 || + os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0)); } @@ -315,7 +360,8 @@ static int wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s) static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s, const u8 *ssid, size_t ssid_len, - struct wpa_scan_res *res) + struct wpa_scan_res *res, + struct os_reltime *fetch_time) { struct wpa_bss *bss; @@ -324,7 +370,7 @@ static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s, return NULL; bss->id = wpa_s->bss_next_id++; bss->last_update_idx = wpa_s->bss_update_idx; - wpa_bss_copy_res(bss, res); + wpa_bss_copy_res(bss, res, fetch_time); os_memcpy(bss->ssid, ssid, ssid_len); bss->ssid_len = ssid_len; bss->ie_len = res->ie_len; @@ -332,6 +378,14 @@ static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s, os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len); wpa_bss_set_hessid(bss); + if (wpa_s->num_bss + 1 > wpa_s->conf->bss_max_count && + wpa_bss_remove_oldest(wpa_s) != 0) { + wpa_printf(MSG_ERROR, "Increasing the MAX BSS count to %d " + "because all BSSes are in use. We should normally " + "not get here!", (int) wpa_s->num_bss + 1); + wpa_s->conf->bss_max_count = wpa_s->num_bss + 1; + } + dl_list_add_tail(&wpa_s->bss, &bss->list); dl_list_add_tail(&wpa_s->bss_id, &bss->list_id); wpa_s->num_bss++; @@ -339,13 +393,6 @@ static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s, " 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 && - wpa_bss_remove_oldest(wpa_s) != 0) { - wpa_printf(MSG_ERROR, "Increasing the MAX BSS count to %d " - "because all BSSes are in use. We should normally " - "not get here!", (int) wpa_s->num_bss); - wpa_s->conf->bss_max_count = wpa_s->num_bss; - } return bss; } @@ -475,21 +522,39 @@ static void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes, if (changes & WPA_BSS_RATES_CHANGED_FLAG) wpas_notify_bss_rates_changed(wpa_s, bss->id); + + wpas_notify_bss_seen(wpa_s, bss->id); } static struct wpa_bss * wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, - struct wpa_scan_res *res) + struct wpa_scan_res *res, struct os_reltime *fetch_time) { u32 changes; changes = wpa_bss_compare_res(bss, res); bss->scan_miss_count = 0; bss->last_update_idx = wpa_s->bss_update_idx; - wpa_bss_copy_res(bss, res); + wpa_bss_copy_res(bss, res, fetch_time); /* Move the entry to the end of the list */ dl_list_del(&bss->list); +#ifdef CONFIG_P2P + if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) && + !wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE)) { + /* + * This can happen when non-P2P station interface runs a scan + * without P2P IE in the Probe Request frame. P2P GO would reply + * to that with a Probe Response that does not include P2P IE. + * Do not update the IEs in this BSS entry to avoid such loss of + * information that may be needed for P2P operations to + * determine group information. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Do not update scan IEs for " + MACSTR " since that would remove P2P IE information", + MAC2STR(bss->bssid)); + } else +#endif /* CONFIG_P2P */ if (bss->ie_len + bss->beacon_ie_len >= res->ie_len + res->beacon_ie_len) { os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len); @@ -511,6 +576,7 @@ wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, } if (wpa_s->current_bss == bss) wpa_s->current_bss = nbss; + wpa_bss_update_pending_connect(wpa_s, bss, nbss); bss = nbss; os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len); @@ -551,17 +617,35 @@ void wpa_bss_update_start(struct wpa_supplicant *wpa_s) * wpa_bss_update_scan_res - Update a BSS table entry based on a scan result * @wpa_s: Pointer to wpa_supplicant data * @res: Scan result + * @fetch_time: Time when the result was fetched from the driver * * This function updates a BSS table entry (or adds one) based on a scan result. * This is called separately for each scan result between the calls to * wpa_bss_update_start() and wpa_bss_update_end(). */ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, - struct wpa_scan_res *res) + struct wpa_scan_res *res, + struct os_reltime *fetch_time) { - const u8 *ssid, *p2p; + const u8 *ssid, *p2p, *mesh; struct wpa_bss *bss; + if (wpa_s->conf->ignore_old_scan_res) { + struct os_reltime update; + calculate_update_time(fetch_time, res->age, &update); + if (os_reltime_before(&update, &wpa_s->scan_trigger_time)) { + struct os_reltime age; + os_reltime_sub(&wpa_s->scan_trigger_time, &update, + &age); + wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Ignore driver BSS " + "table entry that is %u.%06u seconds older " + "than our scan trigger", + (unsigned int) age.sec, + (unsigned int) age.usec); + return; + } + } + ssid = wpa_scan_get_ie(res, WLAN_EID_SSID); if (ssid == NULL) { wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for " @@ -593,11 +677,26 @@ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, /* TODO: add option for ignoring BSSes we are not interested in * (to save memory) */ + + mesh = wpa_scan_get_ie(res, WLAN_EID_MESH_ID); + if (mesh && mesh[1] <= 32) + ssid = mesh; + bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]); if (bss == NULL) - bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res); - else - bss = wpa_bss_update(wpa_s, bss, res); + bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res, fetch_time); + else { + bss = wpa_bss_update(wpa_s, bss, res, fetch_time); + if (wpa_s->last_scan_res) { + unsigned int i; + for (i = 0; i < wpa_s->last_scan_res_used; i++) { + if (bss == wpa_s->last_scan_res[i]) { + /* Already in the list */ + return; + } + } + } + } if (bss == NULL) return; @@ -616,7 +715,8 @@ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, wpa_s->last_scan_res_size = siz; } - wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss; + if (wpa_s->last_scan_res) + wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss; } @@ -676,26 +776,10 @@ void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info, { struct wpa_bss *bss, *n; - wpa_s->last_scan_full = 0; - os_get_time(&wpa_s->last_scan); + os_get_reltime(&wpa_s->last_scan); if (!new_scan) return; /* do not expire entries without new scan */ - if (info && !info->aborted && !info->freqs) { - size_t i; - if (info->num_ssids == 0) { - wpa_s->last_scan_full = 1; - } else { - for (i = 0; i < info->num_ssids; i++) { - if (info->ssids[i].ssid == NULL || - info->ssids[i].ssid_len == 0) { - wpa_s->last_scan_full = 1; - break; - } - } - } - } - dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) { if (wpa_bss_in_use(wpa_s, bss)) continue; @@ -709,10 +793,8 @@ void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info, } } - wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%u/%u " - "last_scan_full=%d", - wpa_s->last_scan_res_used, wpa_s->last_scan_res_size, - wpa_s->last_scan_full); + wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%u/%u", + wpa_s->last_scan_res_used, wpa_s->last_scan_res_size); } @@ -726,19 +808,19 @@ void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info, void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age) { struct wpa_bss *bss, *n; - struct os_time t; + struct os_reltime t; if (dl_list_empty(&wpa_s->bss)) return; - os_get_time(&t); + os_get_reltime(&t); 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)) { + if (os_reltime_before(&bss->last_update, &t)) { wpa_bss_remove(wpa_s, bss, __func__); } else break; @@ -782,6 +864,8 @@ void wpa_bss_flush(struct wpa_supplicant *wpa_s) { struct wpa_bss *bss, *n; + wpa_s->clear_driver_scan_cache = 1; + if (wpa_s->bss.next == NULL) return; /* BSS table not yet initialized */ @@ -824,6 +908,34 @@ struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s, } +/** + * wpa_bss_get_bssid_latest - Fetch the latest BSS table entry based on BSSID + * @wpa_s: Pointer to wpa_supplicant data + * @bssid: BSSID + * Returns: Pointer to the BSS entry or %NULL if not found + * + * This function is like wpa_bss_get_bssid(), but full BSS table is iterated to + * find the entry that has the most recent update. This can help in finding the + * correct entry in cases where the SSID of the AP may have changed recently + * (e.g., in WPS reconfiguration cases). + */ +struct wpa_bss * wpa_bss_get_bssid_latest(struct wpa_supplicant *wpa_s, + const u8 *bssid) +{ + struct wpa_bss *bss, *found = NULL; + if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid)) + return NULL; + dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) { + if (os_memcmp(bss->bssid, bssid, ETH_ALEN) != 0) + continue; + if (found == NULL || + os_reltime_before(&found->last_update, &bss->last_update)) + found = bss; + } + return found; +} + + #ifdef CONFIG_P2P /** * wpa_bss_get_p2p_dev_addr - Fetch a BSS table entry based on P2P Device Addr @@ -865,6 +977,29 @@ struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id) /** + * wpa_bss_get_id_range - Fetch a BSS table entry based on identifier range + * @wpa_s: Pointer to wpa_supplicant data + * @idf: Smallest allowed identifier assigned for the entry + * @idf: Largest allowed identifier assigned for the entry + * Returns: Pointer to the BSS entry or %NULL if not found + * + * This function is similar to wpa_bss_get_id() but allows a BSS entry with the + * smallest id value to be fetched within the specified range without the + * caller having to know the exact id. + */ +struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s, + unsigned int idf, unsigned int idl) +{ + struct wpa_bss *bss; + dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) { + if (bss->id >= idf && bss->id <= idl) + return bss; + } + return NULL; +} + + +/** * wpa_bss_get_ie - Fetch a specified information element from a BSS entry * @bss: BSS table entry * @ie: Information element identitifier (WLAN_EID_*) @@ -922,6 +1057,43 @@ const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type) /** + * wpa_bss_get_vendor_ie_beacon - Fetch a vendor information from a BSS entry + * @bss: BSS table entry + * @vendor_type: Vendor type (four octets starting the IE payload) + * Returns: Pointer to the information element (id field) or %NULL if not found + * + * This function returns the first matching information element in the BSS + * entry. + * + * This function is like wpa_bss_get_vendor_ie(), but uses IE buffer only + * from Beacon frames instead of either Beacon or Probe Response frames. + */ +const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss, + u32 vendor_type) +{ + const u8 *end, *pos; + + if (bss->beacon_ie_len == 0) + return NULL; + + pos = (const u8 *) (bss + 1); + pos += bss->ie_len; + end = pos + bss->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])) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + + +/** * wpa_bss_get_vendor_ie_multi - Fetch vendor IE data from a BSS entry * @bss: BSS table entry * @vendor_type: Vendor type (four octets starting the IE payload) diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h index 01f6c59d2388b..634aa3cc06d09 100644 --- a/wpa_supplicant/bss.h +++ b/wpa_supplicant/bss.h @@ -1,6 +1,6 @@ /* * BSS table - * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -26,6 +26,7 @@ struct wpa_bss_anqp { /** Number of BSS entries referring to this ANQP data instance */ unsigned int users; #ifdef CONFIG_INTERWORKING + struct wpabuf *capability_list; struct wpabuf *venue_name; struct wpabuf *network_auth_type; struct wpabuf *roaming_consortium; @@ -35,10 +36,12 @@ struct wpa_bss_anqp { struct wpabuf *domain_name; #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_HS20 + struct wpabuf *hs20_capability_list; struct wpabuf *hs20_operator_friendly_name; struct wpabuf *hs20_wan_metrics; struct wpabuf *hs20_connection_capability; struct wpabuf *hs20_operating_class; + struct wpabuf *hs20_osu_providers_list; #endif /* CONFIG_HS20 */ }; @@ -84,7 +87,11 @@ struct wpa_bss { /** Timestamp of last Beacon/Probe Response frame */ u64 tsf; /** Time of the last update (i.e., Beacon or Probe Response RX) */ - struct os_time last_update; + struct os_reltime last_update; + /** Estimated throughput in kbps */ + unsigned int est_throughput; + /** Signal-to-noise ratio in dB */ + int snr; /** ANQP data */ struct wpa_bss_anqp *anqp; /** Length of the following IE field in octets (from Probe Response) */ @@ -97,7 +104,8 @@ struct wpa_bss { void wpa_bss_update_start(struct wpa_supplicant *wpa_s); void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, - struct wpa_scan_res *res); + struct wpa_scan_res *res, + struct os_reltime *fetch_time); 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); @@ -108,11 +116,17 @@ 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_bssid_latest(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); +struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s, + unsigned int idf, unsigned int idl); 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); +const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss, + u32 vendor_type); struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss, u32 vendor_type); struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss, @@ -122,4 +136,15 @@ int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates); struct wpa_bss_anqp * wpa_bss_anqp_alloc(void); int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss); +static inline int bss_is_dmg(const struct wpa_bss *bss) +{ + return bss->freq > 45000; +} + +static inline void wpa_bss_update_level(struct wpa_bss *bss, int new_level) +{ + if (bss != NULL && new_level < 0) + bss->level = new_level; +} + #endif /* BSS_H */ diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index 0fab07aed6dd6..8e6cd2006b0ea 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / Configuration parser and common functions - * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,7 @@ #include "common.h" #include "utils/uuid.h" +#include "utils/ip_addr.h" #include "crypto/sha1.h" #include "rsn_supp/wpa.h" #include "eap_peer/eap.h" @@ -178,10 +179,17 @@ static int wpa_config_parse_int(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) { - int *dst; + int val, *dst; + char *end; dst = (int *) (((u8 *) ssid) + (long) data->param1); - *dst = atoi(value); + val = strtol(value, &end, 0); + if (*end) { + wpa_printf(MSG_ERROR, "Line %d: invalid number \"%s\"", + line, value); + return -1; + } + *dst = val; wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst); if (data->param3 && *dst < (long) data->param3) { @@ -217,7 +225,7 @@ static char * wpa_config_write_int(const struct parse_data *data, if (value == NULL) return NULL; res = os_snprintf(value, 20, "%d", *src); - if (res < 0 || res >= 20) { + if (os_snprintf_error(20, res)) { os_free(value); return NULL; } @@ -227,6 +235,99 @@ static char * wpa_config_write_int(const struct parse_data *data, #endif /* NO_CONFIG_WRITE */ +static int wpa_config_parse_addr_list(const struct parse_data *data, + int line, const char *value, + u8 **list, size_t *num, char *name, + u8 abort_on_error, u8 masked) +{ + const char *pos; + u8 *buf, *n, addr[2 * ETH_ALEN]; + size_t count; + + buf = NULL; + count = 0; + + pos = value; + while (pos && *pos) { + while (*pos == ' ') + pos++; + + if (hwaddr_masked_aton(pos, addr, &addr[ETH_ALEN], masked)) { + if (abort_on_error || count == 0) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid %s address '%s'", + line, name, value); + os_free(buf); + return -1; + } + /* continue anyway since this could have been from a + * truncated configuration file line */ + wpa_printf(MSG_INFO, + "Line %d: Ignore likely truncated %s address '%s'", + line, name, pos); + } else { + n = os_realloc_array(buf, count + 1, 2 * ETH_ALEN); + if (n == NULL) { + os_free(buf); + return -1; + } + buf = n; + os_memmove(buf + 2 * ETH_ALEN, buf, + count * 2 * ETH_ALEN); + os_memcpy(buf, addr, 2 * ETH_ALEN); + count++; + wpa_printf(MSG_MSGDUMP, + "%s: addr=" MACSTR " mask=" MACSTR, + name, MAC2STR(addr), + MAC2STR(&addr[ETH_ALEN])); + } + + pos = os_strchr(pos, ' '); + } + + os_free(*list); + *list = buf; + *num = count; + + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_addr_list(const struct parse_data *data, + const u8 *list, size_t num, char *name) +{ + char *value, *end, *pos; + int res; + size_t i; + + if (list == NULL || num == 0) + return NULL; + + value = os_malloc(2 * 20 * num); + if (value == NULL) + return NULL; + pos = value; + end = value + 2 * 20 * num; + + for (i = num; i > 0; i--) { + const u8 *a = list + (i - 1) * 2 * ETH_ALEN; + const u8 *m = a + ETH_ALEN; + + if (i < num) + *pos++ = ' '; + res = hwaddr_mask_txt(pos, end - pos, a, m); + if (res < 0) { + os_free(value); + return NULL; + } + pos += res; + } + + return value; +} +#endif /* NO_CONFIG_WRITE */ + static int wpa_config_parse_bssid(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) @@ -262,7 +363,7 @@ static char * wpa_config_write_bssid(const struct parse_data *data, if (value == NULL) return NULL; res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid)); - if (res < 0 || res >= 20) { + if (os_snprintf_error(20, res)) { os_free(value); return NULL; } @@ -272,13 +373,57 @@ static char * wpa_config_write_bssid(const struct parse_data *data, #endif /* NO_CONFIG_WRITE */ +static int wpa_config_parse_bssid_blacklist(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + return wpa_config_parse_addr_list(data, line, value, + &ssid->bssid_blacklist, + &ssid->num_bssid_blacklist, + "bssid_blacklist", 1, 1); +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_bssid_blacklist(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_addr_list(data, ssid->bssid_blacklist, + ssid->num_bssid_blacklist, + "bssid_blacklist"); +} +#endif /* NO_CONFIG_WRITE */ + + +static int wpa_config_parse_bssid_whitelist(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + return wpa_config_parse_addr_list(data, line, value, + &ssid->bssid_whitelist, + &ssid->num_bssid_whitelist, + "bssid_whitelist", 1, 1); +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_bssid_whitelist(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_addr_list(data, ssid->bssid_whitelist, + ssid->num_bssid_whitelist, + "bssid_whitelist"); +} +#endif /* NO_CONFIG_WRITE */ + + static int wpa_config_parse_psk(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) { #ifdef CONFIG_EXT_PASSWORD if (os_strncmp(value, "ext:", 4) == 0) { - os_free(ssid->passphrase); + str_clear_free(ssid->passphrase); ssid->passphrase = NULL; ssid->psk_set = 0; os_free(ssid->ext_psk); @@ -314,12 +459,10 @@ static int wpa_config_parse_psk(const struct parse_data *data, os_memcmp(ssid->passphrase, value, len) == 0) return 0; ssid->psk_set = 0; - os_free(ssid->passphrase); - ssid->passphrase = os_malloc(len + 1); + str_clear_free(ssid->passphrase); + ssid->passphrase = dup_binstr(value, len); if (ssid->passphrase == NULL) return -1; - os_memcpy(ssid->passphrase, value, len); - ssid->passphrase[len] = '\0'; return 0; #else /* CONFIG_NO_PBKDF2 */ wpa_printf(MSG_ERROR, "Line %d: ASCII passphrase not " @@ -335,7 +478,7 @@ static int wpa_config_parse_psk(const struct parse_data *data, return -1; } - os_free(ssid->passphrase); + str_clear_free(ssid->passphrase); ssid->passphrase = NULL; ssid->psk_set = 1; @@ -352,9 +495,15 @@ static char * wpa_config_write_psk(const struct parse_data *data, if (ssid->ext_psk) { size_t len = 4 + os_strlen(ssid->ext_psk) + 1; char *buf = os_malloc(len); + int res; + if (buf == NULL) return NULL; - os_snprintf(buf, len, "ext:%s", ssid->ext_psk); + res = os_snprintf(buf, len, "ext:%s", ssid->ext_psk); + if (os_snprintf_error(len, res)) { + os_free(buf); + buf = NULL; + } return buf; } #endif /* CONFIG_EXT_PASSWORD */ @@ -399,6 +548,8 @@ static int wpa_config_parse_proto(const struct parse_data *data, else if (os_strcmp(start, "RSN") == 0 || os_strcmp(start, "WPA2") == 0) val |= WPA_PROTO_RSN; + else if (os_strcmp(start, "OSEN") == 0) + val |= WPA_PROTO_OSEN; else { wpa_printf(MSG_ERROR, "Line %d: invalid proto '%s'", line, start); @@ -427,28 +578,41 @@ static int wpa_config_parse_proto(const struct parse_data *data, static char * wpa_config_write_proto(const struct parse_data *data, struct wpa_ssid *ssid) { - int first = 1, ret; + int ret; char *buf, *pos, *end; - pos = buf = os_zalloc(10); + pos = buf = os_zalloc(20); if (buf == NULL) return NULL; - end = buf + 10; + end = buf + 20; if (ssid->proto & WPA_PROTO_WPA) { - ret = os_snprintf(pos, end - pos, "%sWPA", first ? "" : " "); - if (ret < 0 || ret >= end - pos) + ret = os_snprintf(pos, end - pos, "%sWPA", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) return buf; pos += ret; - first = 0; } if (ssid->proto & WPA_PROTO_RSN) { - ret = os_snprintf(pos, end - pos, "%sRSN", first ? "" : " "); - if (ret < 0 || ret >= end - pos) + ret = os_snprintf(pos, end - pos, "%sRSN", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) + return buf; + pos += ret; + } + + if (ssid->proto & WPA_PROTO_OSEN) { + ret = os_snprintf(pos, end - pos, "%sOSEN", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) return buf; pos += ret; - first = 0; + } + + if (pos == buf) { + os_free(buf); + buf = NULL; } return buf; @@ -510,6 +674,18 @@ static int wpa_config_parse_key_mgmt(const struct parse_data *data, else if (os_strcmp(start, "FT-SAE") == 0) val |= WPA_KEY_MGMT_FT_SAE; #endif /* CONFIG_SAE */ +#ifdef CONFIG_HS20 + else if (os_strcmp(start, "OSEN") == 0) + val |= WPA_KEY_MGMT_OSEN; +#endif /* CONFIG_HS20 */ +#ifdef CONFIG_SUITEB + else if (os_strcmp(start, "WPA-EAP-SUITE-B") == 0) + val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B; +#endif /* CONFIG_SUITEB */ +#ifdef CONFIG_SUITEB192 + else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0) + val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; +#endif /* CONFIG_SUITEB192 */ else { wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", line, start); @@ -541,15 +717,15 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, char *buf, *pos, *end; int ret; - pos = buf = os_zalloc(50); + pos = buf = os_zalloc(100); if (buf == NULL) return NULL; - end = buf + 50; + end = buf + 100; if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) { ret = os_snprintf(pos, end - pos, "%sWPA-PSK", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -559,7 +735,7 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X) { ret = os_snprintf(pos, end - pos, "%sWPA-EAP", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -569,7 +745,7 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) { ret = os_snprintf(pos, end - pos, "%sIEEE8021X", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -579,7 +755,7 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, if (ssid->key_mgmt & WPA_KEY_MGMT_NONE) { ret = os_snprintf(pos, end - pos, "%sNONE", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -589,7 +765,7 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, if (ssid->key_mgmt & WPA_KEY_MGMT_WPA_NONE) { ret = os_snprintf(pos, end - pos, "%sWPA-NONE", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -597,160 +773,157 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, } #ifdef CONFIG_IEEE80211R - if (ssid->key_mgmt & WPA_KEY_MGMT_FT_PSK) - pos += os_snprintf(pos, end - pos, "%sFT-PSK", - pos == buf ? "" : " "); + if (ssid->key_mgmt & WPA_KEY_MGMT_FT_PSK) { + ret = os_snprintf(pos, end - pos, "%sFT-PSK", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } - if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) - pos += os_snprintf(pos, end - pos, "%sFT-EAP", - pos == buf ? "" : " "); + if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { + ret = os_snprintf(pos, end - pos, "%sFT-EAP", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W - if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) - pos += os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256", - pos == buf ? "" : " "); - - if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) - pos += os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256", - pos == buf ? "" : " "); -#endif /* CONFIG_IEEE80211W */ - -#ifdef CONFIG_WPS - if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) - pos += os_snprintf(pos, end - pos, "%sWPS", - pos == buf ? "" : " "); -#endif /* CONFIG_WPS */ - - return buf; -} -#endif /* NO_CONFIG_WRITE */ - - -static int wpa_config_parse_cipher(int line, const char *value) -{ - int val = 0, last; - char *start, *end, *buf; - - buf = os_strdup(value); - if (buf == NULL) - return -1; - start = buf; - - while (*start != '\0') { - while (*start == ' ' || *start == '\t') - start++; - if (*start == '\0') - break; - end = start; - while (*end != ' ' && *end != '\t' && *end != '\0') - end++; - last = *end == '\0'; - *end = '\0'; - if (os_strcmp(start, "CCMP") == 0) - val |= WPA_CIPHER_CCMP; - else if (os_strcmp(start, "GCMP") == 0) - val |= WPA_CIPHER_GCMP; - else if (os_strcmp(start, "TKIP") == 0) - val |= WPA_CIPHER_TKIP; - else if (os_strcmp(start, "WEP104") == 0) - val |= WPA_CIPHER_WEP104; - else if (os_strcmp(start, "WEP40") == 0) - val |= WPA_CIPHER_WEP40; - else if (os_strcmp(start, "NONE") == 0) - val |= WPA_CIPHER_NONE; - else { - wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.", - line, start); - os_free(buf); - return -1; + if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { + ret = os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return buf; } - - if (last) - break; - start = end + 1; + pos += ret; } - os_free(buf); - if (val == 0) { - wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.", - line); - return -1; + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { + ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return buf; + } + pos += ret; } - return val; -} - - -#ifndef NO_CONFIG_WRITE -static char * wpa_config_write_cipher(int cipher) -{ - char *buf, *pos, *end; - int ret; - - pos = buf = os_zalloc(50); - if (buf == NULL) - return NULL; - end = buf + 50; +#endif /* CONFIG_IEEE80211W */ - if (cipher & WPA_CIPHER_CCMP) { - ret = os_snprintf(pos, end - pos, "%sCCMP", +#ifdef CONFIG_WPS + if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { + ret = os_snprintf(pos, end - pos, "%sWPS", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } pos += ret; } +#endif /* CONFIG_WPS */ - if (cipher & WPA_CIPHER_GCMP) { - ret = os_snprintf(pos, end - pos, "%sGCMP", +#ifdef CONFIG_SAE + if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { + ret = os_snprintf(pos, end - pos, "%sSAE", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } pos += ret; } - if (cipher & WPA_CIPHER_TKIP) { - ret = os_snprintf(pos, end - pos, "%sTKIP", + if (ssid->key_mgmt & WPA_KEY_MGMT_FT_SAE) { + ret = os_snprintf(pos, end - pos, "%sFT-SAE", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } pos += ret; } +#endif /* CONFIG_SAE */ - if (cipher & WPA_CIPHER_WEP104) { - ret = os_snprintf(pos, end - pos, "%sWEP104", +#ifdef CONFIG_HS20 + if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN) { + ret = os_snprintf(pos, end - pos, "%sOSEN", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } pos += ret; } +#endif /* CONFIG_HS20 */ - if (cipher & WPA_CIPHER_WEP40) { - ret = os_snprintf(pos, end - pos, "%sWEP40", +#ifdef CONFIG_SUITEB + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { + ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SUITE-B", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } pos += ret; } +#endif /* CONFIG_SUITEB */ - if (cipher & WPA_CIPHER_NONE) { - ret = os_snprintf(pos, end - pos, "%sNONE", +#ifdef CONFIG_SUITEB192 + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { + ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SUITE-B-192", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } pos += ret; } +#endif /* CONFIG_SUITEB192 */ + + if (pos == buf) { + os_free(buf); + buf = NULL; + } + + return buf; +} +#endif /* NO_CONFIG_WRITE */ + + +static int wpa_config_parse_cipher(int line, const char *value) +{ + int val = wpa_parse_cipher(value); + if (val < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.", + line, value); + return -1; + } + if (val == 0) { + wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.", + line); + return -1; + } + return val; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_cipher(int cipher) +{ + char *buf = os_zalloc(50); + if (buf == NULL) + return NULL; + + if (wpa_write_ciphers(buf, buf + 50, cipher, " ") < 0) { + os_free(buf); + return NULL; + } return buf; } @@ -765,8 +938,7 @@ static int wpa_config_parse_pairwise(const struct parse_data *data, val = wpa_config_parse_cipher(line, value); if (val == -1) return -1; - if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | - WPA_CIPHER_NONE)) { + if (val & ~WPA_ALLOWED_PAIRWISE_CIPHERS) { wpa_printf(MSG_ERROR, "Line %d: not allowed pairwise cipher " "(0x%x).", line, val); return -1; @@ -795,8 +967,7 @@ static int wpa_config_parse_group(const struct parse_data *data, val = wpa_config_parse_cipher(line, value); if (val == -1) return -1; - if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | - WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) { + if (val & ~WPA_ALLOWED_GROUP_CIPHERS) { wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher " "(0x%x).", line, val); return -1; @@ -884,7 +1055,7 @@ static char * wpa_config_write_auth_alg(const struct parse_data *data, if (ssid->auth_alg & WPA_AUTH_ALG_OPEN) { ret = os_snprintf(pos, end - pos, "%sOPEN", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -894,7 +1065,7 @@ static char * wpa_config_write_auth_alg(const struct parse_data *data, if (ssid->auth_alg & WPA_AUTH_ALG_SHARED) { ret = os_snprintf(pos, end - pos, "%sSHARED", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -904,21 +1075,24 @@ static char * wpa_config_write_auth_alg(const struct parse_data *data, if (ssid->auth_alg & WPA_AUTH_ALG_LEAP) { ret = os_snprintf(pos, end - pos, "%sLEAP", pos == buf ? "" : " "); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } pos += ret; } + if (pos == buf) { + os_free(buf); + buf = NULL; + } + return buf; } #endif /* NO_CONFIG_WRITE */ -static int * wpa_config_parse_freqs(const struct parse_data *data, - struct wpa_ssid *ssid, int line, - const char *value) +static int * wpa_config_parse_int_array(const char *value) { int *freqs; size_t used, len; @@ -965,9 +1139,13 @@ static int wpa_config_parse_scan_freq(const struct parse_data *data, { int *freqs; - freqs = wpa_config_parse_freqs(data, ssid, line, value); + freqs = wpa_config_parse_int_array(value); if (freqs == NULL) return -1; + if (freqs[0] == 0) { + os_free(freqs); + freqs = NULL; + } os_free(ssid->scan_freq); ssid->scan_freq = freqs; @@ -981,9 +1159,13 @@ static int wpa_config_parse_freq_list(const struct parse_data *data, { int *freqs; - freqs = wpa_config_parse_freqs(data, ssid, line, value); + freqs = wpa_config_parse_int_array(value); if (freqs == NULL) return -1; + if (freqs[0] == 0) { + os_free(freqs); + freqs = NULL; + } os_free(ssid->freq_list); ssid->freq_list = freqs; @@ -1014,7 +1196,7 @@ static char * wpa_config_write_freqs(const struct parse_data *data, for (i = 0; freqs[i]; i++) { ret = os_snprintf(pos, end - pos, "%s%u", i == 0 ? "" : " ", freqs[i]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { end[-1] = '\0'; return buf; } @@ -1137,7 +1319,7 @@ static char * wpa_config_write_eap(const struct parse_data *data, if (name) { ret = os_snprintf(pos, end - pos, "%s%s", pos == buf ? "" : " ", name); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) break; pos += ret; } @@ -1157,7 +1339,7 @@ static int wpa_config_parse_password(const struct parse_data *data, if (os_strcmp(value, "NULL") == 0) { wpa_printf(MSG_DEBUG, "Unset configuration string 'password'"); - os_free(ssid->eap.password); + bin_clear_free(ssid->eap.password, ssid->eap.password_len); ssid->eap.password = NULL; ssid->eap.password_len = 0; return 0; @@ -1168,7 +1350,7 @@ static int wpa_config_parse_password(const struct parse_data *data, char *name = os_strdup(value + 4); if (name == NULL) return -1; - os_free(ssid->eap.password); + bin_clear_free(ssid->eap.password, ssid->eap.password_len); ssid->eap.password = (u8 *) name; ssid->eap.password_len = os_strlen(name); ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH; @@ -1190,7 +1372,7 @@ static int wpa_config_parse_password(const struct parse_data *data, wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name, (u8 *) tmp, res_len); - os_free(ssid->eap.password); + bin_clear_free(ssid->eap.password, ssid->eap.password_len); ssid->eap.password = (u8 *) tmp; ssid->eap.password_len = res_len; ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH; @@ -1219,7 +1401,7 @@ static int wpa_config_parse_password(const struct parse_data *data, wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16); - os_free(ssid->eap.password); + bin_clear_free(ssid->eap.password, ssid->eap.password_len); ssid->eap.password = hash; ssid->eap.password_len = 16; ssid->eap.flags |= EAP_CONFIG_FLAGS_PASSWORD_NTHASH; @@ -1289,9 +1471,9 @@ static int wpa_config_parse_wep_key(u8 *key, size_t *len, int line, line, (unsigned int) *len); } os_memcpy(key, buf, *len); - os_free(buf); + str_clear_free(buf); res = os_snprintf(title, sizeof(title), "wep_key%d", idx); - if (res >= 0 && (size_t) res < sizeof(title)) + if (!os_snprintf_error(sizeof(title), res)) wpa_hexdump_key(MSG_MSGDUMP, title, key, *len); return 0; } @@ -1378,57 +1560,60 @@ static char * wpa_config_write_wep_key3(const struct parse_data *data, #ifdef CONFIG_P2P -static int wpa_config_parse_p2p_client_list(const struct parse_data *data, +static int wpa_config_parse_go_p2p_dev_addr(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; + if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 || + os_strcmp(value, "any") == 0) { + os_memset(ssid->go_p2p_dev_addr, 0, ETH_ALEN); + wpa_printf(MSG_MSGDUMP, "GO P2P Device Address any"); + return 0; + } + if (hwaddr_aton(value, ssid->go_p2p_dev_addr)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid GO P2P Device Address '%s'.", + line, value); + return -1; + } + ssid->bssid_set = 1; + wpa_printf(MSG_MSGDUMP, "GO P2P Device Address " MACSTR, + MAC2STR(ssid->go_p2p_dev_addr)); + return 0; +} - buf = NULL; - count = 0; - pos = value; - while (pos && *pos) { - while (*pos == ' ') - pos++; +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_go_p2p_dev_addr(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + char *value; + int res; - if (hwaddr_aton(pos, addr)) { - if (count == 0) { - wpa_printf(MSG_ERROR, "Line %d: Invalid " - "p2p_client_list address '%s'.", - line, value); - os_free(buf); - return -1; - } - /* continue anyway since this could have been from a - * truncated configuration file line */ - wpa_printf(MSG_INFO, "Line %d: Ignore likely " - "truncated p2p_client_list address '%s'", - line, pos); - } else { - n = os_realloc_array(buf, count + 1, ETH_ALEN); - if (n == NULL) { - os_free(buf); - return -1; - } - buf = n; - os_memmove(buf + ETH_ALEN, buf, count * ETH_ALEN); - os_memcpy(buf, addr, ETH_ALEN); - count++; - wpa_hexdump(MSG_MSGDUMP, "p2p_client_list", - addr, ETH_ALEN); - } + if (is_zero_ether_addr(ssid->go_p2p_dev_addr)) + return NULL; - pos = os_strchr(pos, ' '); + value = os_malloc(20); + if (value == NULL) + return NULL; + res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->go_p2p_dev_addr)); + if (os_snprintf_error(20, res)) { + os_free(value); + return NULL; } + value[20 - 1] = '\0'; + return value; +} +#endif /* NO_CONFIG_WRITE */ - os_free(ssid->p2p_client_list); - ssid->p2p_client_list = buf; - ssid->num_p2p_clients = count; - return 0; +static int wpa_config_parse_p2p_client_list(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + return wpa_config_parse_addr_list(data, line, value, + &ssid->p2p_client_list, + &ssid->num_p2p_clients, + "p2p_client_list", 0, 0); } @@ -1436,39 +1621,107 @@ static int wpa_config_parse_p2p_client_list(const struct parse_data *data, 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; + return wpa_config_write_addr_list(data, ssid->p2p_client_list, + ssid->num_p2p_clients, + "p2p_client_list"); +} +#endif /* NO_CONFIG_WRITE */ - 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; +static int wpa_config_parse_psk_list(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + struct psk_list_entry *p; + const char *pos; + + p = os_zalloc(sizeof(*p)); + if (p == NULL) + return -1; + pos = value; - end = value + 20 * ssid->num_p2p_clients; + if (os_strncmp(pos, "P2P-", 4) == 0) { + p->p2p = 1; + pos += 4; + } - for (i = ssid->num_p2p_clients; i > 0; i--) { - res = os_snprintf(pos, end - pos, MACSTR " ", - MAC2STR(ssid->p2p_client_list + - (i - 1) * ETH_ALEN)); - if (res < 0 || res >= end - pos) { - os_free(value); - return NULL; - } - pos += res; + if (hwaddr_aton(pos, p->addr)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list address '%s'", + line, pos); + os_free(p); + return -1; + } + pos += 17; + if (*pos != '-') { + wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list '%s'", + line, pos); + os_free(p); + return -1; } + pos++; - if (pos > value) - pos[-1] = '\0'; + if (hexstr2bin(pos, p->psk, PMK_LEN) || pos[PMK_LEN * 2] != '\0') { + wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list PSK '%s'", + line, pos); + os_free(p); + return -1; + } - return value; + dl_list_add(&ssid->psk_list, &p->list); + + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_psk_list(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return NULL; } #endif /* NO_CONFIG_WRITE */ #endif /* CONFIG_P2P */ + +#ifdef CONFIG_MESH + +static int wpa_config_parse_mesh_basic_rates(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int *rates = wpa_config_parse_int_array(value); + + if (rates == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid mesh_basic_rates '%s'", + line, value); + return -1; + } + if (rates[0] == 0) { + os_free(rates); + rates = NULL; + } + + os_free(ssid->mesh_basic_rates); + ssid->mesh_basic_rates = rates; + + return 0; +} + + +#ifndef NO_CONFIG_WRITE + +static char * wpa_config_write_mesh_basic_rates(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_freqs(data, ssid->mesh_basic_rates); +} + +#endif /* NO_CONFIG_WRITE */ + +#endif /* CONFIG_MESH */ + + /* Helper macros for network block parser */ #ifdef OFFSET @@ -1560,6 +1813,8 @@ static const struct parse_data ssid_fields[] = { { STR_RANGE(ssid, 0, MAX_SSID_LEN) }, { INT_RANGE(scan_ssid, 0, 1) }, { FUNC(bssid) }, + { FUNC(bssid_blacklist) }, + { FUNC(bssid_whitelist) }, { FUNC_KEY(psk) }, { FUNC(proto) }, { FUNC(key_mgmt) }, @@ -1582,6 +1837,8 @@ static const struct parse_data ssid_fields[] = { { STRe(dh_file) }, { STRe(subject_match) }, { STRe(altsubject_match) }, + { STRe(domain_suffix_match) }, + { STRe(domain_match) }, { STRe(ca_cert2) }, { STRe(ca_path2) }, { STRe(client_cert2) }, @@ -1590,6 +1847,8 @@ static const struct parse_data ssid_fields[] = { { STRe(dh_file2) }, { STRe(subject_match2) }, { STRe(altsubject_match2) }, + { STRe(domain_suffix_match2) }, + { STRe(domain_match2) }, { STRe(phase1) }, { STRe(phase2) }, { STRe(pcsc) }, @@ -1606,6 +1865,9 @@ static const struct parse_data ssid_fields[] = { { INTe(engine) }, { INTe(engine2) }, { INT(eapol_flags) }, + { INTe(sim_num) }, + { STRe(openssl_ciphers) }, + { INTe(erp) }, #endif /* IEEE8021X_EAPOL */ { FUNC_KEY(wep_key0) }, { FUNC_KEY(wep_key1) }, @@ -1617,8 +1879,14 @@ static const struct parse_data ssid_fields[] = { { INT(eap_workaround) }, { STRe(pac_file) }, { INTe(fragment_size) }, + { INTe(ocsp) }, #endif /* IEEE8021X_EAPOL */ +#ifdef CONFIG_MESH + { INT_RANGE(mode, 0, 5) }, + { INT_RANGE(no_auto_peer, 0, 1) }, +#else /* CONFIG_MESH */ { INT_RANGE(mode, 0, 4) }, +#endif /* CONFIG_MESH */ { INT_RANGE(proactive_key_caching, 0, 1) }, { INT_RANGE(disabled, 0, 2) }, { STR(id_str) }, @@ -1628,23 +1896,64 @@ static const struct parse_data ssid_fields[] = { { INT_RANGE(peerkey, 0, 1) }, { INT_RANGE(mixed_cell, 0, 1) }, { INT_RANGE(frequency, 0, 65000) }, + { INT_RANGE(fixed_freq, 0, 1) }, +#ifdef CONFIG_MESH + { FUNC(mesh_basic_rates) }, + { INT(dot11MeshMaxRetries) }, + { INT(dot11MeshRetryTimeout) }, + { INT(dot11MeshConfirmTimeout) }, + { INT(dot11MeshHoldingTimeout) }, +#endif /* CONFIG_MESH */ { INT(wpa_ptk_rekey) }, { STR(bgscan) }, { INT_RANGE(ignore_broadcast_ssid, 0, 2) }, #ifdef CONFIG_P2P + { FUNC(go_p2p_dev_addr) }, { FUNC(p2p_client_list) }, + { FUNC(psk_list) }, #endif /* CONFIG_P2P */ #ifdef CONFIG_HT_OVERRIDES { INT_RANGE(disable_ht, 0, 1) }, { INT_RANGE(disable_ht40, -1, 1) }, { INT_RANGE(disable_sgi, 0, 1) }, + { INT_RANGE(disable_ldpc, 0, 1) }, + { INT_RANGE(ht40_intolerant, 0, 1) }, { INT_RANGE(disable_max_amsdu, -1, 1) }, { INT_RANGE(ampdu_factor, -1, 3) }, { INT_RANGE(ampdu_density, -1, 7) }, { STR(ht_mcs) }, #endif /* CONFIG_HT_OVERRIDES */ +#ifdef CONFIG_VHT_OVERRIDES + { INT_RANGE(disable_vht, 0, 1) }, + { INT(vht_capa) }, + { INT(vht_capa_mask) }, + { INT_RANGE(vht_rx_mcs_nss_1, -1, 3) }, + { INT_RANGE(vht_rx_mcs_nss_2, -1, 3) }, + { INT_RANGE(vht_rx_mcs_nss_3, -1, 3) }, + { INT_RANGE(vht_rx_mcs_nss_4, -1, 3) }, + { INT_RANGE(vht_rx_mcs_nss_5, -1, 3) }, + { INT_RANGE(vht_rx_mcs_nss_6, -1, 3) }, + { INT_RANGE(vht_rx_mcs_nss_7, -1, 3) }, + { INT_RANGE(vht_rx_mcs_nss_8, -1, 3) }, + { INT_RANGE(vht_tx_mcs_nss_1, -1, 3) }, + { INT_RANGE(vht_tx_mcs_nss_2, -1, 3) }, + { INT_RANGE(vht_tx_mcs_nss_3, -1, 3) }, + { INT_RANGE(vht_tx_mcs_nss_4, -1, 3) }, + { INT_RANGE(vht_tx_mcs_nss_5, -1, 3) }, + { INT_RANGE(vht_tx_mcs_nss_6, -1, 3) }, + { INT_RANGE(vht_tx_mcs_nss_7, -1, 3) }, + { INT_RANGE(vht_tx_mcs_nss_8, -1, 3) }, +#endif /* CONFIG_VHT_OVERRIDES */ { INT(ap_max_inactivity) }, { INT(dtim_period) }, + { INT(beacon_int) }, +#ifdef CONFIG_MACSEC + { INT_RANGE(macsec_policy, 0, 1) }, +#endif /* CONFIG_MACSEC */ +#ifdef CONFIG_HS20 + { INT(update_identifier) }, +#endif /* CONFIG_HS20 */ + { INT_RANGE(mac_addr, 0, 2) }, }; #undef OFFSET @@ -1663,7 +1972,7 @@ static const struct parse_data ssid_fields[] = { #undef _FUNC #undef FUNC #undef FUNC_KEY -#define NUM_SSID_FIELDS (sizeof(ssid_fields) / sizeof(ssid_fields[0])) +#define NUM_SSID_FIELDS ARRAY_SIZE(ssid_fields) /** @@ -1754,29 +2063,33 @@ int wpa_config_update_prio_list(struct wpa_config *config) static void eap_peer_config_free(struct eap_peer_config *eap) { os_free(eap->eap_methods); - os_free(eap->identity); + bin_clear_free(eap->identity, eap->identity_len); os_free(eap->anonymous_identity); - os_free(eap->password); + bin_clear_free(eap->password, eap->password_len); os_free(eap->ca_cert); os_free(eap->ca_path); os_free(eap->client_cert); os_free(eap->private_key); - os_free(eap->private_key_passwd); + str_clear_free(eap->private_key_passwd); os_free(eap->dh_file); os_free(eap->subject_match); os_free(eap->altsubject_match); + os_free(eap->domain_suffix_match); + os_free(eap->domain_match); os_free(eap->ca_cert2); os_free(eap->ca_path2); os_free(eap->client_cert2); os_free(eap->private_key2); - os_free(eap->private_key2_passwd); + str_clear_free(eap->private_key2_passwd); os_free(eap->dh_file2); os_free(eap->subject_match2); os_free(eap->altsubject_match2); + os_free(eap->domain_suffix_match2); + os_free(eap->domain_match2); os_free(eap->phase1); os_free(eap->phase2); os_free(eap->pcsc); - os_free(eap->pin); + str_clear_free(eap->pin); os_free(eap->engine_id); os_free(eap->key_id); os_free(eap->cert_id); @@ -1784,12 +2097,14 @@ static void eap_peer_config_free(struct eap_peer_config *eap) os_free(eap->key2_id); os_free(eap->cert2_id); os_free(eap->ca_cert2_id); - os_free(eap->pin2); + str_clear_free(eap->pin2); os_free(eap->engine2_id); os_free(eap->otp); os_free(eap->pending_req_otp); os_free(eap->pac_file); - os_free(eap->new_password); + bin_clear_free(eap->new_password, eap->new_password_len); + str_clear_free(eap->external_sim_resp); + os_free(eap->openssl_ciphers); } #endif /* IEEE8021X_EAPOL */ @@ -1803,8 +2118,10 @@ static void eap_peer_config_free(struct eap_peer_config *eap) */ void wpa_config_free_ssid(struct wpa_ssid *ssid) { + struct psk_list_entry *psk; + os_free(ssid->ssid); - os_free(ssid->passphrase); + str_clear_free(ssid->passphrase); os_free(ssid->ext_psk); #ifdef IEEE8021X_EAPOL eap_peer_config_free(&ssid->eap); @@ -1814,33 +2131,70 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid) os_free(ssid->freq_list); os_free(ssid->bgscan); os_free(ssid->p2p_client_list); + os_free(ssid->bssid_blacklist); + os_free(ssid->bssid_whitelist); #ifdef CONFIG_HT_OVERRIDES os_free(ssid->ht_mcs); #endif /* CONFIG_HT_OVERRIDES */ - os_free(ssid); +#ifdef CONFIG_MESH + os_free(ssid->mesh_basic_rates); +#endif /* CONFIG_MESH */ + while ((psk = dl_list_first(&ssid->psk_list, struct psk_list_entry, + list))) { + dl_list_del(&psk->list); + bin_clear_free(psk, sizeof(*psk)); + } + bin_clear_free(ssid, sizeof(*ssid)); } void wpa_config_free_cred(struct wpa_cred *cred) { + size_t i; + os_free(cred->realm); - os_free(cred->username); - os_free(cred->password); + str_clear_free(cred->username); + str_clear_free(cred->password); os_free(cred->ca_cert); os_free(cred->client_cert); os_free(cred->private_key); - os_free(cred->private_key_passwd); + str_clear_free(cred->private_key_passwd); os_free(cred->imsi); - os_free(cred->milenage); + str_clear_free(cred->milenage); + for (i = 0; i < cred->num_domain; i++) + os_free(cred->domain[i]); os_free(cred->domain); + os_free(cred->domain_suffix_match); os_free(cred->eap_method); os_free(cred->phase1); os_free(cred->phase2); os_free(cred->excluded_ssid); + os_free(cred->roaming_partner); + os_free(cred->provisioning_sp); + for (i = 0; i < cred->num_req_conn_capab; i++) + os_free(cred->req_conn_capab_port[i]); + os_free(cred->req_conn_capab_port); + os_free(cred->req_conn_capab_proto); os_free(cred); } +void wpa_config_flush_blobs(struct wpa_config *config) +{ +#ifndef CONFIG_NO_CONFIG_BLOBS + struct wpa_config_blob *blob, *prev; + + blob = config->blobs; + config->blobs = NULL; + while (blob) { + prev = blob; + blob = blob->next; + wpa_config_free_blob(prev); + } +#endif /* CONFIG_NO_CONFIG_BLOBS */ +} + + /** * wpa_config_free - Free configuration data * @config: Configuration data from wpa_config_read() @@ -1850,11 +2204,9 @@ void wpa_config_free_cred(struct wpa_cred *cred) */ void wpa_config_free(struct wpa_config *config) { -#ifndef CONFIG_NO_CONFIG_BLOBS - struct wpa_config_blob *blob, *prevblob; -#endif /* CONFIG_NO_CONFIG_BLOBS */ struct wpa_ssid *ssid, *prev = NULL; struct wpa_cred *cred, *cprev; + int i; ssid = config->ssid; while (ssid) { @@ -1870,24 +2222,19 @@ void wpa_config_free(struct wpa_config *config) wpa_config_free_cred(cprev); } -#ifndef CONFIG_NO_CONFIG_BLOBS - blob = config->blobs; - prevblob = NULL; - while (blob) { - prevblob = blob; - blob = blob->next; - wpa_config_free_blob(prevblob); - } -#endif /* CONFIG_NO_CONFIG_BLOBS */ + wpa_config_flush_blobs(config); wpabuf_free(config->wps_vendor_ext_m1); + for (i = 0; i < MAX_WPS_VENDOR_EXT; i++) + wpabuf_free(config->wps_vendor_ext[i]); os_free(config->ctrl_interface); os_free(config->ctrl_interface_group); os_free(config->opensc_engine_path); os_free(config->pkcs11_engine_path); os_free(config->pkcs11_module_path); + os_free(config->openssl_ciphers); os_free(config->pcsc_reader); - os_free(config->pcsc_pin); + str_clear_free(config->pcsc_pin); os_free(config->driver_param); os_free(config->device_name); os_free(config->manufacturer); @@ -1898,11 +2245,18 @@ void wpa_config_free(struct wpa_config *config) os_free(config->p2p_ssid_postfix); os_free(config->pssid); os_free(config->p2p_pref_chan); + os_free(config->p2p_no_go_freq.range); os_free(config->autoscan); + os_free(config->freq_list); wpabuf_free(config->wps_nfc_dh_pubkey); wpabuf_free(config->wps_nfc_dh_privkey); wpabuf_free(config->wps_nfc_dev_pw); os_free(config->ext_password_backend); + os_free(config->sae_groups); + wpabuf_free(config->ap_vendor_elements); + os_free(config->osu_dir); + os_free(config->bgscan); + os_free(config->wowlan_triggers); os_free(config); } @@ -1977,6 +2331,7 @@ struct wpa_ssid * wpa_config_add_network(struct wpa_config *config) if (ssid == NULL) return NULL; ssid->id = id; + dl_list_init(&ssid->psk_list); if (last) last->next = ssid; else @@ -2035,19 +2390,46 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid) ssid->eapol_flags = DEFAULT_EAPOL_FLAGS; ssid->eap_workaround = DEFAULT_EAP_WORKAROUND; ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE; + ssid->eap.sim_num = DEFAULT_USER_SELECTED_SIM; #endif /* IEEE8021X_EAPOL */ +#ifdef CONFIG_MESH + ssid->dot11MeshMaxRetries = DEFAULT_MESH_MAX_RETRIES; + ssid->dot11MeshRetryTimeout = DEFAULT_MESH_RETRY_TIMEOUT; + ssid->dot11MeshConfirmTimeout = DEFAULT_MESH_CONFIRM_TIMEOUT; + ssid->dot11MeshHoldingTimeout = DEFAULT_MESH_HOLDING_TIMEOUT; +#endif /* CONFIG_MESH */ #ifdef CONFIG_HT_OVERRIDES ssid->disable_ht = DEFAULT_DISABLE_HT; ssid->disable_ht40 = DEFAULT_DISABLE_HT40; ssid->disable_sgi = DEFAULT_DISABLE_SGI; + ssid->disable_ldpc = DEFAULT_DISABLE_LDPC; ssid->disable_max_amsdu = DEFAULT_DISABLE_MAX_AMSDU; ssid->ampdu_factor = DEFAULT_AMPDU_FACTOR; ssid->ampdu_density = DEFAULT_AMPDU_DENSITY; #endif /* CONFIG_HT_OVERRIDES */ +#ifdef CONFIG_VHT_OVERRIDES + ssid->vht_rx_mcs_nss_1 = -1; + ssid->vht_rx_mcs_nss_2 = -1; + ssid->vht_rx_mcs_nss_3 = -1; + ssid->vht_rx_mcs_nss_4 = -1; + ssid->vht_rx_mcs_nss_5 = -1; + ssid->vht_rx_mcs_nss_6 = -1; + ssid->vht_rx_mcs_nss_7 = -1; + ssid->vht_rx_mcs_nss_8 = -1; + ssid->vht_tx_mcs_nss_1 = -1; + ssid->vht_tx_mcs_nss_2 = -1; + ssid->vht_tx_mcs_nss_3 = -1; + ssid->vht_tx_mcs_nss_4 = -1; + ssid->vht_tx_mcs_nss_5 = -1; + ssid->vht_tx_mcs_nss_6 = -1; + ssid->vht_tx_mcs_nss_7 = -1; + ssid->vht_tx_mcs_nss_8 = -1; +#endif /* CONFIG_VHT_OVERRIDES */ ssid->proactive_key_caching = -1; #ifdef CONFIG_IEEE80211W ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT; #endif /* CONFIG_IEEE80211W */ + ssid->mac_addr = -1; } @@ -2244,7 +2626,7 @@ char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var) wpa_printf(MSG_DEBUG, "Do not allow " "key_data field to be " "exposed"); - os_free(res); + str_clear_free(res); return os_strdup("*"); } @@ -2279,17 +2661,93 @@ void wpa_config_update_psk(struct wpa_ssid *ssid) } +static int wpa_config_set_cred_req_conn_capab(struct wpa_cred *cred, + const char *value) +{ + u8 *proto; + int **port; + int *ports, *nports; + const char *pos; + unsigned int num_ports; + + proto = os_realloc_array(cred->req_conn_capab_proto, + cred->num_req_conn_capab + 1, sizeof(u8)); + if (proto == NULL) + return -1; + cred->req_conn_capab_proto = proto; + + port = os_realloc_array(cred->req_conn_capab_port, + cred->num_req_conn_capab + 1, sizeof(int *)); + if (port == NULL) + return -1; + cred->req_conn_capab_port = port; + + proto[cred->num_req_conn_capab] = atoi(value); + + pos = os_strchr(value, ':'); + if (pos == NULL) { + port[cred->num_req_conn_capab] = NULL; + cred->num_req_conn_capab++; + return 0; + } + pos++; + + ports = NULL; + num_ports = 0; + + while (*pos) { + nports = os_realloc_array(ports, num_ports + 1, sizeof(int)); + if (nports == NULL) { + os_free(ports); + return -1; + } + ports = nports; + ports[num_ports++] = atoi(pos); + + pos = os_strchr(pos, ','); + if (pos == NULL) + break; + pos++; + } + + nports = os_realloc_array(ports, num_ports + 1, sizeof(int)); + if (nports == NULL) { + os_free(ports); + return -1; + } + ports = nports; + ports[num_ports] = -1; + + port[cred->num_req_conn_capab] = ports; + cred->num_req_conn_capab++; + return 0; +} + + int wpa_config_set_cred(struct wpa_cred *cred, const char *var, const char *value, int line) { char *val; size_t len; + if (os_strcmp(var, "temporary") == 0) { + cred->temporary = atoi(value); + return 0; + } + if (os_strcmp(var, "priority") == 0) { cred->priority = atoi(value); return 0; } + if (os_strcmp(var, "sp_priority") == 0) { + int prio = atoi(value); + if (prio < 0 || prio > 255) + return -1; + cred->sp_priority = prio; + return 0; + } + if (os_strcmp(var, "pcsc") == 0) { cred->pcsc = atoi(value); return 0; @@ -2314,12 +2772,55 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, if (os_strcmp(var, "password") == 0 && os_strncmp(value, "ext:", 4) == 0) { - os_free(cred->password); + str_clear_free(cred->password); cred->password = os_strdup(value); cred->ext_password = 1; return 0; } + if (os_strcmp(var, "update_identifier") == 0) { + cred->update_identifier = atoi(value); + return 0; + } + + if (os_strcmp(var, "min_dl_bandwidth_home") == 0) { + cred->min_dl_bandwidth_home = atoi(value); + return 0; + } + + if (os_strcmp(var, "min_ul_bandwidth_home") == 0) { + cred->min_ul_bandwidth_home = atoi(value); + return 0; + } + + if (os_strcmp(var, "min_dl_bandwidth_roaming") == 0) { + cred->min_dl_bandwidth_roaming = atoi(value); + return 0; + } + + if (os_strcmp(var, "min_ul_bandwidth_roaming") == 0) { + cred->min_ul_bandwidth_roaming = atoi(value); + return 0; + } + + if (os_strcmp(var, "max_bss_load") == 0) { + cred->max_bss_load = atoi(value); + return 0; + } + + if (os_strcmp(var, "req_conn_capab") == 0) + return wpa_config_set_cred_req_conn_capab(cred, value); + + if (os_strcmp(var, "ocsp") == 0) { + cred->ocsp = atoi(value); + return 0; + } + + if (os_strcmp(var, "sim_num") == 0) { + cred->sim_num = atoi(value); + return 0; + } + val = wpa_config_parse_string(value, &len); if (val == NULL) { wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string " @@ -2334,13 +2835,13 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, } if (os_strcmp(var, "username") == 0) { - os_free(cred->username); + str_clear_free(cred->username); cred->username = val; return 0; } if (os_strcmp(var, "password") == 0) { - os_free(cred->password); + str_clear_free(cred->password); cred->password = val; cred->ext_password = 0; return 0; @@ -2365,7 +2866,7 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, } if (os_strcmp(var, "private_key_passwd") == 0) { - os_free(cred->private_key_passwd); + str_clear_free(cred->private_key_passwd); cred->private_key_passwd = val; return 0; } @@ -2377,14 +2878,28 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, } if (os_strcmp(var, "milenage") == 0) { - os_free(cred->milenage); + str_clear_free(cred->milenage); cred->milenage = val; return 0; } + if (os_strcmp(var, "domain_suffix_match") == 0) { + os_free(cred->domain_suffix_match); + cred->domain_suffix_match = val; + return 0; + } + if (os_strcmp(var, "domain") == 0) { - os_free(cred->domain); - cred->domain = val; + char **new_domain; + new_domain = os_realloc_array(cred->domain, + cred->num_domain + 1, + sizeof(char *)); + if (new_domain == NULL) { + os_free(val); + return -1; + } + new_domain[cred->num_domain++] = val; + cred->domain = new_domain; return 0; } @@ -2414,6 +2929,21 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, return 0; } + if (os_strcmp(var, "required_roaming_consortium") == 0) { + if (len < 3 || len > sizeof(cred->required_roaming_consortium)) + { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "required_roaming_consortium length %d " + "(3..15 expected)", line, (int) len); + os_free(val); + return -1; + } + os_memcpy(cred->required_roaming_consortium, val, len); + cred->required_roaming_consortium_len = len; + os_free(val); + return 0; + } + if (os_strcmp(var, "excluded_ssid") == 0) { struct excluded_ssid *e; @@ -2442,6 +2972,69 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, return 0; } + if (os_strcmp(var, "roaming_partner") == 0) { + struct roaming_partner *p; + char *pos; + + p = os_realloc_array(cred->roaming_partner, + cred->num_roaming_partner + 1, + sizeof(struct roaming_partner)); + if (p == NULL) { + os_free(val); + return -1; + } + cred->roaming_partner = p; + + p = &cred->roaming_partner[cred->num_roaming_partner]; + + pos = os_strchr(val, ','); + if (pos == NULL) { + os_free(val); + return -1; + } + *pos++ = '\0'; + if (pos - val - 1 >= (int) sizeof(p->fqdn)) { + os_free(val); + return -1; + } + os_memcpy(p->fqdn, val, pos - val); + + p->exact_match = atoi(pos); + + pos = os_strchr(pos, ','); + if (pos == NULL) { + os_free(val); + return -1; + } + *pos++ = '\0'; + + p->priority = atoi(pos); + + pos = os_strchr(pos, ','); + if (pos == NULL) { + os_free(val); + return -1; + } + *pos++ = '\0'; + + if (os_strlen(pos) >= sizeof(p->country)) { + os_free(val); + return -1; + } + os_memcpy(p->country, pos, os_strlen(pos) + 1); + + cred->num_roaming_partner++; + os_free(val); + + return 0; + } + + if (os_strcmp(var, "provisioning_sp") == 0) { + os_free(cred->provisioning_sp); + cred->provisioning_sp = val; + return 0; + } + if (line) { wpa_printf(MSG_ERROR, "Line %d: unknown cred field '%s'.", line, var); @@ -2453,6 +3046,281 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, } +static char * alloc_int_str(int val) +{ + const unsigned int bufsize = 20; + char *buf; + int res; + + buf = os_malloc(bufsize); + if (buf == NULL) + return NULL; + res = os_snprintf(buf, bufsize, "%d", val); + if (os_snprintf_error(bufsize, res)) { + os_free(buf); + buf = NULL; + } + return buf; +} + + +static char * alloc_strdup(const char *str) +{ + if (str == NULL) + return NULL; + return os_strdup(str); +} + + +char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var) +{ + if (os_strcmp(var, "temporary") == 0) + return alloc_int_str(cred->temporary); + + if (os_strcmp(var, "priority") == 0) + return alloc_int_str(cred->priority); + + if (os_strcmp(var, "sp_priority") == 0) + return alloc_int_str(cred->sp_priority); + + if (os_strcmp(var, "pcsc") == 0) + return alloc_int_str(cred->pcsc); + + if (os_strcmp(var, "eap") == 0) { + if (!cred->eap_method) + return NULL; + return alloc_strdup(eap_get_name(cred->eap_method[0].vendor, + cred->eap_method[0].method)); + } + + if (os_strcmp(var, "update_identifier") == 0) + return alloc_int_str(cred->update_identifier); + + if (os_strcmp(var, "min_dl_bandwidth_home") == 0) + return alloc_int_str(cred->min_dl_bandwidth_home); + + if (os_strcmp(var, "min_ul_bandwidth_home") == 0) + return alloc_int_str(cred->min_ul_bandwidth_home); + + if (os_strcmp(var, "min_dl_bandwidth_roaming") == 0) + return alloc_int_str(cred->min_dl_bandwidth_roaming); + + if (os_strcmp(var, "min_ul_bandwidth_roaming") == 0) + return alloc_int_str(cred->min_ul_bandwidth_roaming); + + if (os_strcmp(var, "max_bss_load") == 0) + return alloc_int_str(cred->max_bss_load); + + if (os_strcmp(var, "req_conn_capab") == 0) { + unsigned int i; + char *buf, *end, *pos; + int ret; + + if (!cred->num_req_conn_capab) + return NULL; + + buf = os_malloc(4000); + if (buf == NULL) + return NULL; + pos = buf; + end = pos + 4000; + for (i = 0; i < cred->num_req_conn_capab; i++) { + int *ports; + + ret = os_snprintf(pos, end - pos, "%s%u", + i > 0 ? "\n" : "", + cred->req_conn_capab_proto[i]); + if (os_snprintf_error(end - pos, ret)) + return buf; + pos += ret; + + ports = cred->req_conn_capab_port[i]; + if (ports) { + int j; + for (j = 0; ports[j] != -1; j++) { + ret = os_snprintf(pos, end - pos, + "%s%d", + j > 0 ? "," : ":", + ports[j]); + if (os_snprintf_error(end - pos, ret)) + return buf; + pos += ret; + } + } + } + + return buf; + } + + if (os_strcmp(var, "ocsp") == 0) + return alloc_int_str(cred->ocsp); + + if (os_strcmp(var, "realm") == 0) + return alloc_strdup(cred->realm); + + if (os_strcmp(var, "username") == 0) + return alloc_strdup(cred->username); + + if (os_strcmp(var, "password") == 0) { + if (!cred->password) + return NULL; + return alloc_strdup("*"); + } + + if (os_strcmp(var, "ca_cert") == 0) + return alloc_strdup(cred->ca_cert); + + if (os_strcmp(var, "client_cert") == 0) + return alloc_strdup(cred->client_cert); + + if (os_strcmp(var, "private_key") == 0) + return alloc_strdup(cred->private_key); + + if (os_strcmp(var, "private_key_passwd") == 0) { + if (!cred->private_key_passwd) + return NULL; + return alloc_strdup("*"); + } + + if (os_strcmp(var, "imsi") == 0) + return alloc_strdup(cred->imsi); + + if (os_strcmp(var, "milenage") == 0) { + if (!(cred->milenage)) + return NULL; + return alloc_strdup("*"); + } + + if (os_strcmp(var, "domain_suffix_match") == 0) + return alloc_strdup(cred->domain_suffix_match); + + if (os_strcmp(var, "domain") == 0) { + unsigned int i; + char *buf, *end, *pos; + int ret; + + if (!cred->num_domain) + return NULL; + + buf = os_malloc(4000); + if (buf == NULL) + return NULL; + pos = buf; + end = pos + 4000; + + for (i = 0; i < cred->num_domain; i++) { + ret = os_snprintf(pos, end - pos, "%s%s", + i > 0 ? "\n" : "", cred->domain[i]); + if (os_snprintf_error(end - pos, ret)) + return buf; + pos += ret; + } + + return buf; + } + + if (os_strcmp(var, "phase1") == 0) + return alloc_strdup(cred->phase1); + + if (os_strcmp(var, "phase2") == 0) + return alloc_strdup(cred->phase2); + + if (os_strcmp(var, "roaming_consortium") == 0) { + size_t buflen; + char *buf; + + if (!cred->roaming_consortium_len) + return NULL; + buflen = cred->roaming_consortium_len * 2 + 1; + buf = os_malloc(buflen); + if (buf == NULL) + return NULL; + wpa_snprintf_hex(buf, buflen, cred->roaming_consortium, + cred->roaming_consortium_len); + return buf; + } + + if (os_strcmp(var, "required_roaming_consortium") == 0) { + size_t buflen; + char *buf; + + if (!cred->required_roaming_consortium_len) + return NULL; + buflen = cred->required_roaming_consortium_len * 2 + 1; + buf = os_malloc(buflen); + if (buf == NULL) + return NULL; + wpa_snprintf_hex(buf, buflen, cred->required_roaming_consortium, + cred->required_roaming_consortium_len); + return buf; + } + + if (os_strcmp(var, "excluded_ssid") == 0) { + unsigned int i; + char *buf, *end, *pos; + + if (!cred->num_excluded_ssid) + return NULL; + + buf = os_malloc(4000); + if (buf == NULL) + return NULL; + pos = buf; + end = pos + 4000; + + for (i = 0; i < cred->num_excluded_ssid; i++) { + struct excluded_ssid *e; + int ret; + + e = &cred->excluded_ssid[i]; + ret = os_snprintf(pos, end - pos, "%s%s", + i > 0 ? "\n" : "", + wpa_ssid_txt(e->ssid, e->ssid_len)); + if (os_snprintf_error(end - pos, ret)) + return buf; + pos += ret; + } + + return buf; + } + + if (os_strcmp(var, "roaming_partner") == 0) { + unsigned int i; + char *buf, *end, *pos; + + if (!cred->num_roaming_partner) + return NULL; + + buf = os_malloc(4000); + if (buf == NULL) + return NULL; + pos = buf; + end = pos + 4000; + + for (i = 0; i < cred->num_roaming_partner; i++) { + struct roaming_partner *p; + int ret; + + p = &cred->roaming_partner[i]; + ret = os_snprintf(pos, end - pos, "%s%s,%d,%u,%s", + i > 0 ? "\n" : "", + p->fqdn, p->exact_match, p->priority, + p->country); + if (os_snprintf_error(end - pos, ret)) + return buf; + pos += ret; + } + + return buf; + } + + if (os_strcmp(var, "provisioning_sp") == 0) + return alloc_strdup(cred->provisioning_sp); + + return NULL; +} + + struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id) { struct wpa_cred *cred; @@ -2487,6 +3355,7 @@ struct wpa_cred * wpa_config_add_cred(struct wpa_config *config) if (cred == NULL) return NULL; cred->id = id; + cred->sim_num = DEFAULT_USER_SELECTED_SIM; if (last) last->next = cred; else @@ -2567,7 +3436,7 @@ void wpa_config_free_blob(struct wpa_config_blob *blob) { if (blob) { os_free(blob->name); - os_free(blob->data); + bin_clear_free(blob->data, blob->len); os_free(blob); } } @@ -2627,19 +3496,29 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, return NULL; config->eapol_version = DEFAULT_EAPOL_VERSION; config->ap_scan = DEFAULT_AP_SCAN; + config->user_mpm = DEFAULT_USER_MPM; + config->max_peer_links = DEFAULT_MAX_PEER_LINKS; + config->mesh_max_inactivity = DEFAULT_MESH_MAX_INACTIVITY; config->fast_reauth = DEFAULT_FAST_REAUTH; config->p2p_go_intent = DEFAULT_P2P_GO_INTENT; config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS; config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY; + config->p2p_optimize_listen_chan = DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN; + config->p2p_go_ctwindow = DEFAULT_P2P_GO_CTWINDOW; 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; + config->scan_cur_freq = DEFAULT_SCAN_CUR_FREQ; config->wmm_ac_params[0] = ac_be; config->wmm_ac_params[1] = ac_bk; config->wmm_ac_params[2] = ac_vi; config->wmm_ac_params[3] = ac_vo; + config->p2p_search_delay = DEFAULT_P2P_SEARCH_DELAY; + config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME; + config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD; + config->cert_in_cb = DEFAULT_CERT_IN_CB; if (ctrl_interface) config->ctrl_interface = os_strdup(ctrl_interface); @@ -2679,6 +3558,8 @@ struct global_parse_data { char *name; int (*parser)(const struct global_parse_data *data, struct wpa_config *config, int line, const char *value); + int (*get)(const char *name, struct wpa_config *config, long offset, + char *buf, size_t buflen, int pretty_print); void *param1, *param2, *param3; unsigned int changed_flag; }; @@ -2688,9 +3569,18 @@ static int wpa_global_config_parse_int(const struct global_parse_data *data, struct wpa_config *config, int line, const char *pos) { - int *dst; + int val, *dst; + char *end; + dst = (int *) (((u8 *) config) + (long) data->param1); - *dst = atoi(pos); + val = strtol(pos, &end, 0); + if (*end) { + wpa_printf(MSG_ERROR, "Line %d: invalid number \"%s\"", + line, pos); + return -1; + } + *dst = val; + wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst); if (data->param2 && *dst < (long) data->param2) { @@ -2748,6 +3638,27 @@ static int wpa_global_config_parse_str(const struct global_parse_data *data, } +static int wpa_config_process_bgscan(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + size_t len; + char *tmp; + int res; + + tmp = wpa_config_parse_string(pos, &len); + if (tmp == NULL) { + wpa_printf(MSG_ERROR, "Line %d: failed to parse %s", + line, data->name); + return -1; + } + + res = wpa_global_config_parse_str(data, config, line, tmp); + os_free(tmp); + return res; +} + + static int wpa_global_config_parse_bin(const struct global_parse_data *data, struct wpa_config *config, int line, const char *pos) @@ -2777,6 +3688,48 @@ static int wpa_global_config_parse_bin(const struct global_parse_data *data, } +static int wpa_config_process_freq_list(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *value) +{ + int *freqs; + + freqs = wpa_config_parse_int_array(value); + if (freqs == NULL) + return -1; + if (freqs[0] == 0) { + os_free(freqs); + freqs = NULL; + } + os_free(config->freq_list); + config->freq_list = freqs; + return 0; +} + + +#ifdef CONFIG_P2P +static int wpa_global_config_parse_ipv4(const struct global_parse_data *data, + struct wpa_config *config, int line, + const char *pos) +{ + u32 *dst; + struct hostapd_ip_addr addr; + + if (hostapd_parse_ip_addr(pos, &addr) < 0) + return -1; + if (addr.af != AF_INET) + return -1; + + dst = (u32 *) (((u8 *) config) + (long) data->param1); + os_memcpy(dst, &addr.u.v4.s_addr, 4); + wpa_printf(MSG_DEBUG, "%s = 0x%x", data->name, + WPA_GET_BE32((u8 *) dst)); + + return 0; +} +#endif /* CONFIG_P2P */ + + static int wpa_config_process_country(const struct global_parse_data *data, struct wpa_config *config, int line, const char *pos) @@ -2961,6 +3914,26 @@ fail: wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_pref_chan list", line); return -1; } + + +static int wpa_config_process_p2p_no_go_freq( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + int ret; + + ret = freq_range_list_parse(&config->p2p_no_go_freq, pos); + if (ret < 0) { + wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_no_go_freq", line); + return -1; + } + + wpa_printf(MSG_DEBUG, "P2P: p2p_no_go_freq with %u items", + config->p2p_no_go_freq.num); + + return 0; +} + #endif /* CONFIG_P2P */ @@ -2978,36 +3951,151 @@ static int wpa_config_process_hessid( } +static int wpa_config_process_sae_groups( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + int *groups = wpa_config_parse_int_array(pos); + if (groups == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid sae_groups '%s'", + line, pos); + return -1; + } + + os_free(config->sae_groups); + config->sae_groups = groups; + + return 0; +} + + +static int wpa_config_process_ap_vendor_elements( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + struct wpabuf *tmp; + int len = os_strlen(pos) / 2; + u8 *p; + + if (!len) { + wpa_printf(MSG_ERROR, "Line %d: invalid ap_vendor_elements", + line); + return -1; + } + + tmp = wpabuf_alloc(len); + if (tmp) { + p = wpabuf_put(tmp, len); + + if (hexstr2bin(pos, p, len)) { + wpa_printf(MSG_ERROR, "Line %d: invalid " + "ap_vendor_elements", line); + wpabuf_free(tmp); + return -1; + } + + wpabuf_free(config->ap_vendor_elements); + config->ap_vendor_elements = tmp; + } else { + wpa_printf(MSG_ERROR, "Cannot allocate memory for " + "ap_vendor_elements"); + return -1; + } + + return 0; +} + + +#ifdef CONFIG_CTRL_IFACE +static int wpa_config_process_no_ctrl_interface( + const struct global_parse_data *data, + struct wpa_config *config, int line, const char *pos) +{ + wpa_printf(MSG_DEBUG, "no_ctrl_interface -> ctrl_interface=NULL"); + os_free(config->ctrl_interface); + config->ctrl_interface = NULL; + return 0; +} +#endif /* CONFIG_CTRL_IFACE */ + + +static int wpa_config_get_int(const char *name, struct wpa_config *config, + long offset, char *buf, size_t buflen, + int pretty_print) +{ + int *val = (int *) (((u8 *) config) + (long) offset); + + if (pretty_print) + return os_snprintf(buf, buflen, "%s=%d\n", name, *val); + return os_snprintf(buf, buflen, "%d", *val); +} + + +static int wpa_config_get_str(const char *name, struct wpa_config *config, + long offset, char *buf, size_t buflen, + int pretty_print) +{ + char **val = (char **) (((u8 *) config) + (long) offset); + int res; + + if (pretty_print) + res = os_snprintf(buf, buflen, "%s=%s\n", name, + *val ? *val : "null"); + else if (!*val) + return -1; + else + res = os_snprintf(buf, buflen, "%s", *val); + if (os_snprintf_error(buflen, res)) + res = -1; + + return res; +} + + #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 FUNC(f) #f, wpa_config_process_ ## f, NULL, OFFSET(f), NULL, NULL +#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL, NULL +#define _INT(f) #f, wpa_global_config_parse_int, wpa_config_get_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) #f, wpa_global_config_parse_str, wpa_config_get_str, OFFSET(f) #define STR(f) _STR(f), NULL, NULL #define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max -#define BIN(f) #f, wpa_global_config_parse_bin, OFFSET(f), NULL, NULL +#define BIN(f) #f, wpa_global_config_parse_bin, NULL, OFFSET(f), NULL, NULL +#define IPV4(f) #f, wpa_global_config_parse_ipv4, NULL, OFFSET(f), NULL, NULL static const struct global_parse_data global_fields[] = { #ifdef CONFIG_CTRL_IFACE { STR(ctrl_interface), 0 }, + { FUNC_NO_VAR(no_ctrl_interface), 0 }, { STR(ctrl_interface_group), 0 } /* deprecated */, #endif /* CONFIG_CTRL_IFACE */ +#ifdef CONFIG_MACSEC + { INT_RANGE(eapol_version, 1, 3), 0 }, +#else /* CONFIG_MACSEC */ { INT_RANGE(eapol_version, 1, 2), 0 }, +#endif /* CONFIG_MACSEC */ { INT(ap_scan), 0 }, + { FUNC(bgscan), 0 }, +#ifdef CONFIG_MESH + { INT(user_mpm), 0 }, + { INT_RANGE(max_peer_links, 0, 255), 0 }, + { INT(mesh_max_inactivity), 0 }, +#endif /* CONFIG_MESH */ { INT(disable_scan_offload), 0 }, { INT(fast_reauth), 0 }, { STR(opensc_engine_path), 0 }, { STR(pkcs11_engine_path), 0 }, { STR(pkcs11_module_path), 0 }, + { STR(openssl_ciphers), 0 }, { STR(pcsc_reader), 0 }, { STR(pcsc_pin), 0 }, + { INT(external_sim), 0 }, { STR(driver_param), 0 }, { INT(dot11RSNAConfigPMKLifetime), 0 }, { INT(dot11RSNAConfigPMKReauthThreshold), 0 }, @@ -3033,17 +4121,29 @@ static const struct global_parse_data global_fields[] = { { 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(p2p_oper_reg_class), CFG_CHANGED_P2P_OPER_CHANNEL }, + { INT(p2p_oper_channel), CFG_CHANGED_P2P_OPER_CHANNEL }, { 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 }, + { INT_RANGE(p2p_passphrase_len, 8, 63), + CFG_CHANGED_P2P_PASSPHRASE_LEN }, { FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN }, + { FUNC(p2p_no_go_freq), CFG_CHANGED_P2P_PREF_CHAN }, + { INT_RANGE(p2p_add_cli_chan, 0, 1), 0 }, + { INT_RANGE(p2p_optimize_listen_chan, 0, 1), 0 }, { INT(p2p_go_ht40), 0 }, + { INT(p2p_go_vht), 0 }, { INT(p2p_disabled), 0 }, + { INT_RANGE(p2p_go_ctwindow, 0, 127), 0 }, { INT(p2p_no_group_iface), 0 }, + { INT_RANGE(p2p_ignore_shared_freq, 0, 1), 0 }, + { IPV4(ip_addr_go), 0 }, + { IPV4(ip_addr_mask), 0 }, + { IPV4(ip_addr_start), 0 }, + { IPV4(ip_addr_end), 0 }, #endif /* CONFIG_P2P */ { FUNC(country), CFG_CHANGED_COUNTRY }, { INT(bss_max_count), 0 }, @@ -3061,15 +4161,34 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(access_network_type, 0, 15), 0 }, { INT_RANGE(pbc_in_m1, 0, 1), 0 }, { STR(autoscan), 0 }, - { INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff), 0 }, - { BIN(wps_nfc_dh_pubkey), 0 }, - { BIN(wps_nfc_dh_privkey), 0 }, - { BIN(wps_nfc_dev_pw), 0 }, + { INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff), + CFG_CHANGED_NFC_PASSWORD_TOKEN }, + { BIN(wps_nfc_dh_pubkey), CFG_CHANGED_NFC_PASSWORD_TOKEN }, + { BIN(wps_nfc_dh_privkey), CFG_CHANGED_NFC_PASSWORD_TOKEN }, + { BIN(wps_nfc_dev_pw), CFG_CHANGED_NFC_PASSWORD_TOKEN }, { STR(ext_password_backend), CFG_CHANGED_EXT_PW_BACKEND }, { INT(p2p_go_max_inactivity), 0 }, { INT_RANGE(auto_interworking, 0, 1), 0 }, { INT(okc), 0 }, { INT(pmf), 0 }, + { FUNC(sae_groups), 0 }, + { INT(dtim_period), 0 }, + { INT(beacon_int), 0 }, + { FUNC(ap_vendor_elements), 0 }, + { INT_RANGE(ignore_old_scan_res, 0, 1), 0 }, + { FUNC(freq_list), 0 }, + { INT(scan_cur_freq), 0 }, + { INT(sched_scan_interval), 0 }, + { INT(tdls_external_control), 0}, + { STR(osu_dir), 0 }, + { STR(wowlan_triggers), 0 }, + { INT(p2p_search_delay), 0}, + { INT(mac_addr), 0 }, + { INT(rand_addr_lifetime), 0 }, + { INT(preassoc_mac_addr), 0 }, + { INT(key_mgmt_offload), 0}, + { INT(passive_scan), 0 }, + { INT(reassoc_same_bss_optim), 0 }, }; #undef FUNC @@ -3080,7 +4199,52 @@ static const struct global_parse_data global_fields[] = { #undef STR #undef STR_RANGE #undef BIN -#define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0])) +#undef IPV4 +#define NUM_GLOBAL_FIELDS ARRAY_SIZE(global_fields) + + +int wpa_config_dump_values(struct wpa_config *config, char *buf, size_t buflen) +{ + int result = 0; + size_t i; + + for (i = 0; i < NUM_GLOBAL_FIELDS; i++) { + const struct global_parse_data *field = &global_fields[i]; + int tmp; + + if (!field->get) + continue; + + tmp = field->get(field->name, config, (long) field->param1, + buf, buflen, 1); + if (tmp < 0) + return -1; + buf += tmp; + buflen -= tmp; + result += tmp; + } + return result; +} + + +int wpa_config_get_value(const char *name, struct wpa_config *config, + char *buf, size_t buflen) +{ + size_t i; + + for (i = 0; i < NUM_GLOBAL_FIELDS; i++) { + const struct global_parse_data *field = &global_fields[i]; + + if (os_strcmp(name, field->name) != 0) + continue; + if (!field->get) + break; + return field->get(name, config, (long) field->param1, + buf, buflen, 0); + } + + return -1; +} int wpa_config_process_global(struct wpa_config *config, char *pos, int line) @@ -3100,6 +4264,8 @@ int wpa_config_process_global(struct wpa_config *config, char *pos, int line) "parse '%s'.", line, pos); ret = -1; } + if (field->changed_flag == CFG_CHANGED_NFC_PASSWORD_TOKEN) + config->wps_nfc_pw_from_config = 1; config->changed_parameters |= field->changed_flag; break; } diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h index bb70b9c55d736..34b754e09c532 100644 --- a/wpa_supplicant/config.h +++ b/wpa_supplicant/config.h @@ -15,15 +15,25 @@ #else /* CONFIG_NO_SCAN_PROCESSING */ #define DEFAULT_AP_SCAN 1 #endif /* CONFIG_NO_SCAN_PROCESSING */ +#define DEFAULT_USER_MPM 1 +#define DEFAULT_MAX_PEER_LINKS 99 +#define DEFAULT_MESH_MAX_INACTIVITY 300 #define DEFAULT_FAST_REAUTH 1 #define DEFAULT_P2P_GO_INTENT 7 #define DEFAULT_P2P_INTRA_BSS 1 #define DEFAULT_P2P_GO_MAX_INACTIVITY (5 * 60) +#define DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN 0 #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 +#define DEFAULT_SCAN_CUR_FREQ 0 +#define DEFAULT_P2P_SEARCH_DELAY 500 +#define DEFAULT_RAND_ADDR_LIFETIME 60 +#define DEFAULT_KEY_MGMT_OFFLOAD 1 +#define DEFAULT_CERT_IN_CB 1 +#define DEFAULT_P2P_GO_CTWINDOW 0 #include "config_ssid.h" #include "wps/wps.h" @@ -51,6 +61,11 @@ struct wpa_cred { int id; /** + * temporary - Whether this credential is temporary and not to be saved + */ + int temporary; + + /** * priority - Priority group * * By default, all networks and credentials get the same priority group @@ -149,12 +164,37 @@ struct wpa_cred { char *milenage; /** - * domain - Home service provider FQDN + * domain_suffix_match - Constraint for server domain name + * + * If set, this FQDN is used as a suffix match requirement for the AAA + * server certificate in SubjectAltName dNSName element(s). If a + * matching dNSName is found, this constraint is met. If no dNSName + * values are present, this constraint is matched against SubjectName CN + * using same suffix match comparison. Suffix match here means that the + * host/domain name is compared one label at a time starting from the + * top-level domain and all the labels in @domain_suffix_match shall be + * included in the certificate. The certificate may include additional + * sub-level labels in addition to the required labels. + * + * For example, domain_suffix_match=example.com would match + * test.example.com but would not match test-example.com. + */ + char *domain_suffix_match; + + /** + * domain - Home service provider FQDN(s) * * This is used to compare against the Domain Name List to figure out - * whether the AP is operated by the Home SP. + * whether the AP is operated by the Home SP. Multiple domain entries + * can be used to configure alternative FQDNs that will be considered + * home networks. + */ + char **domain; + + /** + * num_domain - Number of FQDNs in the domain array */ - char *domain; + size_t num_domain; /** * roaming_consortium - Roaming Consortium OI @@ -174,6 +214,9 @@ struct wpa_cred { */ size_t roaming_consortium_len; + u8 required_roaming_consortium[15]; + size_t required_roaming_consortium_len; + /** * eap_method - EAP method to use * @@ -202,6 +245,66 @@ struct wpa_cred { size_t ssid_len; } *excluded_ssid; size_t num_excluded_ssid; + + struct roaming_partner { + char fqdn[128]; + int exact_match; + u8 priority; + char country[3]; + } *roaming_partner; + size_t num_roaming_partner; + + int update_identifier; + + /** + * provisioning_sp - FQDN of the SP that provisioned the credential + */ + char *provisioning_sp; + + /** + * sp_priority - Credential priority within a provisioning SP + * + * This is the priority of the credential among all credentials + * provisionined by the same SP (i.e., for entries that have identical + * provisioning_sp value). The range of this priority is 0-255 with 0 + * being the highest and 255 the lower priority. + */ + int sp_priority; + + unsigned int min_dl_bandwidth_home; + unsigned int min_ul_bandwidth_home; + unsigned int min_dl_bandwidth_roaming; + unsigned int min_ul_bandwidth_roaming; + + /** + * max_bss_load - Maximum BSS Load Channel Utilization (1..255) + * This value is used as the maximum channel utilization for network + * selection purposes for home networks. If the AP does not advertise + * BSS Load or if the limit would prevent any connection, this + * constraint will be ignored. + */ + unsigned int max_bss_load; + + unsigned int num_req_conn_capab; + u8 *req_conn_capab_proto; + int **req_conn_capab_port; + + /** + * ocsp - Whether to use/require OCSP to check server certificate + * + * 0 = do not use OCSP stapling (TLS certificate status extension) + * 1 = try to use OCSP stapling, but not require response + * 2 = require valid OCSP stapling response + */ + int ocsp; + + /** + * sim_num - User selected SIM identifier + * + * This variable is used for identifying which SIM is used if the system + * has more than one. + */ + int sim_num; }; @@ -220,6 +323,8 @@ struct wpa_cred { #define CFG_CHANGED_P2P_OPER_CHANNEL BIT(12) #define CFG_CHANGED_P2P_PREF_CHAN BIT(13) #define CFG_CHANGED_EXT_PW_BACKEND BIT(14) +#define CFG_CHANGED_NFC_PASSWORD_TOKEN BIT(15) +#define CFG_CHANGED_P2P_PASSPHRASE_LEN BIT(16) /** * struct wpa_config - wpa_supplicant configuration data @@ -299,6 +404,18 @@ struct wpa_config { int ap_scan; /** + * bgscan - Background scan and roaming parameters or %NULL if none + * + * This is an optional set of parameters for background scanning and + * roaming within a network (ESS). For more detailed information see + * ssid block documentation. + * + * The variable defines default bgscan behavior for all BSS station + * networks except for those which have their own bgscan configuration. + */ + char *bgscan; + + /** * disable_scan_offload - Disable automatic offloading of scan requests * * By default, %wpa_supplicant tries to offload scanning if the driver @@ -406,6 +523,15 @@ struct wpa_config { char *pkcs11_module_path; /** + * openssl_ciphers - OpenSSL cipher string + * + * This is an OpenSSL specific configuration option for configuring the + * default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the + * default. + */ + char *openssl_ciphers; + + /** * pcsc_reader - PC/SC reader name prefix * * If not %NULL, PC/SC reader with a name that matches this prefix is @@ -423,6 +549,11 @@ struct wpa_config { char *pcsc_pin; /** + * external_sim - Use external processing for SIM/USIM operations + */ + int external_sim; + + /** * driver_param - Driver interface parameters * * This text string is passed to the selected driver interface with the @@ -570,6 +701,10 @@ struct wpa_config { int p2p_intra_bss; unsigned int num_p2p_pref_chan; struct p2p_channel *p2p_pref_chan; + struct wpa_freq_range_list p2p_no_go_freq; + int p2p_add_cli_chan; + int p2p_ignore_shared_freq; + int p2p_optimize_listen_chan; struct wpabuf *wps_vendor_ext_m1; @@ -598,6 +733,14 @@ struct wpa_config { int p2p_group_idle; /** + * p2p_passphrase_len - Passphrase length (8..63) for P2P GO + * + * This parameter controls the length of the random passphrase that is + * generated at the GO. + */ + unsigned int p2p_passphrase_len; + + /** * bss_max_count - Maximum number of BSS entries to keep in memory */ unsigned int bss_max_count; @@ -643,6 +786,22 @@ struct wpa_config { unsigned int max_num_sta; /** + * freq_list - Array of allowed scan frequencies or %NULL for all + * + * This is an optional zero-terminated array of frequencies in + * megahertz (MHz) to allow for narrowing scanning range. + */ + int *freq_list; + + /** + * scan_cur_freq - Whether to scan only the current channel + * + * If true, attempt to scan only the current channel if any other + * VIFs on this radio are already associated on a particular channel. + */ + int scan_cur_freq; + + /** * changed_parameters - Bitmap of changed parameters since last update */ unsigned int changed_parameters; @@ -706,6 +865,15 @@ struct wpa_config { char *autoscan; /** + * wps_nfc_pw_from_config - NFC Device Password was read from config + * + * This parameter can be determined whether the NFC Device Password was + * included in the configuration (1) or generated dynamically (0). Only + * the former case is re-written back to the configuration file. + */ + int wps_nfc_pw_from_config; + + /** * wps_nfc_dev_pw_id - NFC Device Password ID for password token */ int wps_nfc_dev_pw_id; @@ -765,6 +933,24 @@ struct wpa_config { int p2p_go_ht40; /** + * p2p_go_vht - Default mode for VHT enable when operating as GO + * + * This will take effect for p2p_group_add, p2p_connect, and p2p_invite. + * Note that regulatory constraints and driver capabilities are + * consulted anyway, so setting it to 1 can't do real harm. + * By default: 0 (disabled) + */ + int p2p_go_vht; + + /** + * p2p_go_ctwindow - CTWindow to use when operating as GO + * + * By default: 0 (no CTWindow). Values 0-127 can be used to indicate + * the length of the CTWindow in TUs. + */ + int p2p_go_ctwindow; + + /** * p2p_disabled - Whether P2P operations are disabled for this interface */ int p2p_disabled; @@ -797,6 +983,186 @@ struct wpa_config { * this default behavior. */ enum mfp_options pmf; + + /** + * sae_groups - Preference list of enabled groups for SAE + * + * By default (if this parameter is not set), the mandatory group 19 + * (ECC group defined over a 256-bit prime order field) is preferred, + * but other groups are also enabled. If this parameter is set, the + * groups will be tried in the indicated order. + */ + int *sae_groups; + + /** + * dtim_period - Default DTIM period in Beacon intervals + * + * This parameter can be used to set the default value for network + * blocks that do not specify dtim_period. + */ + int dtim_period; + + /** + * beacon_int - Default Beacon interval in TU + * + * This parameter can be used to set the default value for network + * blocks that do not specify beacon_int. + */ + int beacon_int; + + /** + * ap_vendor_elements: Vendor specific elements for Beacon/ProbeResp + * + * This parameter can be used to define additional vendor specific + * elements for Beacon and Probe Response frames in AP/P2P GO mode. The + * format for these element(s) is a hexdump of the raw information + * elements (id+len+payload for one or more elements). + */ + struct wpabuf *ap_vendor_elements; + + /** + * ignore_old_scan_res - Ignore scan results older than request + * + * The driver may have a cache of scan results that makes it return + * information that is older than our scan trigger. This parameter can + * be used to configure such old information to be ignored instead of + * allowing it to update the internal BSS table. + */ + int ignore_old_scan_res; + + /** + * sched_scan_interval - schedule scan interval + */ + unsigned int sched_scan_interval; + + /** + * tdls_external_control - External control for TDLS setup requests + * + * Enable TDLS mode where external programs are given the control + * to specify the TDLS link to get established to the driver. The + * driver requests the TDLS setup to the supplicant only for the + * specified TDLS peers. + */ + int tdls_external_control; + + u8 ip_addr_go[4]; + u8 ip_addr_mask[4]; + u8 ip_addr_start[4]; + u8 ip_addr_end[4]; + + /** + * osu_dir - OSU provider information directory + * + * If set, allow FETCH_OSU control interface command to be used to fetch + * OSU provider information into all APs and store the results in this + * directory. + */ + char *osu_dir; + + /** + * wowlan_triggers - Wake-on-WLAN triggers + * + * If set, these wowlan triggers will be configured. + */ + char *wowlan_triggers; + + /** + * p2p_search_delay - Extra delay between concurrent search iterations + * + * Add extra delay (in milliseconds) between search iterations when + * there is a concurrent operation to make p2p_find friendlier to + * concurrent operations by avoiding it from taking 100% of radio + * resources. + */ + unsigned int p2p_search_delay; + + /** + * mac_addr - MAC address policy default + * + * 0 = use permanent MAC address + * 1 = use random MAC address for each ESS connection + * 2 = like 1, but maintain OUI (with local admin bit set) + * + * By default, permanent MAC address is used unless policy is changed by + * the per-network mac_addr parameter. Global mac_addr=1 can be used to + * change this default behavior. + */ + int mac_addr; + + /** + * rand_addr_lifetime - Lifetime of random MAC address in seconds + */ + unsigned int rand_addr_lifetime; + + /** + * preassoc_mac_addr - Pre-association MAC address policy + * + * 0 = use permanent MAC address + * 1 = use random MAC address + * 2 = like 1, but maintain OUI (with local admin bit set) + */ + int preassoc_mac_addr; + + /** + * key_mgmt_offload - Use key management offload + * + * Key management offload should be used if the device supports it. + * Key management offload is the capability of a device operating as + * a station to do the exchange necessary to establish temporal keys + * during initial RSN connection, after roaming, or during a PTK + * rekeying operation. + */ + int key_mgmt_offload; + + /** + * user_mpm - MPM residency + * + * 0: MPM lives in driver. + * 1: wpa_supplicant handles peering and station allocation. + * + * If AMPE or SAE is enabled, the MPM is always in userspace. + */ + int user_mpm; + + /** + * max_peer_links - Maximum number of peer links + * + * Maximum number of mesh peering currently maintained by the STA. + */ + int max_peer_links; + + /** + * cert_in_cb - Whether to include a peer certificate dump in events + * + * This controls whether peer certificates for authentication server and + * its certificate chain are included in EAP peer certificate events. + */ + int cert_in_cb; + + /** + * mesh_max_inactivity - Timeout in seconds to detect STA inactivity + * + * This timeout value is used in mesh STA to clean up inactive stations. + * By default: 300 seconds. + */ + int mesh_max_inactivity; + + /** + * passive_scan - Whether to force passive scan for network connection + * + * This parameter can be used to force only passive scanning to be used + * for network connection cases. It should be noted that this will slow + * down scan operations and reduce likelihood of finding the AP. In + * addition, some use cases will override this due to functional + * requirements, e.g., for finding an AP that uses hidden SSID + * (scan_ssid=1) or P2P device discovery. + */ + int passive_scan; + + /** + * reassoc_same_bss_optim - Whether to optimize reassoc-to-same-BSS + */ + int reassoc_same_bss_optim; }; @@ -815,6 +1181,11 @@ 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); +int wpa_config_dump_values(struct wpa_config *config, char *buf, + size_t buflen); +int wpa_config_get_value(const char *name, struct wpa_config *config, + char *buf, size_t buflen); + 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); @@ -828,6 +1199,7 @@ void wpa_config_set_blob(struct wpa_config *config, struct wpa_config_blob *blob); void wpa_config_free_blob(struct wpa_config_blob *blob); int wpa_config_remove_blob(struct wpa_config *config, const char *name); +void wpa_config_flush_blobs(struct wpa_config *config); struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id); struct wpa_cred * wpa_config_add_cred(struct wpa_config *config); @@ -835,6 +1207,7 @@ int wpa_config_remove_cred(struct wpa_config *config, int id); void wpa_config_free_cred(struct wpa_cred *cred); int wpa_config_set_cred(struct wpa_cred *cred, const char *var, const char *value, int line); +char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var); struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, const char *driver_param); @@ -855,6 +1228,7 @@ int wpa_config_process_global(struct wpa_config *config, char *pos, int line); * wpa_config_read - Read and parse configuration database * @name: Name of the configuration (e.g., path and file name for the * configuration file) + * @cfgp: Pointer to previously allocated configuration data or %NULL if none * Returns: Pointer to allocated configuration data or %NULL on failure * * This function reads configuration data, parses its contents, and allocates @@ -863,7 +1237,7 @@ int wpa_config_process_global(struct wpa_config *config, char *pos, int line); * * Each configuration backend needs to implement this function. */ -struct wpa_config * wpa_config_read(const char *name); +struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp); /** * wpa_config_write - Write or update configuration data diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 8f32cc8d91e4a..3d3a6e404fd8a 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -11,6 +11,9 @@ */ #include "includes.h" +#ifdef ANDROID +#include <sys/stat.h> +#endif /* ANDROID */ #include "common.h" #include "config.h" @@ -143,6 +146,15 @@ static int wpa_config_validate_network(struct wpa_ssid *ssid, int line) ssid->group_cipher &= ~WPA_CIPHER_CCMP; } + if (ssid->mode == WPAS_MODE_MESH && + (ssid->key_mgmt != WPA_KEY_MGMT_NONE && + ssid->key_mgmt != WPA_KEY_MGMT_SAE)) { + wpa_printf(MSG_ERROR, + "Line %d: key_mgmt for mesh network should be open or SAE", + line); + errors++; + } + return errors; } @@ -158,6 +170,7 @@ static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id) ssid = os_zalloc(sizeof(*ssid)); if (ssid == NULL) return NULL; + dl_list_init(&ssid->psk_list); ssid->id = id; wpa_config_set_network_defaults(ssid); @@ -218,6 +231,7 @@ static struct wpa_cred * wpa_config_read_cred(FILE *f, int *line, int id) if (cred == NULL) return NULL; cred->id = id; + cred->sim_num = DEFAULT_USER_SELECTED_SIM; while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) { if (os_strcmp(pos, "}") == 0) { @@ -345,23 +359,34 @@ static int wpa_config_process_blob(struct wpa_config *config, FILE *f, #endif /* CONFIG_NO_CONFIG_BLOBS */ -struct wpa_config * wpa_config_read(const char *name) +struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp) { FILE *f; char buf[512], *pos; int errors = 0, line = 0; - struct wpa_ssid *ssid, *tail = NULL, *head = NULL; - struct wpa_cred *cred, *cred_tail = NULL, *cred_head = NULL; + struct wpa_ssid *ssid, *tail, *head; + struct wpa_cred *cred, *cred_tail, *cred_head; struct wpa_config *config; int id = 0; int cred_id = 0; - config = wpa_config_alloc_empty(NULL, NULL); + if (name == NULL) + return NULL; + if (cfgp) + config = cfgp; + else + config = wpa_config_alloc_empty(NULL, NULL); if (config == NULL) { wpa_printf(MSG_ERROR, "Failed to allocate config file " "structure"); return NULL; } + tail = head = config->ssid; + while (tail && tail->next) + tail = tail->next; + cred_tail = cred_head = config->cred; + while (cred_tail && cred_tail->next) + cred_tail = cred_tail->next; wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name); f = fopen(name, "r"); @@ -586,7 +611,7 @@ static void write_wep_key(FILE *f, int idx, struct wpa_ssid *ssid) int res; res = os_snprintf(field, sizeof(field), "wep_key%d", idx); - if (res < 0 || (size_t) res >= sizeof(field)) + if (os_snprintf_error(sizeof(field), res)) return; value = wpa_config_get(ssid, field); if (value) { @@ -597,6 +622,16 @@ static void write_wep_key(FILE *f, int idx, struct wpa_ssid *ssid) #ifdef CONFIG_P2P + +static void write_go_p2p_dev_addr(FILE *f, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, "go_p2p_dev_addr"); + if (value == NULL) + return; + fprintf(f, "\tgo_p2p_dev_addr=%s\n", value); + os_free(value); +} + static void write_p2p_client_list(FILE *f, struct wpa_ssid *ssid) { char *value = wpa_config_get(ssid, "p2p_client_list"); @@ -605,6 +640,20 @@ static void write_p2p_client_list(FILE *f, struct wpa_ssid *ssid) fprintf(f, "\tp2p_client_list=%s\n", value); os_free(value); } + + +static void write_psk_list(FILE *f, struct wpa_ssid *ssid) +{ + struct psk_list_entry *psk; + char hex[32 * 2 + 1]; + + dl_list_for_each(psk, &ssid->psk_list, struct psk_list_entry, list) { + wpa_snprintf_hex(hex, sizeof(hex), psk->psk, sizeof(psk->psk)); + fprintf(f, "\tpsk_list=%s" MACSTR "-%s\n", + psk->p2p ? "P2P-" : "", MAC2STR(psk->addr), hex); + } +} + #endif /* CONFIG_P2P */ @@ -621,6 +670,8 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) STR(ssid); INT(scan_ssid); write_bssid(f, ssid); + write_str(f, "bssid_blacklist", ssid); + write_str(f, "bssid_whitelist", ssid); write_psk(f, ssid); write_proto(f, ssid); write_key_mgmt(f, ssid); @@ -630,6 +681,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) write_auth_alg(f, ssid); STR(bgscan); STR(autoscan); + STR(scan_freq); #ifdef IEEE8021X_EAPOL write_eap(f, ssid); STR(identity); @@ -643,6 +695,8 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) STR(dh_file); STR(subject_match); STR(altsubject_match); + STR(domain_suffix_match); + STR(domain_match); STR(ca_cert2); STR(ca_path2); STR(client_cert2); @@ -651,6 +705,8 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) STR(dh_file2); STR(subject_match2); STR(altsubject_match2); + STR(domain_suffix_match2); + STR(domain_match2); STR(phase1); STR(phase2); STR(pcsc); @@ -667,6 +723,8 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INTe(engine); INTe(engine2); INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS); + STR(openssl_ciphers); + INTe(erp); #endif /* IEEE8021X_EAPOL */ for (i = 0; i < 4; i++) write_wep_key(f, i, ssid); @@ -676,20 +734,78 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND); STR(pac_file); INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE); + INTe(ocsp); + INT_DEFe(sim_num, DEFAULT_USER_SELECTED_SIM); #endif /* IEEE8021X_EAPOL */ INT(mode); + INT(no_auto_peer); INT(frequency); + INT(fixed_freq); write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1); INT(disabled); INT(peerkey); + INT(mixed_cell); #ifdef CONFIG_IEEE80211W write_int(f, "ieee80211w", ssid->ieee80211w, MGMT_FRAME_PROTECTION_DEFAULT); #endif /* CONFIG_IEEE80211W */ STR(id_str); #ifdef CONFIG_P2P + write_go_p2p_dev_addr(f, ssid); write_p2p_client_list(f, ssid); + write_psk_list(f, ssid); #endif /* CONFIG_P2P */ + INT(ap_max_inactivity); + INT(dtim_period); + INT(beacon_int); +#ifdef CONFIG_MACSEC + INT(macsec_policy); +#endif /* CONFIG_MACSEC */ +#ifdef CONFIG_HS20 + INT(update_identifier); +#endif /* CONFIG_HS20 */ + write_int(f, "mac_addr", ssid->mac_addr, -1); +#ifdef CONFIG_MESH + STR(mesh_basic_rates); + INT_DEF(dot11MeshMaxRetries, DEFAULT_MESH_MAX_RETRIES); + INT_DEF(dot11MeshRetryTimeout, DEFAULT_MESH_RETRY_TIMEOUT); + INT_DEF(dot11MeshConfirmTimeout, DEFAULT_MESH_CONFIRM_TIMEOUT); + INT_DEF(dot11MeshHoldingTimeout, DEFAULT_MESH_HOLDING_TIMEOUT); +#endif /* CONFIG_MESH */ + INT(wpa_ptk_rekey); + INT(ignore_broadcast_ssid); +#ifdef CONFIG_HT_OVERRIDES + INT_DEF(disable_ht, DEFAULT_DISABLE_HT); + INT_DEF(disable_ht40, DEFAULT_DISABLE_HT40); + INT_DEF(disable_sgi, DEFAULT_DISABLE_SGI); + INT_DEF(disable_ldpc, DEFAULT_DISABLE_LDPC); + INT(ht40_intolerant); + INT_DEF(disable_max_amsdu, DEFAULT_DISABLE_MAX_AMSDU); + INT_DEF(ampdu_factor, DEFAULT_AMPDU_FACTOR); + INT_DEF(ampdu_density, DEFAULT_AMPDU_DENSITY); + STR(ht_mcs); +#endif /* CONFIG_HT_OVERRIDES */ +#ifdef CONFIG_VHT_OVERRIDES + INT(disable_vht); + INT(vht_capa); + INT(vht_capa_mask); + INT_DEF(vht_rx_mcs_nss_1, -1); + INT_DEF(vht_rx_mcs_nss_2, -1); + INT_DEF(vht_rx_mcs_nss_3, -1); + INT_DEF(vht_rx_mcs_nss_4, -1); + INT_DEF(vht_rx_mcs_nss_5, -1); + INT_DEF(vht_rx_mcs_nss_6, -1); + INT_DEF(vht_rx_mcs_nss_7, -1); + INT_DEF(vht_rx_mcs_nss_8, -1); + INT_DEF(vht_tx_mcs_nss_1, -1); + INT_DEF(vht_tx_mcs_nss_2, -1); + INT_DEF(vht_tx_mcs_nss_3, -1); + INT_DEF(vht_tx_mcs_nss_4, -1); + INT_DEF(vht_tx_mcs_nss_5, -1); + INT_DEF(vht_tx_mcs_nss_6, -1); + INT_DEF(vht_tx_mcs_nss_7, -1); + INT_DEF(vht_tx_mcs_nss_8, -1); +#endif /* CONFIG_VHT_OVERRIDES */ #undef STR #undef INT @@ -699,6 +815,8 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred) { + size_t i; + if (cred->priority) fprintf(f, "\tpriority=%d\n", cred->priority); if (cred->pcsc) @@ -724,10 +842,12 @@ static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred) fprintf(f, "\timsi=\"%s\"\n", cred->imsi); if (cred->milenage) fprintf(f, "\tmilenage=\"%s\"\n", cred->milenage); - if (cred->domain) - fprintf(f, "\tdomain=\"%s\"\n", cred->domain); + for (i = 0; i < cred->num_domain; i++) + fprintf(f, "\tdomain=\"%s\"\n", cred->domain[i]); + if (cred->domain_suffix_match) + fprintf(f, "\tdomain_suffix_match=\"%s\"\n", + cred->domain_suffix_match); if (cred->roaming_consortium_len) { - size_t i; fprintf(f, "\troaming_consortium="); for (i = 0; i < cred->roaming_consortium_len; i++) fprintf(f, "%02x", cred->roaming_consortium[i]); @@ -737,14 +857,15 @@ static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred) const char *name; name = eap_get_name(cred->eap_method[0].vendor, cred->eap_method[0].method); - fprintf(f, "\teap=%s\n", name); + if (name) + fprintf(f, "\teap=%s\n", name); } if (cred->phase1) fprintf(f, "\tphase1=\"%s\"\n", cred->phase1); if (cred->phase2) fprintf(f, "\tphase2=\"%s\"\n", cred->phase2); if (cred->excluded_ssid) { - size_t i, j; + size_t j; for (i = 0; i < cred->num_excluded_ssid; i++) { struct excluded_ssid *e = &cred->excluded_ssid[i]; fprintf(f, "\texcluded_ssid="); @@ -753,6 +874,70 @@ static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred) fprintf(f, "\n"); } } + if (cred->roaming_partner) { + for (i = 0; i < cred->num_roaming_partner; i++) { + struct roaming_partner *p = &cred->roaming_partner[i]; + fprintf(f, "\troaming_partner=\"%s,%d,%u,%s\"\n", + p->fqdn, p->exact_match, p->priority, + p->country); + } + } + if (cred->update_identifier) + fprintf(f, "\tupdate_identifier=%d\n", cred->update_identifier); + + if (cred->provisioning_sp) + fprintf(f, "\tprovisioning_sp=\"%s\"\n", cred->provisioning_sp); + if (cred->sp_priority) + fprintf(f, "\tsp_priority=%d\n", cred->sp_priority); + + if (cred->min_dl_bandwidth_home) + fprintf(f, "\tmin_dl_bandwidth_home=%u\n", + cred->min_dl_bandwidth_home); + if (cred->min_ul_bandwidth_home) + fprintf(f, "\tmin_ul_bandwidth_home=%u\n", + cred->min_ul_bandwidth_home); + if (cred->min_dl_bandwidth_roaming) + fprintf(f, "\tmin_dl_bandwidth_roaming=%u\n", + cred->min_dl_bandwidth_roaming); + if (cred->min_ul_bandwidth_roaming) + fprintf(f, "\tmin_ul_bandwidth_roaming=%u\n", + cred->min_ul_bandwidth_roaming); + + if (cred->max_bss_load) + fprintf(f, "\tmax_bss_load=%u\n", + cred->max_bss_load); + + if (cred->ocsp) + fprintf(f, "\tocsp=%d\n", cred->ocsp); + + if (cred->num_req_conn_capab) { + for (i = 0; i < cred->num_req_conn_capab; i++) { + int *ports; + + fprintf(f, "\treq_conn_capab=%u", + cred->req_conn_capab_proto[i]); + ports = cred->req_conn_capab_port[i]; + if (ports) { + int j; + for (j = 0; ports[j] != -1; j++) { + fprintf(f, "%s%d", j > 0 ? "," : ":", + ports[j]); + } + } + fprintf(f, "\n"); + } + } + + if (cred->required_roaming_consortium_len) { + fprintf(f, "\trequired_roaming_consortium="); + for (i = 0; i < cred->required_roaming_consortium_len; i++) + fprintf(f, "%02x", + cred->required_roaming_consortium[i]); + fprintf(f, "\n"); + } + + if (cred->sim_num != DEFAULT_USER_SELECTED_SIM) + fprintf(f, "\tsim_num=%d\n", cred->sim_num); } @@ -816,6 +1001,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->pkcs11_module_path) fprintf(f, "pkcs11_module_path=%s\n", config->pkcs11_module_path); + if (config->openssl_ciphers) + fprintf(f, "openssl_ciphers=%s\n", config->openssl_ciphers); if (config->pcsc_reader) fprintf(f, "pcsc_reader=%s\n", config->pcsc_reader); if (config->pcsc_pin) @@ -898,6 +1085,9 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) 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_passphrase_len) + fprintf(f, "p2p_passphrase_len=%u\n", + config->p2p_passphrase_len); if (config->p2p_pref_chan) { unsigned int i; fprintf(f, "p2p_pref_chan="); @@ -908,13 +1098,33 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) } fprintf(f, "\n"); } + if (config->p2p_no_go_freq.num) { + char *val = freq_range_list_str(&config->p2p_no_go_freq); + if (val) { + fprintf(f, "p2p_no_go_freq=%s\n", val); + os_free(val); + } + } + if (config->p2p_add_cli_chan) + fprintf(f, "p2p_add_cli_chan=%d\n", config->p2p_add_cli_chan); + if (config->p2p_optimize_listen_chan != + DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN) + fprintf(f, "p2p_optimize_listen_chan=%d\n", + config->p2p_optimize_listen_chan); if (config->p2p_go_ht40) fprintf(f, "p2p_go_ht40=%u\n", config->p2p_go_ht40); + if (config->p2p_go_vht) + fprintf(f, "p2p_go_vht=%u\n", config->p2p_go_vht); + if (config->p2p_go_ctwindow != DEFAULT_P2P_GO_CTWINDOW) + fprintf(f, "p2p_go_ctwindow=%u\n", config->p2p_go_ctwindow); if (config->p2p_disabled) fprintf(f, "p2p_disabled=%u\n", config->p2p_disabled); if (config->p2p_no_group_iface) fprintf(f, "p2p_no_group_iface=%u\n", config->p2p_no_group_iface); + if (config->p2p_ignore_shared_freq) + fprintf(f, "p2p_ignore_shared_freq=%u\n", + config->p2p_ignore_shared_freq); #endif /* CONFIG_P2P */ if (config->country[0] && config->country[1]) { fprintf(f, "country=%c%c\n", @@ -950,12 +1160,16 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) #endif /* CONFIG_INTERWORKING */ if (config->pbc_in_m1) fprintf(f, "pbc_in_m1=%u\n", config->pbc_in_m1); - if (config->wps_nfc_dev_pw_id) - fprintf(f, "wps_nfc_dev_pw_id=%d\n", - config->wps_nfc_dev_pw_id); - write_global_bin(f, "wps_nfc_dh_pubkey", config->wps_nfc_dh_pubkey); - write_global_bin(f, "wps_nfc_dh_privkey", config->wps_nfc_dh_privkey); - write_global_bin(f, "wps_nfc_dev_pw", config->wps_nfc_dev_pw); + if (config->wps_nfc_pw_from_config) { + if (config->wps_nfc_dev_pw_id) + fprintf(f, "wps_nfc_dev_pw_id=%d\n", + config->wps_nfc_dev_pw_id); + write_global_bin(f, "wps_nfc_dh_pubkey", + config->wps_nfc_dh_pubkey); + write_global_bin(f, "wps_nfc_dh_privkey", + config->wps_nfc_dh_privkey); + write_global_bin(f, "wps_nfc_dev_pw", config->wps_nfc_dev_pw); + } if (config->ext_password_backend) fprintf(f, "ext_password_backend=%s\n", @@ -970,6 +1184,102 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) fprintf(f, "okc=%d\n", config->okc); if (config->pmf) fprintf(f, "pmf=%d\n", config->pmf); + if (config->dtim_period) + fprintf(f, "dtim_period=%d\n", config->dtim_period); + if (config->beacon_int) + fprintf(f, "beacon_int=%d\n", config->beacon_int); + + if (config->sae_groups) { + int i; + fprintf(f, "sae_groups="); + for (i = 0; config->sae_groups[i] >= 0; i++) { + fprintf(f, "%s%d", i > 0 ? " " : "", + config->sae_groups[i]); + } + fprintf(f, "\n"); + } + + if (config->ap_vendor_elements) { + int i, len = wpabuf_len(config->ap_vendor_elements); + const u8 *p = wpabuf_head_u8(config->ap_vendor_elements); + if (len > 0) { + fprintf(f, "ap_vendor_elements="); + for (i = 0; i < len; i++) + fprintf(f, "%02x", *p++); + fprintf(f, "\n"); + } + } + + if (config->ignore_old_scan_res) + fprintf(f, "ignore_old_scan_res=%d\n", + config->ignore_old_scan_res); + + if (config->freq_list && config->freq_list[0]) { + int i; + fprintf(f, "freq_list="); + for (i = 0; config->freq_list[i]; i++) { + fprintf(f, "%s%u", i > 0 ? " " : "", + config->freq_list[i]); + } + fprintf(f, "\n"); + } + if (config->scan_cur_freq != DEFAULT_SCAN_CUR_FREQ) + fprintf(f, "scan_cur_freq=%d\n", config->scan_cur_freq); + + if (config->sched_scan_interval) + fprintf(f, "sched_scan_interval=%u\n", + config->sched_scan_interval); + + if (config->external_sim) + fprintf(f, "external_sim=%d\n", config->external_sim); + + if (config->tdls_external_control) + fprintf(f, "tdls_external_control=%d\n", + config->tdls_external_control); + + if (config->wowlan_triggers) + fprintf(f, "wowlan_triggers=%s\n", + config->wowlan_triggers); + + if (config->bgscan) + fprintf(f, "bgscan=\"%s\"\n", config->bgscan); + + if (config->p2p_search_delay != DEFAULT_P2P_SEARCH_DELAY) + fprintf(f, "p2p_search_delay=%u\n", + config->p2p_search_delay); + + if (config->mac_addr) + fprintf(f, "mac_addr=%d\n", config->mac_addr); + + if (config->rand_addr_lifetime != DEFAULT_RAND_ADDR_LIFETIME) + fprintf(f, "rand_addr_lifetime=%u\n", + config->rand_addr_lifetime); + + if (config->preassoc_mac_addr) + fprintf(f, "preassoc_mac_addr=%d\n", config->preassoc_mac_addr); + + if (config->key_mgmt_offload != DEFAULT_KEY_MGMT_OFFLOAD) + fprintf(f, "key_mgmt_offload=%u\n", config->key_mgmt_offload); + + if (config->user_mpm != DEFAULT_USER_MPM) + fprintf(f, "user_mpm=%d\n", config->user_mpm); + + if (config->max_peer_links != DEFAULT_MAX_PEER_LINKS) + fprintf(f, "max_peer_links=%d\n", config->max_peer_links); + + if (config->cert_in_cb != DEFAULT_CERT_IN_CB) + fprintf(f, "cert_in_cb=%d\n", config->cert_in_cb); + + if (config->mesh_max_inactivity != DEFAULT_MESH_MAX_INACTIVITY) + fprintf(f, "mesh_max_inactivity=%d\n", + config->mesh_max_inactivity); + + if (config->passive_scan) + fprintf(f, "passive_scan=%d\n", config->passive_scan); + + if (config->reassoc_same_bss_optim) + fprintf(f, "reassoc_same_bss_optim=%d\n", + config->reassoc_same_bss_optim); } #endif /* CONFIG_NO_CONFIG_WRITE */ @@ -985,18 +1295,29 @@ int wpa_config_write(const char *name, struct wpa_config *config) struct wpa_config_blob *blob; #endif /* CONFIG_NO_CONFIG_BLOBS */ int ret = 0; + const char *orig_name = name; + int tmp_len = os_strlen(name) + 5; /* allow space for .tmp suffix */ + char *tmp_name = os_malloc(tmp_len); + + if (tmp_name) { + os_snprintf(tmp_name, tmp_len, "%s.tmp", name); + name = tmp_name; + } wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name); f = fopen(name, "w"); if (f == NULL) { wpa_printf(MSG_DEBUG, "Failed to open '%s' for writing", name); + os_free(tmp_name); return -1; } wpa_config_write_global(f, config); for (cred = config->cred; cred; cred = cred->next) { + if (cred->temporary) + continue; fprintf(f, "\ncred={\n"); wpa_config_write_cred(f, cred); fprintf(f, "}\n"); @@ -1023,8 +1344,21 @@ int wpa_config_write(const char *name, struct wpa_config *config) fclose(f); + if (tmp_name) { + int chmod_ret = 0; + +#ifdef ANDROID + chmod_ret = chmod(tmp_name, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); +#endif /* ANDROID */ + if (chmod_ret != 0 || rename(tmp_name, orig_name) != 0) + ret = -1; + + os_free(tmp_name); + } + wpa_printf(MSG_DEBUG, "Configuration file '%s' written %ssuccessfully", - name, ret ? "un" : ""); + orig_name, ret ? "un" : ""); return ret; #else /* CONFIG_NO_CONFIG_WRITE */ return -1; diff --git a/wpa_supplicant/config_none.c b/wpa_supplicant/config_none.c index 589ea3620d15c..2aac28fa3d172 100644 --- a/wpa_supplicant/config_none.c +++ b/wpa_supplicant/config_none.c @@ -17,11 +17,16 @@ #include "base64.h" -struct wpa_config * wpa_config_read(const char *name) +struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp) { struct wpa_config *config; - config = wpa_config_alloc_empty(NULL, NULL); + if (name == NULL) + return NULL; + if (cfgp) + config = cfgp; + else + config = wpa_config_alloc_empty(NULL, NULL); if (config == NULL) return NULL; /* TODO: fill in configuration data */ diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index 9ac67c739eeeb..7c826cfd983f8 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -1,6 +1,6 @@ /* * WPA Supplicant / Network configuration structures - * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,7 @@ #define CONFIG_SSID_H #include "common/defs.h" +#include "utils/list.h" #include "eap_peer/eap_config.h" #define MAX_SSID_LEN 32 @@ -26,12 +27,25 @@ #define DEFAULT_FRAGMENT_SIZE 1398 #define DEFAULT_BG_SCAN_PERIOD -1 +#define DEFAULT_MESH_MAX_RETRIES 2 +#define DEFAULT_MESH_RETRY_TIMEOUT 40 +#define DEFAULT_MESH_CONFIRM_TIMEOUT 40 +#define DEFAULT_MESH_HOLDING_TIMEOUT 40 #define DEFAULT_DISABLE_HT 0 #define DEFAULT_DISABLE_HT40 0 #define DEFAULT_DISABLE_SGI 0 +#define DEFAULT_DISABLE_LDPC 0 #define DEFAULT_DISABLE_MAX_AMSDU -1 /* no change */ #define DEFAULT_AMPDU_FACTOR -1 /* no change */ #define DEFAULT_AMPDU_DENSITY -1 /* no change */ +#define DEFAULT_USER_SELECTED_SIM 1 + +struct psk_list_entry { + struct dl_list list; + u8 addr[ETH_ALEN]; + u8 psk[32]; + u8 p2p; +}; /** * struct wpa_ssid - Network configuration data @@ -118,11 +132,28 @@ struct wpa_ssid { u8 bssid[ETH_ALEN]; /** + * bssid_blacklist - List of inacceptable BSSIDs + */ + u8 *bssid_blacklist; + size_t num_bssid_blacklist; + + /** + * bssid_blacklist - List of acceptable BSSIDs + */ + u8 *bssid_whitelist; + size_t num_bssid_whitelist; + + /** * bssid_set - Whether BSSID is configured for this network */ int bssid_set; /** + * go_p2p_dev_addr - GO's P2P Device Address or all zeros if not set + */ + u8 go_p2p_dev_addr[ETH_ALEN]; + + /** * psk - WPA pre-shared key (256 bits) */ u8 psk[32]; @@ -302,12 +333,15 @@ struct wpa_ssid { * 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 - * following network block options: proto=WPA, key_mgmt=WPA-NONE, - * pairwise=NONE, group=TKIP (or CCMP, but not both), and psk must also - * be set (either directly or using ASCII passphrase). + * 5 = Mesh + * + * Note: IBSS can only be used with key_mgmt NONE (plaintext and static + * WEP) and WPA-PSK (with proto=RSN). In addition, key_mgmt=WPA-NONE + * (fixed group key TKIP/CCMP) is available for backwards compatibility, + * but its use is deprecated. WPA-None requires following network block + * options: proto=WPA, key_mgmt=WPA-NONE, pairwise=NONE, group=TKIP (or + * CCMP, but not both), and psk must also be set (either directly or + * using ASCII passphrase). */ enum wpas_mode { WPAS_MODE_INFRA = 0, @@ -315,6 +349,7 @@ struct wpa_ssid { WPAS_MODE_AP = 2, WPAS_MODE_P2P_GO = 3, WPAS_MODE_P2P_GROUP_FORMATION = 4, + WPAS_MODE_MESH = 5, } mode; /** @@ -384,8 +419,29 @@ struct wpa_ssid { */ int frequency; + /** + * fixed_freq - Use fixed frequency for IBSS + */ + int fixed_freq; + + /** + * mesh_basic_rates - BSS Basic rate set for mesh network + * + */ + int *mesh_basic_rates; + + /** + * Mesh network plink parameters + */ + int dot11MeshMaxRetries; + int dot11MeshRetryTimeout; /* msec */ + int dot11MeshConfirmTimeout; /* msec */ + int dot11MeshHoldingTimeout; /* msec */ + int ht40; + int vht; + /** * wpa_ptk_rekey - Maximum lifetime for PTK in seconds * @@ -456,6 +512,11 @@ struct wpa_ssid { #endif /* P2P_MAX_STORED_CLIENTS */ /** + * psk_list - Per-client PSKs (struct psk_list_entry) + */ + struct dl_list psk_list; + + /** * p2p_group - Network generated as a P2P group (used internally) */ int p2p_group; @@ -504,6 +565,19 @@ struct wpa_ssid { int disable_sgi; /** + * disable_ldpc - Disable LDPC for this network + * + * By default, use it if it is available, but this can be configured + * to 1 to have it disabled. + */ + int disable_ldpc; + + /** + * ht40_intolerant - Indicate 40 MHz intolerant for this network + */ + int ht40_intolerant; + + /** * disable_max_amsdu - Disable MAX A-MSDU * * A-MDSU will be 3839 bytes when disabled, or 7935 @@ -534,6 +608,35 @@ struct wpa_ssid { char *ht_mcs; #endif /* CONFIG_HT_OVERRIDES */ +#ifdef CONFIG_VHT_OVERRIDES + /** + * disable_vht - Disable VHT (IEEE 802.11ac) for this network + * + * By default, use it if it is available, but this can be configured + * to 1 to have it disabled. + */ + int disable_vht; + + /** + * vht_capa - VHT capabilities to use + */ + unsigned int vht_capa; + + /** + * vht_capa_mask - mask for VHT capabilities + */ + unsigned int vht_capa_mask; + + int vht_rx_mcs_nss_1, vht_rx_mcs_nss_2, + vht_rx_mcs_nss_3, vht_rx_mcs_nss_4, + vht_rx_mcs_nss_5, vht_rx_mcs_nss_6, + vht_rx_mcs_nss_7, vht_rx_mcs_nss_8; + int vht_tx_mcs_nss_1, vht_tx_mcs_nss_2, + vht_tx_mcs_nss_3, vht_tx_mcs_nss_4, + vht_tx_mcs_nss_5, vht_tx_mcs_nss_6, + vht_tx_mcs_nss_7, vht_tx_mcs_nss_8; +#endif /* CONFIG_VHT_OVERRIDES */ + /** * ap_max_inactivity - Timeout in seconds to detect STA's inactivity * @@ -549,6 +652,11 @@ struct wpa_ssid { int dtim_period; /** + * beacon_int - Beacon interval (default: 100 TU) + */ + int beacon_int; + + /** * auth_failures - Number of consecutive authentication failures */ unsigned int auth_failures; @@ -556,7 +664,7 @@ struct wpa_ssid { /** * disabled_until - Network block disabled until this time if non-zero */ - struct os_time disabled_until; + struct os_reltime disabled_until; /** * parent_cred - Pointer to parent wpa_cred entry @@ -566,6 +674,44 @@ struct wpa_ssid { * dereferences since it may not be updated in all cases. */ void *parent_cred; + +#ifdef CONFIG_MACSEC + /** + * macsec_policy - Determines the policy for MACsec secure session + * + * 0: MACsec not in use (default) + * 1: MACsec enabled - Should secure, accept key server's advice to + * determine whether to use a secure session or not. + */ + int macsec_policy; +#endif /* CONFIG_MACSEC */ + +#ifdef CONFIG_HS20 + int update_identifier; +#endif /* CONFIG_HS20 */ + + unsigned int wps_run; + + /** + * mac_addr - MAC address policy + * + * 0 = use permanent MAC address + * 1 = use random MAC address for each ESS connection + * 2 = like 1, but maintain OUI (with local admin bit set) + * + * Internally, special value -1 is used to indicate that the parameter + * was not specified in the configuration (i.e., default behavior is + * followed). + */ + int mac_addr; + + /** + * no_auto_peer - Do not automatically peer with compatible mesh peers + * + * When unset, the reception of a beacon from a another mesh peer in + * this MBSS will trigger a peering attempt. + */ + int no_auto_peer; }; #endif /* CONFIG_SSID_H */ diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c index 2750b63734ae8..199f04fbbf13e 100644 --- a/wpa_supplicant/config_winreg.c +++ b/wpa_supplicant/config_winreg.c @@ -302,6 +302,7 @@ static struct wpa_ssid * wpa_config_read_network(HKEY hk, const TCHAR *netw, RegCloseKey(nhk); return NULL; } + dl_list_init(&ssid->psk_list); ssid->id = id; wpa_config_set_network_defaults(ssid); @@ -434,7 +435,7 @@ static int wpa_config_read_networks(struct wpa_config *config, HKEY hk) } -struct wpa_config * wpa_config_read(const char *name) +struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp) { TCHAR buf[256]; int errors = 0; @@ -442,7 +443,12 @@ struct wpa_config * wpa_config_read(const char *name) HKEY hk; LONG ret; - config = wpa_config_alloc_empty(NULL, NULL); + if (name == NULL) + return NULL; + if (cfgp) + config = cfgp; + else + config = wpa_config_alloc_empty(NULL, NULL); if (config == NULL) return NULL; wpa_printf(MSG_DEBUG, "Reading configuration profile '%s'", name); @@ -617,6 +623,9 @@ static int wpa_config_write_global(struct wpa_config *config, HKEY hk) wpa_config_write_reg_dword(hk, TEXT("okc"), config->okc, 0); wpa_config_write_reg_dword(hk, TEXT("pmf"), config->pmf, 0); + wpa_config_write_reg_dword(hk, TEXT("external_sim"), + config->external_sim, 0); + return 0; } @@ -921,6 +930,9 @@ static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id) MGMT_FRAME_PROTECTION_DEFAULT); #endif /* CONFIG_IEEE80211W */ STR(id_str); +#ifdef CONFIG_HS20 + INT(update_identifier); +#endif /* CONFIG_HS20 */ #undef STR #undef INT diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 864dd7d6d76a3..b4aefb65ec254 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -1,19 +1,26 @@ /* * WPA Supplicant / Control interface (shared code for all backends) - * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" +#ifdef CONFIG_TESTING_OPTIONS +#include <net/ethernet.h> +#include <netinet/ip.h> +#endif /* CONFIG_TESTING_OPTIONS */ #include "utils/common.h" #include "utils/eloop.h" +#include "utils/uuid.h" #include "common/version.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" +#include "crypto/tls.h" +#include "ap/hostapd.h" #include "eap_peer/eap.h" #include "eapol_supp/eapol_supp_sm.h" #include "rsn_supp/wpa.h" @@ -39,98 +46,16 @@ #include "blacklist.h" #include "autoscan.h" #include "wnm_sta.h" - -extern struct wpa_driver_ops *wpa_drivers[]; +#include "offchannel.h" +#include "drivers/driver.h" +#include "mesh.h" static int wpa_supplicant_global_iface_list(struct wpa_global *global, char *buf, int len); static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, char *buf, int len); - - -static int pno_start(struct wpa_supplicant *wpa_s) -{ - int ret; - size_t i, num_ssid; - struct wpa_ssid *ssid; - struct wpa_driver_scan_params params; - - if (wpa_s->pno) - return 0; - - if (wpa_s->wpa_state == WPA_SCANNING) { - wpa_supplicant_cancel_sched_scan(wpa_s); - wpa_supplicant_cancel_scan(wpa_s); - } - - os_memset(¶ms, 0, sizeof(params)); - - num_ssid = 0; - ssid = wpa_s->conf->ssid; - while (ssid) { - if (!wpas_network_disabled(wpa_s, ssid)) - num_ssid++; - ssid = ssid->next; - } - if (num_ssid > WPAS_MAX_SCAN_SSIDS) { - wpa_printf(MSG_DEBUG, "PNO: Use only the first %u SSIDs from " - "%u", WPAS_MAX_SCAN_SSIDS, (unsigned int) num_ssid); - num_ssid = WPAS_MAX_SCAN_SSIDS; - } - - if (num_ssid == 0) { - wpa_printf(MSG_DEBUG, "PNO: No configured SSIDs"); - return -1; - } - - params.filter_ssids = os_malloc(sizeof(struct wpa_driver_scan_filter) * - num_ssid); - if (params.filter_ssids == NULL) - return -1; - i = 0; - ssid = wpa_s->conf->ssid; - while (ssid) { - if (!wpas_network_disabled(wpa_s, ssid)) { - params.ssids[i].ssid = ssid->ssid; - params.ssids[i].ssid_len = ssid->ssid_len; - params.num_ssids++; - os_memcpy(params.filter_ssids[i].ssid, ssid->ssid, - ssid->ssid_len); - params.filter_ssids[i].ssid_len = ssid->ssid_len; - params.num_filter_ssids++; - i++; - if (i == num_ssid) - break; - } - ssid = ssid->next; - } - - if (wpa_s->conf->filter_rssi) - params.filter_rssi = wpa_s->conf->filter_rssi; - - ret = wpa_drv_sched_scan(wpa_s, ¶ms, 10 * 1000); - os_free(params.filter_ssids); - if (ret == 0) - wpa_s->pno = 1; - return ret; -} - - -static int pno_stop(struct wpa_supplicant *wpa_s) -{ - int ret = 0; - - if (wpa_s->pno) { - wpa_s->pno = 0; - ret = wpa_drv_stop_sched_scan(wpa_s); - } - - if (wpa_s->wpa_state == WPA_SCANNING) - wpa_supplicant_req_scan(wpa_s, 0, 0); - - return ret; -} - +static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s, + char *val); static int set_bssid_filter(struct wpa_supplicant *wpa_s, char *val) { @@ -178,7 +103,7 @@ static int set_disallow_aps(struct wpa_supplicant *wpa_s, char *val) struct wpa_ssid *c; /* - * disallow_list ::= <ssid_spec> | <bssid_spec> | <disallow_list> | “” + * disallow_list ::= <ssid_spec> | <bssid_spec> | <disallow_list> | "" * SSID_SPEC ::= ssid <SSID_HEX> * BSSID_SPEC ::= bssid <BSSID_HEX> */ @@ -284,6 +209,7 @@ static int set_disallow_aps(struct wpa_supplicant *wpa_s, char *val) wpa_s->sme.prev_bssid_set = 0; #endif /* CONFIG_SME */ wpa_s->reassociate = 1; + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); wpa_supplicant_req_scan(wpa_s, 0, 0); @@ -291,6 +217,72 @@ static int set_disallow_aps(struct wpa_supplicant *wpa_s, char *val) } +#ifndef CONFIG_NO_CONFIG_BLOBS +static int wpas_ctrl_set_blob(struct wpa_supplicant *wpa_s, char *pos) +{ + char *name = pos; + struct wpa_config_blob *blob; + size_t len; + + pos = os_strchr(pos, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + len = os_strlen(pos); + if (len & 1) + return -1; + + wpa_printf(MSG_DEBUG, "CTRL: Set blob '%s'", name); + blob = os_zalloc(sizeof(*blob)); + if (blob == NULL) + return -1; + blob->name = os_strdup(name); + blob->data = os_malloc(len / 2); + if (blob->name == NULL || blob->data == NULL) { + wpa_config_free_blob(blob); + return -1; + } + + if (hexstr2bin(pos, blob->data, len / 2) < 0) { + wpa_printf(MSG_DEBUG, "CTRL: Invalid blob hex data"); + wpa_config_free_blob(blob); + return -1; + } + blob->len = len / 2; + + wpa_config_set_blob(wpa_s->conf, blob); + + return 0; +} +#endif /* CONFIG_NO_CONFIG_BLOBS */ + + +static int wpas_ctrl_pno(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *params; + char *pos; + int *freqs = NULL; + int ret; + + if (atoi(cmd)) { + params = os_strchr(cmd, ' '); + os_free(wpa_s->manual_sched_scan_freqs); + if (params) { + params++; + pos = os_strstr(params, "freq="); + if (pos) + freqs = freq_range_to_channel_list(wpa_s, + pos + 5); + } + wpa_s->manual_sched_scan_freqs = freqs; + ret = wpas_start_pno(wpa_s); + } else { + ret = wpas_stop_pno(wpa_s); + } + return ret; +} + + static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, char *cmd) { @@ -348,17 +340,21 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, wps_testing_dummy_cred = atoi(value); wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d", wps_testing_dummy_cred); + } else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) { + wps_corrupt_pkhash = atoi(value); + wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d", + wps_corrupt_pkhash); #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 #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); @@ -370,10 +366,7 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, wpa_tdls_enable(wpa_s->wpa, !disabled); #endif /* CONFIG_TDLS */ } else if (os_strcasecmp(cmd, "pno") == 0) { - if (atoi(value)) - ret = pno_start(wpa_s); - else - ret = pno_stop(wpa_s); + ret = wpas_ctrl_pno(wpa_s, value); } else if (os_strcasecmp(cmd, "radio_disabled") == 0) { int disabled = atoi(value); if (wpa_drv_radio_disable(wpa_s, disabled) < 0) @@ -420,7 +413,11 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, ret = wpa_drv_set_p2p_powersave(wpa_s, atoi(value), -1, -1); #ifdef CONFIG_WIFI_DISPLAY } else if (os_strcasecmp(cmd, "wifi_display") == 0) { - wifi_display_enable(wpa_s->global, !!atoi(value)); + int enabled = !!atoi(value); + if (enabled && !wpa_s->global->p2p) + ret = -1; + else + wifi_display_enable(wpa_s->global, enabled); #endif /* CONFIG_WIFI_DISPLAY */ } else if (os_strcasecmp(cmd, "bssid_filter") == 0) { ret = set_bssid_filter(wpa_s, value); @@ -428,6 +425,35 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, ret = set_disallow_aps(wpa_s, value); } else if (os_strcasecmp(cmd, "no_keep_alive") == 0) { wpa_s->no_keep_alive = !!atoi(value); +#ifdef CONFIG_TESTING_OPTIONS + } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) { + wpa_s->ext_mgmt_frame_handling = !!atoi(value); + } else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) { + wpa_s->ext_eapol_frame_io = !!atoi(value); +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + wpa_s->ap_iface->bss[0]->ext_eapol_frame_io = + wpa_s->ext_eapol_frame_io; + } +#endif /* CONFIG_AP */ + } else if (os_strcasecmp(cmd, "extra_roc_dur") == 0) { + wpa_s->extra_roc_dur = atoi(value); + } else if (os_strcasecmp(cmd, "test_failure") == 0) { + wpa_s->test_failure = atoi(value); +#endif /* CONFIG_TESTING_OPTIONS */ +#ifndef CONFIG_NO_CONFIG_BLOBS + } else if (os_strcmp(cmd, "blob") == 0) { + ret = wpas_ctrl_set_blob(wpa_s, value); +#endif /* CONFIG_NO_CONFIG_BLOBS */ + } else if (os_strcasecmp(cmd, "setband") == 0) { + if (os_strcmp(value, "AUTO") == 0) + wpa_s->setband = WPA_SETBAND_AUTO; + else if (os_strcmp(value, "5G") == 0) + wpa_s->setband = WPA_SETBAND_5G; + else if (os_strcmp(value, "2G") == 0) + wpa_s->setband = WPA_SETBAND_2G; + else + ret = -1; } else { value[-1] = '='; ret = wpa_config_process_global(wpa_s->conf, cmd, -1); @@ -455,15 +481,29 @@ static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s, wpa_s->conf->country[1]); #ifdef CONFIG_WIFI_DISPLAY } else if (os_strcasecmp(cmd, "wifi_display") == 0) { - res = os_snprintf(buf, buflen, "%d", - wpa_s->global->wifi_display); - if (res < 0 || (unsigned int) res >= buflen) + int enabled; + if (wpa_s->global->p2p == NULL || + wpa_s->global->p2p_disabled) + enabled = 0; + else + enabled = wpa_s->global->wifi_display; + res = os_snprintf(buf, buflen, "%d", enabled); +#endif /* CONFIG_WIFI_DISPLAY */ +#ifdef CONFIG_TESTING_GET_GTK + } else if (os_strcmp(cmd, "gtk") == 0) { + if (wpa_s->last_gtk_len == 0) return -1; + res = wpa_snprintf_hex(buf, buflen, wpa_s->last_gtk, + wpa_s->last_gtk_len); return res; -#endif /* CONFIG_WIFI_DISPLAY */ +#endif /* CONFIG_TESTING_GET_GTK */ + } else if (os_strcmp(cmd, "tls_library") == 0) { + res = tls_get_library_version(buf, buflen); + } else { + res = wpa_config_get_value(cmd, wpa_s->conf, buf, buflen); } - if (res < 0 || (unsigned int) res >= buflen) + if (os_snprintf_error(buflen, res)) return -1; return res; } @@ -554,13 +594,16 @@ static int wpa_supplicant_ctrl_iface_tdls_setup( 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); - } + if ((wpa_s->conf->tdls_external_control) && + wpa_tdls_is_external_setup(wpa_s->wpa)) + return wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer); + + wpa_tdls_remove(wpa_s->wpa, peer); + + 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; } @@ -570,6 +613,14 @@ static int wpa_supplicant_ctrl_iface_tdls_teardown( struct wpa_supplicant *wpa_s, char *addr) { u8 peer[ETH_ALEN]; + int ret; + + if (os_strcmp(addr, "*") == 0) { + /* remove everyone */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN *"); + wpa_tdls_teardown_peers(wpa_s->wpa); + return 0; + } if (hwaddr_aton(addr, peer)) { wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN: invalid " @@ -580,13 +631,187 @@ static int wpa_supplicant_ctrl_iface_tdls_teardown( 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); + if ((wpa_s->conf->tdls_external_control) && + wpa_tdls_is_external_setup(wpa_s->wpa)) + return wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer); + + if (wpa_tdls_is_external_setup(wpa_s->wpa)) + ret = wpa_tdls_teardown_link( + wpa_s->wpa, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); + else + ret = wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer); + + return ret; +} + + +static int ctrl_iface_get_capability_tdls( + struct wpa_supplicant *wpa_s, char *buf, size_t buflen) +{ + int ret; + + ret = os_snprintf(buf, buflen, "%s\n", + wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT ? + (wpa_s->drv_flags & + WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP ? + "EXTERNAL" : "INTERNAL") : "UNSUPPORTED"); + if (os_snprintf_error(buflen, ret)) + return -1; + return ret; +} + + +static int wpa_supplicant_ctrl_iface_tdls_chan_switch( + struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 peer[ETH_ALEN]; + struct hostapd_freq_params freq_params; + u8 oper_class; + char *pos, *end; + + if (!wpa_tdls_is_external_setup(wpa_s->wpa)) { + wpa_printf(MSG_INFO, + "tdls_chanswitch: Only supported with external setup"); + return -1; + } + + os_memset(&freq_params, 0, sizeof(freq_params)); + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + oper_class = strtol(pos, &end, 10); + if (pos == end) { + wpa_printf(MSG_INFO, + "tdls_chanswitch: Invalid op class provided"); + return -1; + } + + pos = end; + freq_params.freq = atoi(pos); + if (freq_params.freq == 0) { + wpa_printf(MSG_INFO, "tdls_chanswitch: Invalid freq provided"); + return -1; + } + +#define SET_FREQ_SETTING(str) \ + do { \ + const char *pos2 = os_strstr(pos, " " #str "="); \ + if (pos2) { \ + pos2 += sizeof(" " #str "=") - 1; \ + freq_params.str = atoi(pos2); \ + } \ + } while (0) + + SET_FREQ_SETTING(center_freq1); + SET_FREQ_SETTING(center_freq2); + SET_FREQ_SETTING(bandwidth); + SET_FREQ_SETTING(sec_channel_offset); +#undef SET_FREQ_SETTING + + freq_params.ht_enabled = !!os_strstr(pos, " ht"); + freq_params.vht_enabled = !!os_strstr(pos, " vht"); + + if (hwaddr_aton(cmd, peer)) { + wpa_printf(MSG_DEBUG, + "CTRL_IFACE TDLS_CHAN_SWITCH: Invalid address '%s'", + cmd); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CHAN_SWITCH " MACSTR + " OP CLASS %d FREQ %d CENTER1 %d CENTER2 %d BW %d SEC_OFFSET %d%s%s", + MAC2STR(peer), oper_class, freq_params.freq, + freq_params.center_freq1, freq_params.center_freq2, + freq_params.bandwidth, freq_params.sec_channel_offset, + freq_params.ht_enabled ? " HT" : "", + freq_params.vht_enabled ? " VHT" : ""); + + return wpa_tdls_enable_chan_switch(wpa_s->wpa, peer, oper_class, + &freq_params); +} + + +static int wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch( + struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 peer[ETH_ALEN]; + + if (!wpa_tdls_is_external_setup(wpa_s->wpa)) { + wpa_printf(MSG_INFO, + "tdls_chanswitch: Only supported with external setup"); + return -1; + } + + if (hwaddr_aton(cmd, peer)) { + wpa_printf(MSG_DEBUG, + "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH: Invalid address '%s'", + cmd); + return -1; + } + + wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH " MACSTR, + MAC2STR(peer)); + + return wpa_tdls_disable_chan_switch(wpa_s->wpa, peer); } #endif /* CONFIG_TDLS */ +static int wmm_ac_ctrl_addts(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *token, *context = NULL; + struct wmm_ac_ts_setup_params params = { + .tsid = 0xff, + .direction = 0xff, + }; + + while ((token = str_token(cmd, " ", &context))) { + if (sscanf(token, "tsid=%i", ¶ms.tsid) == 1 || + sscanf(token, "up=%i", ¶ms.user_priority) == 1 || + sscanf(token, "nominal_msdu_size=%i", + ¶ms.nominal_msdu_size) == 1 || + sscanf(token, "mean_data_rate=%i", + ¶ms.mean_data_rate) == 1 || + sscanf(token, "min_phy_rate=%i", + ¶ms.minimum_phy_rate) == 1 || + sscanf(token, "sba=%i", + ¶ms.surplus_bandwidth_allowance) == 1) + continue; + + if (os_strcasecmp(token, "downlink") == 0) { + params.direction = WMM_TSPEC_DIRECTION_DOWNLINK; + } else if (os_strcasecmp(token, "uplink") == 0) { + params.direction = WMM_TSPEC_DIRECTION_UPLINK; + } else if (os_strcasecmp(token, "bidi") == 0) { + params.direction = WMM_TSPEC_DIRECTION_BI_DIRECTIONAL; + } else if (os_strcasecmp(token, "fixed_nominal_msdu") == 0) { + params.fixed_nominal_msdu = 1; + } else { + wpa_printf(MSG_DEBUG, + "CTRL: Invalid WMM_AC_ADDTS parameter: '%s'", + token); + return -1; + } + + } + + return wpas_wmm_ac_addts(wpa_s, ¶ms); +} + + +static int wmm_ac_ctrl_delts(struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 tsid = atoi(cmd); + + return wpas_wmm_ac_delts(wpa_s, tsid); +} + + #ifdef CONFIG_IEEE80211R static int wpa_supplicant_ctrl_iface_ft_ds( struct wpa_supplicant *wpa_s, char *addr) @@ -700,7 +925,7 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s, if (ret < 0) return -1; ret = os_snprintf(buf, buflen, "%s", pin); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return -1; return ret; } @@ -712,7 +937,7 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s, done: /* Return the generated PIN */ ret = os_snprintf(buf, buflen, "%08d", ret); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return -1; return ret; } @@ -749,14 +974,14 @@ static int wpa_supplicant_ctrl_iface_wps_check_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) + if (os_snprintf_error(buflen, ret)) return -1; return ret; } } ret = os_snprintf(buf, buflen, "%s", pin); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return -1; return ret; @@ -775,7 +1000,41 @@ static int wpa_supplicant_ctrl_iface_wps_nfc(struct wpa_supplicant *wpa_s, else if (hwaddr_aton(cmd, bssid)) return -1; - return wpas_wps_start_nfc(wpa_s, _bssid); + return wpas_wps_start_nfc(wpa_s, NULL, _bssid, NULL, 0, 0, NULL, NULL, + 0, 0); +} + + +static int wpa_supplicant_ctrl_iface_wps_nfc_config_token( + struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) +{ + int ndef; + struct wpabuf *buf; + int res; + char *pos; + + pos = os_strchr(cmd, ' '); + if (pos) + *pos++ = '\0'; + if (os_strcmp(cmd, "WPS") == 0) + ndef = 0; + else if (os_strcmp(cmd, "NDEF") == 0) + ndef = 1; + else + return -1; + + buf = wpas_wps_nfc_config_token(wpa_s, ndef, pos); + if (buf == NULL) + return -1; + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; } @@ -814,6 +1073,15 @@ static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read( size_t len; struct wpabuf *buf; int ret; + char *freq; + int forced_freq = 0; + + freq = strstr(pos, " freq="); + if (freq) { + *freq = '\0'; + freq += 6; + forced_freq = atoi(freq); + } len = os_strlen(pos); if (len & 0x01) @@ -828,7 +1096,7 @@ static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read( return -1; } - ret = wpas_wps_nfc_tag_read(wpa_s, buf); + ret = wpas_wps_nfc_tag_read(wpa_s, buf, forced_freq); wpabuf_free(buf); return ret; @@ -836,12 +1104,13 @@ static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read( static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s, - char *reply, size_t max_len) + char *reply, size_t max_len, + int ndef) { struct wpabuf *buf; int res; - buf = wpas_wps_nfc_handover_req(wpa_s); + buf = wpas_wps_nfc_handover_req(wpa_s, ndef); if (buf == NULL) return -1; @@ -856,36 +1125,77 @@ static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_P2P +static int wpas_ctrl_nfc_get_handover_req_p2p(struct wpa_supplicant *wpa_s, + char *reply, size_t max_len, + int ndef) +{ + struct wpabuf *buf; + int res; + + buf = wpas_p2p_nfc_handover_req(wpa_s, ndef); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Could not generate NFC handover request"); + return -1; + } + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; +} +#endif /* CONFIG_P2P */ + + static int wpas_ctrl_nfc_get_handover_req(struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) { char *pos; + int ndef; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; - if (os_strcmp(cmd, "NDEF") != 0) + if (os_strcmp(cmd, "WPS") == 0) + ndef = 0; + else if (os_strcmp(cmd, "NDEF") == 0) + ndef = 1; + else return -1; - if (os_strcmp(pos, "WPS") == 0) { - return wpas_ctrl_nfc_get_handover_req_wps(wpa_s, reply, - max_len); + if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) { + if (!ndef) + return -1; + return wpas_ctrl_nfc_get_handover_req_wps( + wpa_s, reply, max_len, ndef); + } + +#ifdef CONFIG_P2P + if (os_strcmp(pos, "P2P-CR") == 0) { + return wpas_ctrl_nfc_get_handover_req_p2p( + wpa_s, reply, max_len, ndef); } +#endif /* CONFIG_P2P */ return -1; } static int wpas_ctrl_nfc_get_handover_sel_wps(struct wpa_supplicant *wpa_s, - char *reply, size_t max_len) + char *reply, size_t max_len, + int ndef, int cr, char *uuid) { struct wpabuf *buf; int res; - buf = wpas_wps_nfc_handover_sel(wpa_s); + buf = wpas_wps_nfc_handover_sel(wpa_s, ndef, cr, uuid); if (buf == NULL) return -1; @@ -900,79 +1210,188 @@ static int wpas_ctrl_nfc_get_handover_sel_wps(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_P2P +static int wpas_ctrl_nfc_get_handover_sel_p2p(struct wpa_supplicant *wpa_s, + char *reply, size_t max_len, + int ndef, int tag) +{ + struct wpabuf *buf; + int res; + + buf = wpas_p2p_nfc_handover_sel(wpa_s, ndef, tag); + if (buf == NULL) + return -1; + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; +} +#endif /* CONFIG_P2P */ + + static int wpas_ctrl_nfc_get_handover_sel(struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) { - char *pos; + char *pos, *pos2; + int ndef; pos = os_strchr(cmd, ' '); if (pos == NULL) return -1; *pos++ = '\0'; - if (os_strcmp(cmd, "NDEF") != 0) + if (os_strcmp(cmd, "WPS") == 0) + ndef = 0; + else if (os_strcmp(cmd, "NDEF") == 0) + ndef = 1; + else return -1; - if (os_strcmp(pos, "WPS") == 0) { - return wpas_ctrl_nfc_get_handover_sel_wps(wpa_s, reply, - max_len); + pos2 = os_strchr(pos, ' '); + if (pos2) + *pos2++ = '\0'; + if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) { + if (!ndef) + return -1; + return wpas_ctrl_nfc_get_handover_sel_wps( + wpa_s, reply, max_len, ndef, + os_strcmp(pos, "WPS-CR") == 0, pos2); } +#ifdef CONFIG_P2P + if (os_strcmp(pos, "P2P-CR") == 0) { + return wpas_ctrl_nfc_get_handover_sel_p2p( + wpa_s, reply, max_len, ndef, 0); + } + + if (os_strcmp(pos, "P2P-CR-TAG") == 0) { + return wpas_ctrl_nfc_get_handover_sel_p2p( + wpa_s, reply, max_len, ndef, 1); + } +#endif /* CONFIG_P2P */ + return -1; } -static int wpas_ctrl_nfc_rx_handover_req(struct wpa_supplicant *wpa_s, - char *cmd, char *reply, - size_t max_len) +static int wpas_ctrl_nfc_report_handover(struct wpa_supplicant *wpa_s, + char *cmd) { size_t len; - struct wpabuf *buf; + struct wpabuf *req, *sel; int ret; + char *pos, *role, *type, *pos2; +#ifdef CONFIG_P2P + char *freq; + int forced_freq = 0; - len = os_strlen(cmd); - if (len & 0x01) - return -1; - len /= 2; + freq = strstr(cmd, " freq="); + if (freq) { + *freq = '\0'; + freq += 6; + forced_freq = atoi(freq); + } +#endif /* CONFIG_P2P */ - buf = wpabuf_alloc(len); - if (buf == NULL) - return -1; - if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) { - wpabuf_free(buf); + role = cmd; + pos = os_strchr(role, ' '); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "NFC: Missing type in handover report"); return -1; } + *pos++ = '\0'; - ret = wpas_wps_nfc_rx_handover_req(wpa_s, buf); - wpabuf_free(buf); + type = pos; + pos = os_strchr(type, ' '); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "NFC: Missing request message in handover report"); + return -1; + } + *pos++ = '\0'; - return ret; -} + pos2 = os_strchr(pos, ' '); + if (pos2 == NULL) { + wpa_printf(MSG_DEBUG, "NFC: Missing select message in handover report"); + return -1; + } + *pos2++ = '\0'; + len = os_strlen(pos); + if (len & 0x01) { + wpa_printf(MSG_DEBUG, "NFC: Invalid request message length in handover report"); + return -1; + } + len /= 2; -static int wpas_ctrl_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s, - char *cmd) -{ - size_t len; - struct wpabuf *buf; - int ret; + req = wpabuf_alloc(len); + if (req == NULL) { + wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for request message"); + return -1; + } + if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) { + wpa_printf(MSG_DEBUG, "NFC: Invalid request message hexdump in handover report"); + wpabuf_free(req); + return -1; + } - len = os_strlen(cmd); - if (len & 0x01) + len = os_strlen(pos2); + if (len & 0x01) { + wpa_printf(MSG_DEBUG, "NFC: Invalid select message length in handover report"); + wpabuf_free(req); return -1; + } len /= 2; - buf = wpabuf_alloc(len); - if (buf == NULL) + sel = wpabuf_alloc(len); + if (sel == NULL) { + wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for select message"); + wpabuf_free(req); return -1; - if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) { - wpabuf_free(buf); + } + if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) { + wpa_printf(MSG_DEBUG, "NFC: Invalid select message hexdump in handover report"); + wpabuf_free(req); + wpabuf_free(sel); return -1; } - ret = wpas_wps_nfc_rx_handover_sel(wpa_s, buf); - wpabuf_free(buf); + wpa_printf(MSG_DEBUG, "NFC: Connection handover reported - role=%s type=%s req_len=%d sel_len=%d", + role, type, (int) wpabuf_len(req), (int) wpabuf_len(sel)); + + if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "WPS") == 0) { + ret = wpas_wps_nfc_report_handover(wpa_s, req, sel); +#ifdef CONFIG_AP + } else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0) + { + ret = wpas_ap_wps_nfc_report_handover(wpa_s, req, sel); + if (ret < 0) + ret = wpas_er_wps_nfc_report_handover(wpa_s, req, sel); +#endif /* CONFIG_AP */ +#ifdef CONFIG_P2P + } else if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "P2P") == 0) + { + ret = wpas_p2p_nfc_report_handover(wpa_s, 1, req, sel, 0); + } else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "P2P") == 0) + { + ret = wpas_p2p_nfc_report_handover(wpa_s, 0, req, sel, + forced_freq); +#endif /* CONFIG_P2P */ + } else { + wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover " + "reported: role=%s type=%s", role, type); + ret = -1; + } + wpabuf_free(req); + wpabuf_free(sel); + + if (ret) + wpa_printf(MSG_DEBUG, "NFC: Failed to process reported handover messages"); return ret; } @@ -1282,7 +1701,14 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, { char *pos, *end, tmp[30]; int res, verbose, wps, ret; +#ifdef CONFIG_HS20 + const u8 *hs20; +#endif /* CONFIG_HS20 */ + const u8 *sess_id; + size_t sess_id_len; + if (os_strcmp(params, "-DRIVER") == 0) + return wpa_drv_status(wpa_s, buf, buflen); verbose = os_strcmp(params, "-VERBOSE") == 0; wps = os_strcmp(params, "-WPS") == 0; pos = buf; @@ -1291,7 +1717,12 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid = wpa_s->current_ssid; ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n", MAC2STR(wpa_s->bssid)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + ret = os_snprintf(pos, end - pos, "freq=%u\n", + wpa_s->assoc_freq); + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; if (ssid) { @@ -1309,7 +1740,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, ret = os_snprintf(pos, end - pos, "ssid=%s\nid=%d\n", wpa_ssid_txt(_ssid, ssid_len), ssid->id); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -1320,7 +1751,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, ret = os_snprintf(pos, end - pos, "passphrase=%s\n", ssid->passphrase); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -1328,7 +1759,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, ret = os_snprintf(pos, end - pos, "id_str=%s\n", ssid->id_str); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -1359,7 +1790,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, ret = 0; break; } - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -1373,16 +1804,29 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, #endif /* CONFIG_AP */ pos += wpa_sm_get_status(wpa_s->wpa, pos, end - pos, verbose); } +#ifdef CONFIG_SAE + if (wpa_s->wpa_state >= WPA_ASSOCIATED && +#ifdef CONFIG_AP + !wpa_s->ap_iface && +#endif /* CONFIG_AP */ + wpa_s->sme.sae.state == SAE_ACCEPTED) { + ret = os_snprintf(pos, end - pos, "sae_group=%d\n", + wpa_s->sme.sae.group); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_SAE */ ret = os_snprintf(pos, end - pos, "wpa_state=%s\n", wpa_supplicant_state_txt(wpa_s->wpa_state)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; if (wpa_s->l2 && l2_packet_get_ip_addr(wpa_s->l2, tmp, sizeof(tmp)) >= 0) { ret = os_snprintf(pos, end - pos, "ip_address=%s\n", tmp); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -1391,7 +1835,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, 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) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -1399,17 +1843,23 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, ret = os_snprintf(pos, end - pos, "address=" MACSTR "\n", MAC2STR(wpa_s->own_addr)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; #ifdef CONFIG_HS20 if (wpa_s->current_bss && - wpa_bss_get_vendor_ie(wpa_s->current_bss, HS20_IE_VENDOR_TYPE) && + (hs20 = wpa_bss_get_vendor_ie(wpa_s->current_bss, + HS20_IE_VENDOR_TYPE)) && wpa_s->wpa_proto == WPA_PROTO_RSN && wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) { - ret = os_snprintf(pos, end - pos, "hs20=1\n"); - if (ret < 0 || ret >= end - pos) + int release = 1; + if (hs20[1] >= 5) { + u8 rel_num = (hs20[6] & 0xf0) >> 4; + release = rel_num + 1; + } + ret = os_snprintf(pos, end - pos, "hs20=%d\n", release); + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -1419,17 +1869,43 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, char *type; for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + size_t i; + if (wpa_s->current_ssid->parent_cred != cred) continue; - if (!cred->domain) - continue; + if (cred->provisioning_sp) { + ret = os_snprintf(pos, end - pos, + "provisioning_sp=%s\n", + cred->provisioning_sp); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + if (!cred->domain) + goto no_domain; + + i = 0; + if (wpa_s->current_bss && wpa_s->current_bss->anqp) { + struct wpabuf *names = + wpa_s->current_bss->anqp->domain_name; + for (i = 0; names && i < cred->num_domain; i++) + { + if (domain_name_list_contains( + names, cred->domain[i], 1)) + break; + } + if (i == cred->num_domain) + i = 0; /* show first entry by default */ + } ret = os_snprintf(pos, end - pos, "home_sp=%s\n", - cred->domain); - if (ret < 0 || ret >= end - pos) + cred->domain[i]); + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; + no_domain: if (wpa_s->current_bss == NULL || wpa_s->current_bss->anqp == NULL) res = -1; @@ -1445,7 +1921,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, type = "unknown"; ret = os_snprintf(pos, end - pos, "sp_type=%s\n", type); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -1462,10 +1938,66 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, pos += res; } + sess_id = eapol_sm_get_session_id(wpa_s->eapol, &sess_id_len); + if (sess_id) { + char *start = pos; + + ret = os_snprintf(pos, end - pos, "eap_session_id="); + if (os_snprintf_error(end - pos, ret)) + return start - buf; + pos += ret; + ret = wpa_snprintf_hex(pos, end - pos, sess_id, sess_id_len); + if (ret <= 0) + return start - buf; + pos += ret; + ret = os_snprintf(pos, end - pos, "\n"); + if (os_snprintf_error(end - pos, ret)) + return start - buf; + pos += ret; + } + res = rsn_preauth_get_status(wpa_s->wpa, pos, end - pos, verbose); if (res >= 0) pos += res; +#ifdef CONFIG_WPS + { + char uuid_str[100]; + uuid_bin2str(wpa_s->wps->uuid, uuid_str, sizeof(uuid_str)); + ret = os_snprintf(pos, end - pos, "uuid=%s\n", uuid_str); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_WPS */ + +#ifdef ANDROID + /* + * Allow using the STATUS command with default behavior, say for debug, + * i.e., don't generate a "fake" CONNECTION and SUPPLICANT_STATE_CHANGE + * events with STATUS-NO_EVENTS. + */ + if (os_strcmp(params, "-NO_EVENTS")) { + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE + "id=%d state=%d BSSID=" MACSTR " SSID=%s", + wpa_s->current_ssid ? wpa_s->current_ssid->id : -1, + wpa_s->wpa_state, + MAC2STR(wpa_s->bssid), + wpa_s->current_ssid && wpa_s->current_ssid->ssid ? + wpa_ssid_txt(wpa_s->current_ssid->ssid, + wpa_s->current_ssid->ssid_len) : ""); + if (wpa_s->wpa_state == WPA_COMPLETED) { + struct wpa_ssid *ssid = wpa_s->current_ssid; + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED + "- connection to " MACSTR + " completed %s [id=%d id_str=%s]", + MAC2STR(wpa_s->bssid), "(auth)", + ssid ? ssid->id : -1, + ssid && ssid->id_str ? ssid->id_str : ""); + } + } +#endif /* ANDROID */ + return pos - buf; } @@ -1521,7 +2053,7 @@ static int wpa_supplicant_ctrl_iface_blacklist(struct wpa_supplicant *wpa_s, while (e) { ret = os_snprintf(pos, end - pos, MACSTR "\n", MAC2STR(e->bssid)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; e = e->next; @@ -1547,19 +2079,16 @@ static int wpa_supplicant_ctrl_iface_blacklist(struct wpa_supplicant *wpa_s, * skipped when processing scan results. */ ret = wpa_blacklist_add(wpa_s, bssid); - if (ret != 0) + if (ret < 0) return -1; ret = wpa_blacklist_add(wpa_s, bssid); - if (ret != 0) + 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) { @@ -1606,10 +2135,6 @@ static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s, char *pos, *end, *stamp; int ret; - if (cmd == NULL) { - return -1; - } - /* cmd: "LOG_LEVEL [<level>]" */ if (*cmd == '\0') { pos = buf; @@ -1618,7 +2143,7 @@ static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s, "Timestamp: %d\n", debug_level_str(wpa_debug_level), wpa_debug_timestamp); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) ret = 0; return ret; @@ -1651,9 +2176,9 @@ static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s, static int wpa_supplicant_ctrl_iface_list_networks( - struct wpa_supplicant *wpa_s, char *buf, size_t buflen) + struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { - char *pos, *end; + char *pos, *end, *prev; struct wpa_ssid *ssid; int ret; @@ -1661,17 +2186,29 @@ static int wpa_supplicant_ctrl_iface_list_networks( end = buf + buflen; ret = os_snprintf(pos, end - pos, "network id / ssid / bssid / flags\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; ssid = wpa_s->conf->ssid; + + /* skip over ssids until we find next one */ + if (cmd != NULL && os_strncmp(cmd, "LAST_ID=", 8) == 0) { + int last_id = atoi(cmd + 8); + if (last_id != -1) { + while (ssid != NULL && ssid->id <= last_id) { + ssid = ssid->next; + } + } + } + while (ssid) { + prev = pos; ret = os_snprintf(pos, end - pos, "%d\t%s", ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); - if (ret < 0 || ret >= end - pos) - return pos - buf; + if (os_snprintf_error(end - pos, ret)) + return prev - buf; pos += ret; if (ssid->bssid_set) { ret = os_snprintf(pos, end - pos, "\t" MACSTR, @@ -1679,8 +2216,8 @@ static int wpa_supplicant_ctrl_iface_list_networks( } else { ret = os_snprintf(pos, end - pos, "\tany"); } - if (ret < 0 || ret >= end - pos) - return pos - buf; + if (os_snprintf_error(end - pos, ret)) + return prev - buf; pos += ret; ret = os_snprintf(pos, end - pos, "\t%s%s%s%s", ssid == wpa_s->current_ssid ? @@ -1690,12 +2227,12 @@ static int wpa_supplicant_ctrl_iface_list_networks( "[TEMP-DISABLED]" : "", ssid->disabled == 2 ? "[P2P-PERSISTENT]" : ""); - if (ret < 0 || ret >= end - pos) - return pos - buf; + if (os_snprintf_error(end - pos, ret)) + return prev - buf; pos += ret; ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) - return pos - buf; + if (os_snprintf_error(end - pos, ret)) + return prev - buf; pos += ret; ssid = ssid->next; @@ -1707,54 +2244,15 @@ static int wpa_supplicant_ctrl_iface_list_networks( static char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher) { - int first = 1, ret; + int ret; ret = os_snprintf(pos, end - pos, "-"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) + return pos; + pos += ret; + ret = wpa_write_ciphers(pos, end, cipher, "+"); + if (ret < 0) return pos; pos += ret; - if (cipher & WPA_CIPHER_NONE) { - ret = os_snprintf(pos, end - pos, "%sNONE", first ? "" : "+"); - if (ret < 0 || ret >= end - pos) - return pos; - pos += ret; - first = 0; - } - if (cipher & WPA_CIPHER_WEP40) { - ret = os_snprintf(pos, end - pos, "%sWEP40", first ? "" : "+"); - if (ret < 0 || ret >= end - pos) - return pos; - pos += ret; - first = 0; - } - if (cipher & WPA_CIPHER_WEP104) { - ret = os_snprintf(pos, end - pos, "%sWEP104", - first ? "" : "+"); - if (ret < 0 || ret >= end - pos) - return pos; - pos += ret; - first = 0; - } - if (cipher & WPA_CIPHER_TKIP) { - ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : "+"); - if (ret < 0 || ret >= end - pos) - return pos; - pos += ret; - first = 0; - } - if (cipher & WPA_CIPHER_CCMP) { - ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : "+"); - if (ret < 0 || ret >= end - pos) - return pos; - pos += ret; - first = 0; - } - if (cipher & WPA_CIPHER_GCMP) { - ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : "+"); - if (ret < 0 || ret >= end - pos) - return pos; - pos += ret; - first = 0; - } return pos; } @@ -1763,91 +2261,122 @@ static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto, const u8 *ie, size_t ie_len) { struct wpa_ie_data data; - int first, ret; + char *start; + int ret; ret = os_snprintf(pos, end - pos, "[%s-", proto); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; if (wpa_parse_wpa_ie(ie, ie_len, &data) < 0) { ret = os_snprintf(pos, end - pos, "?]"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; return pos; } - first = 1; + start = pos; if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) { - ret = os_snprintf(pos, end - pos, "%sEAP", first ? "" : "+"); - if (ret < 0 || ret >= end - pos) + ret = os_snprintf(pos, end - pos, "%sEAP", + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; - first = 0; } if (data.key_mgmt & WPA_KEY_MGMT_PSK) { - ret = os_snprintf(pos, end - pos, "%sPSK", first ? "" : "+"); - if (ret < 0 || ret >= end - pos) + ret = os_snprintf(pos, end - pos, "%sPSK", + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; - first = 0; } if (data.key_mgmt & WPA_KEY_MGMT_WPA_NONE) { - ret = os_snprintf(pos, end - pos, "%sNone", first ? "" : "+"); - if (ret < 0 || ret >= end - pos) + ret = os_snprintf(pos, end - pos, "%sNone", + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) + return pos; + pos += ret; + } + if (data.key_mgmt & WPA_KEY_MGMT_SAE) { + ret = os_snprintf(pos, end - pos, "%sSAE", + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; - first = 0; } #ifdef CONFIG_IEEE80211R if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { ret = os_snprintf(pos, end - pos, "%sFT/EAP", - first ? "" : "+"); - if (ret < 0 || ret >= end - pos) + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; - first = 0; } if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) { ret = os_snprintf(pos, end - pos, "%sFT/PSK", - first ? "" : "+"); - if (ret < 0 || ret >= end - pos) + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) + return pos; + pos += ret; + } + if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE) { + ret = os_snprintf(pos, end - pos, "%sFT/SAE", + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; - first = 0; } #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { ret = os_snprintf(pos, end - pos, "%sEAP-SHA256", - first ? "" : "+"); - if (ret < 0 || ret >= end - pos) + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; - first = 0; } if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { ret = os_snprintf(pos, end - pos, "%sPSK-SHA256", - first ? "" : "+"); - if (ret < 0 || ret >= end - pos) + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; - first = 0; } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SUITEB + if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { + ret = os_snprintf(pos, end - pos, "%sEAP-SUITE-B", + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) + return pos; + pos += ret; + } +#endif /* CONFIG_SUITEB */ + +#ifdef CONFIG_SUITEB192 + if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { + ret = os_snprintf(pos, end - pos, "%sEAP-SUITE-B-192", + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) + return pos; + pos += ret; + } +#endif /* CONFIG_SUITEB192 */ + pos = wpa_supplicant_cipher_txt(pos, end, data.pairwise_cipher); if (data.capabilities & WPA_CAPABILITY_PREAUTH) { ret = os_snprintf(pos, end - pos, "-preauth"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; } ret = os_snprintf(pos, end - pos, "]"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos; pos += ret; @@ -1867,17 +2396,15 @@ static char * wpa_supplicant_wps_ie_txt_buf(struct wpa_supplicant *wpa_s, 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 txt = "[WPS]"; ret = os_snprintf(pos, end - pos, "%s", txt); - if (ret >= 0 && ret < end - pos) + if (!os_snprintf_error(end - pos, ret)) pos += ret; wpabuf_free(wps_ie); return pos; @@ -1906,9 +2433,12 @@ static int wpa_supplicant_ctrl_iface_scan_result( { char *pos, *end; int ret; - const u8 *ie, *ie2, *p2p; + const u8 *ie, *ie2, *p2p, *mesh; + mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID); p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE); + if (!p2p) + p2p = wpa_bss_get_vendor_ie_beacon(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) @@ -1919,44 +2449,78 @@ 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) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); if (ie) pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]); ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN); - if (ie2) - pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); + if (ie2) { + pos = wpa_supplicant_ie_txt(pos, end, mesh ? "RSN" : "WPA2", + ie2, 2 + ie2[1]); + } 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) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } - if (bss->caps & IEEE80211_CAP_IBSS) { - ret = os_snprintf(pos, end - pos, "[IBSS]"); - if (ret < 0 || ret >= end - pos) + if (mesh) { + ret = os_snprintf(pos, end - pos, "[MESH]"); + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } - if (bss->caps & IEEE80211_CAP_ESS) { - ret = os_snprintf(pos, end - pos, "[ESS]"); - if (ret < 0 || ret >= end - pos) + if (bss_is_dmg(bss)) { + const char *s; + ret = os_snprintf(pos, end - pos, "[DMG]"); + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; + switch (bss->caps & IEEE80211_CAP_DMG_MASK) { + case IEEE80211_CAP_DMG_IBSS: + s = "[IBSS]"; + break; + case IEEE80211_CAP_DMG_AP: + s = "[ESS]"; + break; + case IEEE80211_CAP_DMG_PBSS: + s = "[PBSS]"; + break; + default: + s = ""; + break; + } + ret = os_snprintf(pos, end - pos, "%s", s); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } else { + if (bss->caps & IEEE80211_CAP_IBSS) { + ret = os_snprintf(pos, end - pos, "[IBSS]"); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + if (bss->caps & IEEE80211_CAP_ESS) { + ret = os_snprintf(pos, end - pos, "[ESS]"); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } } if (p2p) { ret = os_snprintf(pos, end - pos, "[P2P]"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } #ifdef CONFIG_HS20 if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE) && ie2) { ret = os_snprintf(pos, end - pos, "[HS20]"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; } @@ -1964,12 +2528,12 @@ static int wpa_supplicant_ctrl_iface_scan_result( ret = os_snprintf(pos, end - pos, "\t%s", wpa_ssid_txt(bss->ssid, bss->ssid_len)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; @@ -1988,7 +2552,7 @@ static int wpa_supplicant_ctrl_iface_scan_results( end = buf + buflen; ret = os_snprintf(pos, end - pos, "bssid / frequency / signal level / " "flags / ssid\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -2004,14 +2568,125 @@ static int wpa_supplicant_ctrl_iface_scan_results( } +#ifdef CONFIG_MESH + +static int wpa_supplicant_ctrl_iface_mesh_interface_add( + struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len) +{ + char *pos, ifname[IFNAMSIZ + 1]; + + ifname[0] = '\0'; + + pos = os_strstr(cmd, "ifname="); + if (pos) { + pos += 7; + os_strlcpy(ifname, pos, sizeof(ifname)); + } + + if (wpas_mesh_add_interface(wpa_s, ifname, sizeof(ifname)) < 0) + return -1; + + os_strlcpy(reply, ifname, max_len); + return os_strlen(ifname); +} + + +static int wpa_supplicant_ctrl_iface_mesh_group_add( + struct wpa_supplicant *wpa_s, char *cmd) +{ + int id; + struct wpa_ssid *ssid; + + id = atoi(cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_ADD id=%d", id); + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, + "CTRL_IFACE: Could not find network id=%d", id); + return -1; + } + if (ssid->mode != WPAS_MODE_MESH) { + wpa_printf(MSG_DEBUG, + "CTRL_IFACE: Cannot use MESH_GROUP_ADD on a non mesh network"); + return -1; + } + if (ssid->key_mgmt != WPA_KEY_MGMT_NONE && + ssid->key_mgmt != WPA_KEY_MGMT_SAE) { + wpa_printf(MSG_ERROR, + "CTRL_IFACE: key_mgmt for mesh network should be open or SAE"); + return -1; + } + + /* + * TODO: If necessary write our own group_add function, + * for now we can reuse select_network + */ + wpa_supplicant_select_network(wpa_s, ssid); + + return 0; +} + + +static int wpa_supplicant_ctrl_iface_mesh_group_remove( + struct wpa_supplicant *wpa_s, char *cmd) +{ + struct wpa_supplicant *orig; + struct wpa_global *global; + int found = 0; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s", cmd); + + global = wpa_s->global; + orig = wpa_s; + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (os_strcmp(wpa_s->ifname, cmd) == 0) { + found = 1; + break; + } + } + if (!found) { + wpa_printf(MSG_ERROR, + "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s not found", + cmd); + return -1; + } + if (wpa_s->mesh_if_created && wpa_s == orig) { + wpa_printf(MSG_ERROR, + "CTRL_IFACE: MESH_GROUP_REMOVE can't remove itself"); + return -1; + } + + wpa_s->reassociate = 0; + wpa_s->disconnected = 1; + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_cancel_scan(wpa_s); + + /* + * TODO: If necessary write our own group_remove function, + * for now we can reuse deauthenticate + */ + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + + if (wpa_s->mesh_if_created) + wpa_supplicant_remove_iface(global, wpa_s, 0); + + return 0; +} + +#endif /* CONFIG_MESH */ + + static int wpa_supplicant_ctrl_iface_select_network( struct wpa_supplicant *wpa_s, char *cmd) { int id; struct wpa_ssid *ssid; + char *pos; /* cmd: "<network id>" or "any" */ - if (os_strcmp(cmd, "any") == 0) { + if (os_strncmp(cmd, "any", 3) == 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK any"); ssid = NULL; } else { @@ -2031,6 +2706,16 @@ static int wpa_supplicant_ctrl_iface_select_network( } } + pos = os_strstr(cmd, " freq="); + if (pos) { + int *freqs = freq_range_to_channel_list(wpa_s, pos + 6); + if (freqs) { + wpa_s->scan_req = MANUAL_SCAN_REQ; + os_free(wpa_s->manual_scan_freqs); + wpa_s->manual_scan_freqs = freqs; + } + } + wpa_supplicant_select_network(wpa_s, ssid); return 0; @@ -2125,7 +2810,7 @@ static int wpa_supplicant_ctrl_iface_add_network( wpa_config_set_network_defaults(ssid); ret = os_snprintf(buf, buflen, "%d\n", ssid->id); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return -1; return ret; } @@ -2136,18 +2821,14 @@ static int wpa_supplicant_ctrl_iface_remove_network( { int id; struct wpa_ssid *ssid; + int was_disabled; /* cmd: "<network id>" or "all" */ if (os_strcmp(cmd, "all") == 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK all"); - ssid = wpa_s->conf->ssid; - while (ssid) { - struct wpa_ssid *remove_ssid = ssid; - id = ssid->id; - ssid = ssid->next; - wpas_notify_network_removed(wpa_s, remove_ssid); - wpa_config_remove_network(wpa_s->conf, id); - } + if (wpa_s->sched_scanning) + wpa_supplicant_cancel_sched_scan(wpa_s); + eapol_sm_invalidate_cached_session(wpa_s->eapol); if (wpa_s->current_ssid) { #ifdef CONFIG_SME @@ -2155,9 +2836,20 @@ static int wpa_supplicant_ctrl_iface_remove_network( #endif /* CONFIG_SME */ wpa_sm_set_config(wpa_s->wpa, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); } + ssid = wpa_s->conf->ssid; + while (ssid) { + struct wpa_ssid *remove_ssid = ssid; + id = ssid->id; + ssid = ssid->next; + if (wpa_s->last_ssid == remove_ssid) + wpa_s->last_ssid = NULL; + wpas_notify_network_removed(wpa_s, remove_ssid); + wpa_config_remove_network(wpa_s->conf, id); + } return 0; } @@ -2173,6 +2865,9 @@ static int wpa_supplicant_ctrl_iface_remove_network( return -1; } + if (wpa_s->last_ssid == ssid) + wpa_s->last_ssid = NULL; + if (ssid == wpa_s->current_ssid || wpa_s->current_ssid == NULL) { #ifdef CONFIG_SME wpa_s->sme.prev_bssid_set = 0; @@ -2188,16 +2883,59 @@ static int wpa_supplicant_ctrl_iface_remove_network( wpa_sm_set_config(wpa_s->wpa, NULL); eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); } + was_disabled = ssid->disabled; + if (wpa_config_remove_network(wpa_s->conf, id) < 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Not able to remove the " "network id=%d", id); return -1; } + if (!was_disabled && wpa_s->sched_scanning) { + wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to remove " + "network from filters"); + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } + + return 0; +} + + +static int wpa_supplicant_ctrl_iface_update_network( + struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + char *name, char *value) +{ + if (wpa_config_set(ssid, name, value, 0) < 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network " + "variable '%s'", name); + return -1; + } + + if (os_strcmp(name, "bssid") != 0 && + os_strcmp(name, "priority") != 0) + 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 + * or previously used configuration changes. + */ + eapol_sm_invalidate_cached_session(wpa_s->eapol); + } + + if ((os_strcmp(name, "psk") == 0 && + value[0] == '"' && ssid->ssid_len) || + (os_strcmp(name, "ssid") == 0 && ssid->passphrase)) + wpa_config_update_psk(ssid); + else if (os_strcmp(name, "priority") == 0) + wpa_config_update_prio_list(wpa_s->conf); + return 0; } @@ -2205,9 +2943,10 @@ static int wpa_supplicant_ctrl_iface_remove_network( static int wpa_supplicant_ctrl_iface_set_network( struct wpa_supplicant *wpa_s, char *cmd) { - int id; + int id, ret, prev_bssid_set, prev_disabled; struct wpa_ssid *ssid; char *name, *value; + u8 prev_bssid[ETH_ALEN]; /* cmd: "<network id> <variable name> <value>" */ name = os_strchr(cmd, ' '); @@ -2233,32 +2972,21 @@ static int wpa_supplicant_ctrl_iface_set_network( return -1; } - if (wpa_config_set(ssid, name, value, 0) < 0) { - wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network " - "variable '%s'", name); - return -1; - } - - if (os_strcmp(name, "bssid") != 0 && - os_strcmp(name, "priority") != 0) - 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 - * or previously used configuration changes. - */ - eapol_sm_invalidate_cached_session(wpa_s->eapol); - } + prev_bssid_set = ssid->bssid_set; + prev_disabled = ssid->disabled; + os_memcpy(prev_bssid, ssid->bssid, ETH_ALEN); + ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid, name, + value); + if (ret == 0 && + (ssid->bssid_set != prev_bssid_set || + os_memcmp(ssid->bssid, prev_bssid, ETH_ALEN) != 0)) + wpas_notify_network_bssid_set_changed(wpa_s, ssid); - if ((os_strcmp(name, "psk") == 0 && - value[0] == '"' && ssid->ssid_len) || - (os_strcmp(name, "ssid") == 0 && ssid->passphrase)) - wpa_config_update_psk(ssid); - else if (os_strcmp(name, "priority") == 0) - wpa_config_update_prio_list(wpa_s->conf); + if (prev_disabled != ssid->disabled && + (prev_disabled == 2 || ssid->disabled == 2)) + wpas_notify_network_type_changed(wpa_s, ssid); - return 0; + return ret; } @@ -2306,6 +3034,59 @@ static int wpa_supplicant_ctrl_iface_get_network( } +static int wpa_supplicant_ctrl_iface_dup_network( + struct wpa_supplicant *wpa_s, char *cmd) +{ + struct wpa_ssid *ssid_s, *ssid_d; + char *name, *id, *value; + int id_s, id_d, ret; + + /* cmd: "<src network id> <dst network id> <variable name>" */ + id = os_strchr(cmd, ' '); + if (id == NULL) + return -1; + *id++ = '\0'; + + name = os_strchr(id, ' '); + if (name == NULL) + return -1; + *name++ = '\0'; + + id_s = atoi(cmd); + id_d = atoi(id); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: DUP_NETWORK id=%d -> %d name='%s'", + id_s, id_d, name); + + ssid_s = wpa_config_get_network(wpa_s->conf, id_s); + if (ssid_s == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " + "network id=%d", id_s); + return -1; + } + + ssid_d = wpa_config_get_network(wpa_s->conf, id_d); + if (ssid_d == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find " + "network id=%d", id_d); + return -1; + } + + value = wpa_config_get(ssid_s, name); + if (value == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network " + "variable '%s'", name); + return -1; + } + + ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid_d, name, + value); + + os_free(value); + + return ret; +} + + static int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) { @@ -2317,7 +3098,7 @@ static int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s, end = buf + buflen; ret = os_snprintf(pos, end - pos, "cred id / realm / username / domain / imsi\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -2326,9 +3107,9 @@ static int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s, ret = os_snprintf(pos, end - pos, "%d\t%s\t%s\t%s\t%s\n", cred->id, cred->realm ? cred->realm : "", cred->username ? cred->username : "", - cred->domain ? cred->domain : "", + cred->domain ? cred->domain[0] : "", cred->imsi ? cred->imsi : ""); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -2351,8 +3132,10 @@ static int wpa_supplicant_ctrl_iface_add_cred(struct wpa_supplicant *wpa_s, if (cred == NULL) return -1; + wpa_msg(wpa_s, MSG_INFO, CRED_ADDED "%d", cred->id); + ret = os_snprintf(buf, buflen, "%d\n", cred->id); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return -1; return ret; } @@ -2363,19 +3146,32 @@ static int wpas_ctrl_remove_cred(struct wpa_supplicant *wpa_s, { struct wpa_ssid *ssid; char str[20]; + int id; - if (cred == NULL || wpa_config_remove_cred(wpa_s->conf, cred->id) < 0) { + if (cred == NULL) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred"); return -1; } + id = cred->id; + if (wpa_config_remove_cred(wpa_s->conf, id) < 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred"); + return -1; + } + + wpa_msg(wpa_s, MSG_INFO, CRED_REMOVED "%d", id); + /* Remove any network entry created based on the removed credential */ ssid = wpa_s->conf->ssid; while (ssid) { if (ssid->parent_cred == cred) { + int res; + wpa_printf(MSG_DEBUG, "Remove network id %d since it " "used the removed credential", ssid->id); - os_snprintf(str, sizeof(str), "%d", ssid->id); + res = os_snprintf(str, sizeof(str), "%d", ssid->id); + if (os_snprintf_error(sizeof(str), res)) + str[sizeof(str) - 1] = '\0'; ssid = ssid->next; wpa_supplicant_ctrl_iface_remove_network(wpa_s, str); } else @@ -2392,7 +3188,8 @@ static int wpa_supplicant_ctrl_iface_remove_cred(struct wpa_supplicant *wpa_s, int id; struct wpa_cred *cred, *prev; - /* cmd: "<cred id>", "all", or "sp_fqdn=<FQDN>" */ + /* cmd: "<cred id>", "all", "sp_fqdn=<FQDN>", or + * "provisioning_sp=<FQDN> */ if (os_strcmp(cmd, "all") == 0) { wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED all"); cred = wpa_s->conf->cred; @@ -2411,8 +3208,29 @@ static int wpa_supplicant_ctrl_iface_remove_cred(struct wpa_supplicant *wpa_s, while (cred) { prev = cred; cred = cred->next; - if (prev->domain && - os_strcmp(prev->domain, cmd + 8) == 0) + if (prev->domain) { + size_t i; + for (i = 0; i < prev->num_domain; i++) { + if (os_strcmp(prev->domain[i], cmd + 8) + != 0) + continue; + wpas_ctrl_remove_cred(wpa_s, prev); + break; + } + } + } + return 0; + } + + if (os_strncmp(cmd, "provisioning_sp=", 16) == 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED provisioning SP FQDN '%s'", + cmd + 16); + cred = wpa_s->conf->cred; + while (cred) { + prev = cred; + cred = cred->next; + if (prev->provisioning_sp && + os_strcmp(prev->provisioning_sp, cmd + 16) == 0) wpas_ctrl_remove_cred(wpa_s, prev); } return 0; @@ -2463,10 +3281,57 @@ static int wpa_supplicant_ctrl_iface_set_cred(struct wpa_supplicant *wpa_s, return -1; } + wpa_msg(wpa_s, MSG_INFO, CRED_MODIFIED "%d %s", cred->id, name); + return 0; } +static int wpa_supplicant_ctrl_iface_get_cred(struct wpa_supplicant *wpa_s, + char *cmd, char *buf, + size_t buflen) +{ + int id; + size_t res; + struct wpa_cred *cred; + char *name, *value; + + /* cmd: "<cred id> <variable name>" */ + name = os_strchr(cmd, ' '); + if (name == NULL) + return -1; + *name++ = '\0'; + + id = atoi(cmd); + wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CRED id=%d name='%s'", + id, name); + + cred = wpa_config_get_cred(wpa_s->conf, id); + if (cred == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred id=%d", + id); + return -1; + } + + value = wpa_config_get_cred_no_key(cred, name); + if (value == NULL) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get cred variable '%s'", + name); + return -1; + } + + res = os_strlcpy(buf, value, buflen); + if (res >= buflen) { + os_free(value); + return -1; + } + + os_free(value); + + return res; +} + + #ifndef CONFIG_NO_CONFIG_WRITE static int wpa_supplicant_ctrl_iface_save_config(struct wpa_supplicant *wpa_s) { @@ -2492,13 +3357,39 @@ static int wpa_supplicant_ctrl_iface_save_config(struct wpa_supplicant *wpa_s) #endif /* CONFIG_NO_CONFIG_WRITE */ +struct cipher_info { + unsigned int capa; + const char *name; + int group_only; +}; + +static const struct cipher_info ciphers[] = { + { WPA_DRIVER_CAPA_ENC_CCMP_256, "CCMP-256", 0 }, + { WPA_DRIVER_CAPA_ENC_GCMP_256, "GCMP-256", 0 }, + { WPA_DRIVER_CAPA_ENC_CCMP, "CCMP", 0 }, + { WPA_DRIVER_CAPA_ENC_GCMP, "GCMP", 0 }, + { WPA_DRIVER_CAPA_ENC_TKIP, "TKIP", 0 }, + { WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE, "NONE", 0 }, + { WPA_DRIVER_CAPA_ENC_WEP104, "WEP104", 1 }, + { WPA_DRIVER_CAPA_ENC_WEP40, "WEP40", 1 } +}; + +static const struct cipher_info ciphers_group_mgmt[] = { + { WPA_DRIVER_CAPA_ENC_BIP, "AES-128-CMAC", 1 }, + { WPA_DRIVER_CAPA_ENC_BIP_GMAC_128, "BIP-GMAC-128", 1 }, + { WPA_DRIVER_CAPA_ENC_BIP_GMAC_256, "BIP-GMAC-256", 1 }, + { WPA_DRIVER_CAPA_ENC_BIP_CMAC_256, "BIP-CMAC-256", 1 }, +}; + + static int ctrl_iface_get_capability_pairwise(int res, char *strict, struct wpa_driver_capa *capa, char *buf, size_t buflen) { - int ret, first = 1; + int ret; char *pos, *end; size_t len; + unsigned int i; pos = buf; end = pos + buflen; @@ -2512,36 +3403,15 @@ static int ctrl_iface_get_capability_pairwise(int res, char *strict, return len; } - if (capa->enc & WPA_DRIVER_CAPA_ENC_CCMP) { - ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : " "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - first = 0; - } - - if (capa->enc & WPA_DRIVER_CAPA_ENC_GCMP) { - ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : " "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - first = 0; - } - - if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) { - ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - first = 0; - } - - if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { - ret = os_snprintf(pos, end - pos, "%sNONE", first ? "" : " "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - first = 0; + for (i = 0; i < ARRAY_SIZE(ciphers); i++) { + if (!ciphers[i].group_only && capa->enc & ciphers[i].capa) { + ret = os_snprintf(pos, end - pos, "%s%s", + pos == buf ? "" : " ", + ciphers[i].name); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } } return pos - buf; @@ -2552,9 +3422,10 @@ static int ctrl_iface_get_capability_group(int res, char *strict, struct wpa_driver_capa *capa, char *buf, size_t buflen) { - int ret, first = 1; + int ret; char *pos, *end; size_t len; + unsigned int i; pos = buf; end = pos + buflen; @@ -2568,45 +3439,44 @@ static int ctrl_iface_get_capability_group(int res, char *strict, return len; } - if (capa->enc & WPA_DRIVER_CAPA_ENC_CCMP) { - ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : " "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - first = 0; + for (i = 0; i < ARRAY_SIZE(ciphers); i++) { + if (capa->enc & ciphers[i].capa) { + ret = os_snprintf(pos, end - pos, "%s%s", + pos == buf ? "" : " ", + ciphers[i].name); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } } - if (capa->enc & WPA_DRIVER_CAPA_ENC_GCMP) { - ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : " "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - first = 0; - } + return pos - buf; +} - if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) { - ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - first = 0; - } - if (capa->enc & WPA_DRIVER_CAPA_ENC_WEP104) { - ret = os_snprintf(pos, end - pos, "%sWEP104", - first ? "" : " "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - first = 0; - } +static int ctrl_iface_get_capability_group_mgmt(int res, char *strict, + struct wpa_driver_capa *capa, + char *buf, size_t buflen) +{ + int ret; + char *pos, *end; + unsigned int i; - if (capa->enc & WPA_DRIVER_CAPA_ENC_WEP40) { - ret = os_snprintf(pos, end - pos, "%sWEP40", first ? "" : " "); - if (ret < 0 || ret >= end - pos) - return pos - buf; - pos += ret; - first = 0; + pos = buf; + end = pos + buflen; + + if (res < 0) + return 0; + + for (i = 0; i < ARRAY_SIZE(ciphers_group_mgmt); i++) { + if (capa->enc & ciphers_group_mgmt[i].capa) { + ret = os_snprintf(pos, end - pos, "%s%s", + pos == buf ? "" : " ", + ciphers_group_mgmt[i].name); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } } return pos - buf; @@ -2635,14 +3505,14 @@ static int ctrl_iface_get_capability_key_mgmt(int res, char *strict, } ret = os_snprintf(pos, end - pos, "NONE IEEE8021X"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) { ret = os_snprintf(pos, end - pos, " WPA-EAP"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -2650,18 +3520,35 @@ static int ctrl_iface_get_capability_key_mgmt(int res, char *strict, if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { ret = os_snprintf(pos, end - pos, " WPA-PSK"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { ret = os_snprintf(pos, end - pos, " WPA-NONE"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } +#ifdef CONFIG_SUITEB + if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B) { + ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_SUITEB */ +#ifdef CONFIG_SUITEB192 + if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192) { + ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B-192"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_SUITEB192 */ + return pos - buf; } @@ -2670,7 +3557,7 @@ static int ctrl_iface_get_capability_proto(int res, char *strict, struct wpa_driver_capa *capa, char *buf, size_t buflen) { - int ret, first = 1; + int ret; char *pos, *end; size_t len; @@ -2688,31 +3575,32 @@ static int ctrl_iface_get_capability_proto(int res, char *strict, if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { - ret = os_snprintf(pos, end - pos, "%sRSN", first ? "" : " "); - if (ret < 0 || ret >= end - pos) + ret = os_snprintf(pos, end - pos, "%sRSN", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; - first = 0; } if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) { - ret = os_snprintf(pos, end - pos, "%sWPA", first ? "" : " "); - if (ret < 0 || ret >= end - pos) + ret = os_snprintf(pos, end - pos, "%sWPA", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; - first = 0; } return pos - buf; } -static int ctrl_iface_get_capability_auth_alg(int res, char *strict, +static int ctrl_iface_get_capability_auth_alg(struct wpa_supplicant *wpa_s, + int res, char *strict, struct wpa_driver_capa *capa, char *buf, size_t buflen) { - int ret, first = 1; + int ret; char *pos, *end; size_t len; @@ -2729,30 +3617,89 @@ static int ctrl_iface_get_capability_auth_alg(int res, char *strict, } if (capa->auth & (WPA_DRIVER_AUTH_OPEN)) { - ret = os_snprintf(pos, end - pos, "%sOPEN", first ? "" : " "); - if (ret < 0 || ret >= end - pos) + ret = os_snprintf(pos, end - pos, "%sOPEN", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; - first = 0; } if (capa->auth & (WPA_DRIVER_AUTH_SHARED)) { ret = os_snprintf(pos, end - pos, "%sSHARED", - first ? "" : " "); - if (ret < 0 || ret >= end - pos) + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; - first = 0; } if (capa->auth & (WPA_DRIVER_AUTH_LEAP)) { - ret = os_snprintf(pos, end - pos, "%sLEAP", first ? "" : " "); - if (ret < 0 || ret >= end - pos) + ret = os_snprintf(pos, end - pos, "%sLEAP", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + +#ifdef CONFIG_SAE + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) { + ret = os_snprintf(pos, end - pos, "%sSAE", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_SAE */ + + return pos - buf; +} + + +static int ctrl_iface_get_capability_modes(int res, char *strict, + struct wpa_driver_capa *capa, + char *buf, size_t buflen) +{ + int ret; + char *pos, *end; + size_t len; + + pos = buf; + end = pos + buflen; + + if (res < 0) { + if (strict) + return 0; + len = os_strlcpy(buf, "IBSS AP", buflen); + if (len >= buflen) + return -1; + return len; + } + + if (capa->flags & WPA_DRIVER_FLAGS_IBSS) { + ret = os_snprintf(pos, end - pos, "%sIBSS", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; - first = 0; } + if (capa->flags & WPA_DRIVER_FLAGS_AP) { + ret = os_snprintf(pos, end - pos, "%sAP", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + +#ifdef CONFIG_MESH + if (capa->flags & WPA_DRIVER_FLAGS_MESH) { + ret = os_snprintf(pos, end - pos, "%sMESH", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_MESH */ + return pos - buf; } @@ -2785,7 +3732,7 @@ static int ctrl_iface_get_capability_channels(struct wpa_supplicant *wpa_s, continue; } ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:", hmode); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; chnl = wpa_s->hw.modes[j].channels; @@ -2793,12 +3740,69 @@ static int ctrl_iface_get_capability_channels(struct wpa_supplicant *wpa_s, if (chnl[i].flag & HOSTAPD_CHAN_DISABLED) continue; ret = os_snprintf(pos, end - pos, " %d", chnl[i].chan); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + return pos - buf; +} + + +static int ctrl_iface_get_capability_freq(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) +{ + struct hostapd_channel_data *chnl; + int ret, i, j; + char *pos, *end, *hmode; + + pos = buf; + end = pos + buflen; + + for (j = 0; j < wpa_s->hw.num_modes; j++) { + switch (wpa_s->hw.modes[j].mode) { + case HOSTAPD_MODE_IEEE80211B: + hmode = "B"; + break; + case HOSTAPD_MODE_IEEE80211G: + hmode = "G"; + break; + case HOSTAPD_MODE_IEEE80211A: + hmode = "A"; + break; + case HOSTAPD_MODE_IEEE80211AD: + hmode = "AD"; + break; + default: + continue; + } + ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:\n", + hmode); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + chnl = wpa_s->hw.modes[j].channels; + for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) { + if (chnl[i].flag & HOSTAPD_CHAN_DISABLED) + continue; + ret = os_snprintf(pos, end - pos, " %d = %d MHz%s%s\n", + chnl[i].chan, chnl[i].freq, + chnl[i].flag & HOSTAPD_CHAN_NO_IR ? + " (NO_IR)" : "", + chnl[i].flag & HOSTAPD_CHAN_RADAR ? + " (DFS)" : ""); + + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + ret = os_snprintf(pos, end - pos, "\n"); + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -2845,6 +3849,10 @@ static int wpa_supplicant_ctrl_iface_get_capability( return ctrl_iface_get_capability_group(res, strict, &capa, buf, buflen); + if (os_strcmp(field, "group_mgmt") == 0) + return ctrl_iface_get_capability_group_mgmt(res, strict, &capa, + buf, buflen); + if (os_strcmp(field, "key_mgmt") == 0) return ctrl_iface_get_capability_key_mgmt(res, strict, &capa, buf, buflen); @@ -2854,12 +3862,33 @@ static int wpa_supplicant_ctrl_iface_get_capability( buf, buflen); if (os_strcmp(field, "auth_alg") == 0) - return ctrl_iface_get_capability_auth_alg(res, strict, &capa, - buf, buflen); + return ctrl_iface_get_capability_auth_alg(wpa_s, res, strict, + &capa, buf, buflen); + + if (os_strcmp(field, "modes") == 0) + return ctrl_iface_get_capability_modes(res, strict, &capa, + buf, buflen); if (os_strcmp(field, "channels") == 0) return ctrl_iface_get_capability_channels(wpa_s, buf, buflen); + if (os_strcmp(field, "freq") == 0) + return ctrl_iface_get_capability_freq(wpa_s, buf, buflen); + +#ifdef CONFIG_TDLS + if (os_strcmp(field, "tdls") == 0) + return ctrl_iface_get_capability_tdls(wpa_s, buf, buflen); +#endif /* CONFIG_TDLS */ + +#ifdef CONFIG_ERP + if (os_strcmp(field, "erp") == 0) { + res = os_snprintf(buf, buflen, "ERP"); + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } +#endif /* CONFIG_EPR */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", field); @@ -2880,20 +3909,20 @@ static char * anqp_add_hex(char *pos, char *end, const char *title, return start; ret = os_snprintf(pos, end - pos, "%s=", title); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) 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) + if (os_snprintf_error(end - pos, ret)) return start; pos += ret; } ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return start; pos += ret; @@ -2915,7 +3944,7 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (mask & WPA_BSS_MASK_ID) { ret = os_snprintf(pos, end - pos, "id=%u\n", bss->id); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } @@ -2923,14 +3952,14 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (mask & WPA_BSS_MASK_BSSID) { ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n", MAC2STR(bss->bssid)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_FREQ) { ret = os_snprintf(pos, end - pos, "freq=%d\n", bss->freq); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } @@ -2938,7 +3967,7 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (mask & WPA_BSS_MASK_BEACON_INT) { ret = os_snprintf(pos, end - pos, "beacon_int=%d\n", bss->beacon_int); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } @@ -2946,28 +3975,28 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (mask & WPA_BSS_MASK_CAPABILITIES) { ret = os_snprintf(pos, end - pos, "capabilities=0x%04x\n", bss->caps); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_QUAL) { ret = os_snprintf(pos, end - pos, "qual=%d\n", bss->qual); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_NOISE) { ret = os_snprintf(pos, end - pos, "noise=%d\n", bss->noise); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_LEVEL) { ret = os_snprintf(pos, end - pos, "level=%d\n", bss->level); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } @@ -2975,45 +4004,45 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (mask & WPA_BSS_MASK_TSF) { ret = os_snprintf(pos, end - pos, "tsf=%016llu\n", (unsigned long long) bss->tsf); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_AGE) { - struct os_time now; + struct os_reltime now; - os_get_time(&now); + os_get_reltime(&now); ret = os_snprintf(pos, end - pos, "age=%d\n", (int) (now.sec - bss->last_update.sec)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_IE) { ret = os_snprintf(pos, end - pos, "ie="); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; ie = (const u8 *) (bss + 1); for (i = 0; i < bss->ie_len; i++) { ret = os_snprintf(pos, end - pos, "%02x", *ie++); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } if (mask & WPA_BSS_MASK_FLAGS) { ret = os_snprintf(pos, end - pos, "flags="); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; @@ -3028,39 +4057,66 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *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) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } - if (bss->caps & IEEE80211_CAP_IBSS) { - ret = os_snprintf(pos, end - pos, "[IBSS]"); - if (ret < 0 || ret >= end - pos) + if (bss_is_dmg(bss)) { + const char *s; + ret = os_snprintf(pos, end - pos, "[DMG]"); + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; - } - if (bss->caps & IEEE80211_CAP_ESS) { - ret = os_snprintf(pos, end - pos, "[ESS]"); - if (ret < 0 || ret >= end - pos) + switch (bss->caps & IEEE80211_CAP_DMG_MASK) { + case IEEE80211_CAP_DMG_IBSS: + s = "[IBSS]"; + break; + case IEEE80211_CAP_DMG_AP: + s = "[ESS]"; + break; + case IEEE80211_CAP_DMG_PBSS: + s = "[PBSS]"; + break; + default: + s = ""; + break; + } + ret = os_snprintf(pos, end - pos, "%s", s); + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; + } else { + if (bss->caps & IEEE80211_CAP_IBSS) { + ret = os_snprintf(pos, end - pos, "[IBSS]"); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + } + if (bss->caps & IEEE80211_CAP_ESS) { + ret = os_snprintf(pos, end - pos, "[ESS]"); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + } } - if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE)) { + if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) || + wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) { ret = os_snprintf(pos, end - pos, "[P2P]"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } #ifdef CONFIG_HS20 if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) { ret = os_snprintf(pos, end - pos, "[HS20]"); - if (ret < 0 || ret >= end - pos) - return -1; + if (os_snprintf_error(end - pos, ret)) + return 0; pos += ret; } #endif /* CONFIG_HS20 */ ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } @@ -3068,7 +4124,7 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (mask & WPA_BSS_MASK_SSID) { ret = os_snprintf(pos, end - pos, "ssid=%s\n", wpa_ssid_txt(bss->ssid, bss->ssid_len)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return 0; pos += ret; } @@ -3101,8 +4157,10 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, WFD_IE_VENDOR_TYPE); if (wfd) { ret = os_snprintf(pos, end - pos, "wfd_subelems="); - if (ret < 0 || ret >= end - pos) - return pos - buf; + if (os_snprintf_error(end - pos, ret)) { + wpabuf_free(wfd); + return 0; + } pos += ret; pos += wpa_snprintf_hex(pos, end - pos, @@ -3111,8 +4169,8 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, wpabuf_free(wfd); ret = os_snprintf(pos, end - pos, "\n"); - if (ret < 0 || ret >= end - pos) - return pos - buf; + if (os_snprintf_error(end - pos, ret)) + return 0; pos += ret; } } @@ -3121,6 +4179,8 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, #ifdef CONFIG_INTERWORKING if ((mask & WPA_BSS_MASK_INTERNETW) && bss->anqp) { struct wpa_bss_anqp *anqp = bss->anqp; + pos = anqp_add_hex(pos, end, "anqp_capability_list", + anqp->capability_list); pos = anqp_add_hex(pos, end, "anqp_venue_name", anqp->venue_name); pos = anqp_add_hex(pos, end, "anqp_network_auth_type", @@ -3135,16 +4195,54 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, pos = anqp_add_hex(pos, end, "anqp_domain_name", anqp->domain_name); #ifdef CONFIG_HS20 + pos = anqp_add_hex(pos, end, "hs20_capability_list", + anqp->hs20_capability_list); pos = anqp_add_hex(pos, end, "hs20_operator_friendly_name", anqp->hs20_operator_friendly_name); pos = anqp_add_hex(pos, end, "hs20_wan_metrics", anqp->hs20_wan_metrics); pos = anqp_add_hex(pos, end, "hs20_connection_capability", anqp->hs20_connection_capability); + pos = anqp_add_hex(pos, end, "hs20_operating_class", + anqp->hs20_operating_class); + pos = anqp_add_hex(pos, end, "hs20_osu_providers_list", + anqp->hs20_osu_providers_list); #endif /* CONFIG_HS20 */ } #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_MESH + if (mask & WPA_BSS_MASK_MESH_SCAN) { + ie = (const u8 *) (bss + 1); + ret = wpas_mesh_scan_result_text(ie, bss->ie_len, pos, end); + if (ret < 0 || ret >= end - pos) + return 0; + pos += ret; + } +#endif /* CONFIG_MESH */ + + if (mask & WPA_BSS_MASK_SNR) { + ret = os_snprintf(pos, end - pos, "snr=%d\n", bss->snr); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + } + + if (mask & WPA_BSS_MASK_EST_THROUGHPUT) { + ret = os_snprintf(pos, end - pos, "est_throughput=%d\n", + bss->est_throughput); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + } + + if (mask & WPA_BSS_MASK_DELIM) { + ret = os_snprintf(pos, end - pos, "====\n"); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + } + return pos - buf; } @@ -3160,7 +4258,7 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, struct dl_list *next; int ret = 0; int len; - char *ctmp; + char *ctmp, *end = buf + buflen; unsigned long mask = WPA_BSS_MASK_ALL; if (os_strncmp(cmd, "RANGE=", 6) == 0) { @@ -3178,10 +4276,17 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, return 0; } - id1 = atoi(cmd + 6); - bss = wpa_bss_get_id(wpa_s, id1); - id2 = atoi(ctmp + 1); - if (id2 == 0) + if (*(cmd + 6) == '-') + id1 = 0; + else + id1 = atoi(cmd + 6); + ctmp++; + if (*ctmp >= '0' && *ctmp <= '9') + id2 = atoi(ctmp); + else + id2 = (unsigned int) -1; + bss = wpa_bss_get_id_range(wpa_s, id1, id2); + if (id2 == (unsigned int) -1) bsslast = dl_list_last(&wpa_s->bss_id, struct wpa_bss, list_id); @@ -3203,8 +4308,10 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, } } } - } else if (os_strcmp(cmd, "FIRST") == 0) + } else if (os_strncmp(cmd, "FIRST", 5) == 0) bss = dl_list_first(&wpa_s->bss_id, struct wpa_bss, list_id); + else if (os_strncmp(cmd, "LAST", 4) == 0) + bss = dl_list_last(&wpa_s->bss_id, struct wpa_bss, list_id); else if (os_strncmp(cmd, "ID-", 3) == 0) { i = atoi(cmd + 3); bss = wpa_bss_get_id(wpa_s, i); @@ -3257,8 +4364,21 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, ret += len; buf += len; buflen -= len; - if (bss == bsslast) + if (bss == bsslast) { + if ((mask & WPA_BSS_MASK_DELIM) && len && + (bss == dl_list_last(&wpa_s->bss_id, + struct wpa_bss, list_id))) { + int res; + + res = os_snprintf(buf - 5, end - buf + 5, + "####\n"); + if (os_snprintf_error(end - buf + 5, res)) { + wpa_printf(MSG_DEBUG, + "Could not add end delim"); + } + } break; + } next = bss->list_id.next; if (next == &wpa_s->bss_id) break; @@ -3301,7 +4421,7 @@ static int wpa_supplicant_ctrl_iface_bss_expire_count( } -static int wpa_supplicant_ctrl_iface_bss_flush( +static void wpa_supplicant_ctrl_iface_bss_flush( struct wpa_supplicant *wpa_s, char *cmd) { int flush_age = atoi(cmd); @@ -3310,10 +4430,10 @@ static int wpa_supplicant_ctrl_iface_bss_flush( wpa_bss_flush(wpa_s); else wpa_bss_flush_by_age(wpa_s, flush_age); - return 0; } +#ifdef CONFIG_TESTING_OPTIONS static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s) { wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication"); @@ -3335,6 +4455,7 @@ static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s) MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); wpa_sm_drop_sa(wpa_s->wpa); } +#endif /* CONFIG_TESTING_OPTIONS */ static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s, @@ -3355,7 +4476,13 @@ static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM " MACSTR, MAC2STR(bssid)); - bss = wpa_bss_get_bssid(wpa_s, bssid); + if (!ssid) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: No network " + "configuration known for the target AP"); + return -1; + } + + bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len); if (!bss) { wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: Target AP not found " "from BSS table"); @@ -3367,12 +4494,6 @@ static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s, * allow roaming to other networks */ - if (!ssid) { - wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: No network " - "configuration known for the target AP"); - return -1; - } - wpa_s->reassociate = 1; wpa_supplicant_connect(wpa_s, bss, ssid); @@ -3387,9 +4508,18 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd) unsigned int timeout = atoi(cmd); enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL; u8 dev_id[ETH_ALEN], *_dev_id = NULL; + u8 dev_type[WPS_DEV_TYPE_LEN], *_dev_type = NULL; char *pos; unsigned int search_delay; + const char *_seek[P2P_MAX_QUERY_HASH + 1], **seek = NULL; + u8 seek_count = 0; + int freq = 0; + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { + wpa_dbg(wpa_s, MSG_INFO, + "Reject P2P_FIND since interface is disabled"); + return -1; + } if (os_strstr(cmd, "type=social")) type = P2P_FIND_ONLY_SOCIAL; else if (os_strstr(cmd, "type=progressive")) @@ -3403,6 +4533,14 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd) _dev_id = dev_id; } + pos = os_strstr(cmd, "dev_type="); + if (pos) { + pos += 9; + if (wps_dev_type_str2bin(pos, dev_type) < 0) + return -1; + _dev_type = dev_type; + } + pos = os_strstr(cmd, "delay="); if (pos) { pos += 6; @@ -3410,8 +4548,181 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd) } else search_delay = wpas_p2p_search_delay(wpa_s); - return wpas_p2p_find(wpa_s, timeout, type, 0, NULL, _dev_id, - search_delay); + /* Must be searched for last, because it adds nul termination */ + pos = os_strstr(cmd, " seek="); + while (pos && seek_count < P2P_MAX_QUERY_HASH + 1) { + char *term; + + term = os_strchr(pos + 1, ' '); + _seek[seek_count++] = pos + 6; + seek = _seek; + pos = os_strstr(pos + 6, " seek="); + + if (term) + *term = '\0'; + } + if (seek_count > P2P_MAX_QUERY_HASH) { + seek[0] = NULL; + seek_count = 1; + } + + pos = os_strstr(cmd, "freq="); + if (pos) { + pos += 5; + freq = atoi(pos); + if (freq <= 0) + return -1; + } + + return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type, + _dev_id, search_delay, seek_count, seek, freq); +} + + +static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd) +{ + struct p2ps_provision *p2ps_prov; + char *pos; + size_t info_len = 0; + char *info = NULL; + u8 role = P2PS_SETUP_NONE; + long long unsigned val; + + pos = os_strstr(cmd, "info="); + if (pos) { + pos += 5; + info_len = os_strlen(pos); + + if (info_len) { + info = os_malloc(info_len + 1); + if (info) { + info_len = utf8_unescape(pos, info_len, + info, info_len + 1); + } else + info_len = 0; + } + } + + p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + info_len + 1); + if (p2ps_prov == NULL) { + os_free(info); + return NULL; + } + + if (info) { + os_memcpy(p2ps_prov->info, info, info_len); + p2ps_prov->info[info_len] = '\0'; + os_free(info); + } + + pos = os_strstr(cmd, "status="); + if (pos) + p2ps_prov->status = atoi(pos + 7); + else + p2ps_prov->status = -1; + + pos = os_strstr(cmd, "adv_id="); + if (!pos || sscanf(pos + 7, "%llx", &val) != 1 || val > 0xffffffffULL) + goto invalid_args; + p2ps_prov->adv_id = val; + + pos = os_strstr(cmd, "method="); + if (pos) + p2ps_prov->method = strtol(pos + 7, NULL, 16); + else + p2ps_prov->method = 0; + + pos = os_strstr(cmd, "session="); + if (!pos || sscanf(pos + 8, "%llx", &val) != 1 || val > 0xffffffffULL) + goto invalid_args; + p2ps_prov->session_id = val; + + pos = os_strstr(cmd, "adv_mac="); + if (!pos || hwaddr_aton(pos + 8, p2ps_prov->adv_mac)) + goto invalid_args; + + pos = os_strstr(cmd, "session_mac="); + if (!pos || hwaddr_aton(pos + 12, p2ps_prov->session_mac)) + goto invalid_args; + + /* force conncap with tstCap (no sanity checks) */ + pos = os_strstr(cmd, "tstCap="); + if (pos) { + role = strtol(pos + 7, NULL, 16); + } else { + pos = os_strstr(cmd, "role="); + if (pos) { + role = strtol(pos + 5, NULL, 16); + if (role != P2PS_SETUP_CLIENT && + role != P2PS_SETUP_GROUP_OWNER) + role = P2PS_SETUP_NONE; + } + } + p2ps_prov->role = role; + + return p2ps_prov; + +invalid_args: + os_free(p2ps_prov); + return NULL; +} + + +static int p2p_ctrl_asp_provision_resp(struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 addr[ETH_ALEN]; + struct p2ps_provision *p2ps_prov; + char *pos; + + /* <addr> id=<adv_id> [role=<conncap>] [info=<infodata>] */ + + wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd); + + if (hwaddr_aton(cmd, addr)) + return -1; + + pos = cmd + 17; + if (*pos != ' ') + return -1; + + p2ps_prov = p2p_parse_asp_provision_cmd(pos); + if (!p2ps_prov) + return -1; + + if (p2ps_prov->status < 0) { + os_free(p2ps_prov); + return -1; + } + + return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP, + p2ps_prov); +} + + +static int p2p_ctrl_asp_provision(struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 addr[ETH_ALEN]; + struct p2ps_provision *p2ps_prov; + char *pos; + + /* <addr> id=<adv_id> adv_mac=<adv_mac> conncap=<conncap> + * session=<ses_id> mac=<ses_mac> [info=<infodata>] + */ + + wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd); + if (hwaddr_aton(cmd, addr)) + return -1; + + pos = cmd + 17; + if (*pos != ' ') + return -1; + + p2ps_prov = p2p_parse_asp_provision_cmd(pos); + if (!p2ps_prov) + return -1; + + return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP, + p2ps_prov); } @@ -3431,12 +4742,20 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, int go_intent = -1; int freq = 0; int pd; - int ht40; + int ht40, vht; + + if (!wpa_s->global->p2p_init_wpa_s) + return -1; + if (wpa_s->global->p2p_init_wpa_s != wpa_s) { + wpa_dbg(wpa_s, MSG_DEBUG, "Direct P2P_CONNECT command to %s", + wpa_s->global->p2p_init_wpa_s->ifname); + wpa_s = wpa_s->global->p2p_init_wpa_s; + } - /* <addr> <"pbc" | "pin" | PIN> [label|display|keypad] + /* <addr> <"pbc" | "pin" | PIN> [label|display|keypad|p2ps] * [persistent|persistent=<network id>] * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc] - * [ht40] */ + * [ht40] [vht] [auto] */ if (hwaddr_aton(cmd, addr)) return -1; @@ -3464,7 +4783,9 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, auth = os_strstr(pos, " auth") != NULL; automatic = os_strstr(pos, " auto") != NULL; pd = os_strstr(pos, " provdisc") != NULL; - ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40; + vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht; + ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 || + vht; pos2 = os_strstr(pos, " go_intent="); if (pos2) { @@ -3495,6 +4816,8 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, *pos++ = '\0'; if (os_strncmp(pos, "display", 7) == 0) wps_method = WPS_PIN_DISPLAY; + else if (os_strncmp(pos, "p2ps", 4) == 0) + wps_method = WPS_P2PS; } if (!wps_pin_str_valid(pin)) { os_memcpy(buf, "FAIL-INVALID-PIN\n", 17); @@ -3505,7 +4828,7 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, persistent_group, automatic, join, auth, go_intent, freq, persistent_id, pd, - ht40); + ht40, vht); if (new_pin == -2) { os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25); return 25; @@ -3518,7 +4841,7 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, 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) + if (os_snprintf_error(buflen, ret)) return -1; return ret; } @@ -3531,6 +4854,11 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd, static int p2p_ctrl_listen(struct wpa_supplicant *wpa_s, char *cmd) { unsigned int timeout = atoi(cmd); + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { + wpa_dbg(wpa_s, MSG_INFO, + "Reject P2P_LISTEN since interface is disabled"); + return -1; + } return wpas_p2p_listen(wpa_s, timeout); } @@ -3556,7 +4884,7 @@ static int p2p_ctrl_prov_disc(struct wpa_supplicant *wpa_s, char *cmd) else if (os_strstr(pos, " auto") != NULL) use = WPAS_P2P_PD_AUTO; - return wpas_p2p_prov_disc(wpa_s, addr, pos, use); + return wpas_p2p_prov_disc(wpa_s, addr, pos, use, NULL); } @@ -3609,6 +4937,40 @@ static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd, } else if (os_strncmp(pos, "wifi-display ", 13) == 0) { ref = wpas_p2p_sd_request_wifi_display(wpa_s, dst, pos + 13); #endif /* CONFIG_WIFI_DISPLAY */ + } else if (os_strncmp(pos, "asp ", 4) == 0) { + char *svc_str; + char *svc_info = NULL; + u32 id; + + pos += 4; + if (sscanf(pos, "%x", &id) != 1 || id > 0xff) + return -1; + + pos = os_strchr(pos, ' '); + if (pos == NULL || pos[1] == '\0' || pos[1] == ' ') + return -1; + + svc_str = pos + 1; + + pos = os_strchr(svc_str, ' '); + + if (pos) + *pos++ = '\0'; + + /* All remaining data is the svc_info string */ + if (pos && pos[0] && pos[0] != ' ') { + len = os_strlen(pos); + + /* Unescape in place */ + len = utf8_unescape(pos, len, pos, len); + if (len > 0xff) + return -1; + + svc_info = pos; + } + + ref = wpas_p2p_sd_request_asp(wpa_s, dst, (u8) id, + svc_str, svc_info); } else { len = os_strlen(pos); if (len & 1) @@ -3628,7 +4990,7 @@ static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd, if (ref == 0) return -1; res = os_snprintf(buf, buflen, "%llx", (long long unsigned) ref); - if (res < 0 || (unsigned) res >= buflen) + if (os_snprintf_error(buflen, res)) return -1; return res; } @@ -3771,6 +5133,106 @@ static int p2p_ctrl_service_add_upnp(struct wpa_supplicant *wpa_s, char *cmd) } +static int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s, + u8 replace, char *cmd) +{ + char *pos; + char *adv_str; + u32 auto_accept, adv_id, svc_state, config_methods; + char *svc_info = NULL; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + /* Auto-Accept value is mandatory, and must be one of the + * single values (0, 1, 2, 4) */ + auto_accept = atoi(cmd); + switch (auto_accept) { + case P2PS_SETUP_NONE: /* No auto-accept */ + case P2PS_SETUP_NEW: + case P2PS_SETUP_CLIENT: + case P2PS_SETUP_GROUP_OWNER: + break; + default: + return -1; + } + + /* Advertisement ID is mandatory */ + cmd = pos; + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + /* Handle Adv_ID == 0 (wildcard "org.wi-fi.wfds") internally. */ + if (sscanf(cmd, "%x", &adv_id) != 1 || adv_id == 0) + return -1; + + /* Only allow replacements if exist, and adds if not */ + if (wpas_p2p_service_p2ps_id_exists(wpa_s, adv_id)) { + if (!replace) + return -1; + } else { + if (replace) + return -1; + } + + /* svc_state between 0 - 0xff is mandatory */ + if (sscanf(pos, "%x", &svc_state) != 1 || svc_state > 0xff) + return -1; + + pos = os_strchr(pos, ' '); + if (pos == NULL) + return -1; + + /* config_methods is mandatory */ + pos++; + if (sscanf(pos, "%x", &config_methods) != 1) + return -1; + + if (!(config_methods & + (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS))) + return -1; + + pos = os_strchr(pos, ' '); + if (pos == NULL) + return -1; + + pos++; + adv_str = pos; + + /* Advertisement string is mandatory */ + if (!pos[0] || pos[0] == ' ') + return -1; + + /* Terminate svc string */ + pos = os_strchr(pos, ' '); + if (pos != NULL) + *pos++ = '\0'; + + /* Service and Response Information are optional */ + if (pos && pos[0]) { + size_t len; + + /* Note the bare ' included, which cannot exist legally + * in unescaped string. */ + svc_info = os_strstr(pos, "svc_info='"); + + if (svc_info) { + svc_info += 9; + len = os_strlen(svc_info); + utf8_unescape(svc_info, len, svc_info, len); + } + } + + return wpas_p2p_service_add_asp(wpa_s, auto_accept, adv_id, adv_str, + (u8) svc_state, (u16) config_methods, + svc_info); +} + + static int p2p_ctrl_service_add(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; @@ -3784,6 +5246,8 @@ static int p2p_ctrl_service_add(struct wpa_supplicant *wpa_s, char *cmd) return p2p_ctrl_service_add_bonjour(wpa_s, pos); if (os_strcmp(cmd, "upnp") == 0) return p2p_ctrl_service_add_upnp(wpa_s, pos); + if (os_strcmp(cmd, "asp") == 0) + return p2p_ctrl_service_add_asp(wpa_s, 0, pos); wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd); return -1; } @@ -3831,6 +5295,17 @@ static int p2p_ctrl_service_del_upnp(struct wpa_supplicant *wpa_s, char *cmd) } +static int p2p_ctrl_service_del_asp(struct wpa_supplicant *wpa_s, char *cmd) +{ + u32 adv_id; + + if (sscanf(cmd, "%x", &adv_id) != 1) + return -1; + + return wpas_p2p_service_del_asp(wpa_s, adv_id); +} + + static int p2p_ctrl_service_del(struct wpa_supplicant *wpa_s, char *cmd) { char *pos; @@ -3844,6 +5319,25 @@ static int p2p_ctrl_service_del(struct wpa_supplicant *wpa_s, char *cmd) return p2p_ctrl_service_del_bonjour(wpa_s, pos); if (os_strcmp(cmd, "upnp") == 0) return p2p_ctrl_service_del_upnp(wpa_s, pos); + if (os_strcmp(cmd, "asp") == 0) + return p2p_ctrl_service_del_asp(wpa_s, pos); + wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd); + return -1; +} + + +static int p2p_ctrl_service_replace(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + if (os_strcmp(cmd, "asp") == 0) + return p2p_ctrl_service_add_asp(wpa_s, 1, pos); + wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd); return -1; } @@ -3868,8 +5362,8 @@ static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd) int id; struct wpa_ssid *ssid; u8 *_peer = NULL, peer[ETH_ALEN]; - int freq = 0; - int ht40; + int freq = 0, pref_freq = 0; + int ht40, vht; id = atoi(cmd); pos = os_strstr(cmd, " peer="); @@ -3895,9 +5389,20 @@ static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd) return -1; } - ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40; + pos = os_strstr(cmd, " pref="); + if (pos) { + pos += 6; + pref_freq = atoi(pos); + if (pref_freq <= 0) + return -1; + } + + vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht; + ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 || + vht; - return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, ht40); + return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, ht40, vht, + pref_freq); } @@ -3944,7 +5449,8 @@ static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd) static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, - char *cmd, int freq, int ht40) + char *cmd, int freq, int ht40, + int vht) { int id; struct wpa_ssid *ssid; @@ -3958,31 +5464,34 @@ static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s, return -1; } - return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, ht40); + return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, ht40, vht, + NULL, 0); } static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) { - int freq = 0, ht40; + int freq = 0, ht40, vht; char *pos; pos = os_strstr(cmd, "freq="); if (pos) freq = atoi(pos + 5); - ht40 = (os_strstr(cmd, "ht40") != NULL) || wpa_s->conf->p2p_go_ht40; + vht = (os_strstr(cmd, "vht") != NULL) || wpa_s->conf->p2p_go_vht; + ht40 = (os_strstr(cmd, "ht40") != NULL) || wpa_s->conf->p2p_go_ht40 || + vht; if (os_strncmp(cmd, "persistent=", 11) == 0) return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq, - ht40); + ht40, vht); if (os_strcmp(cmd, "persistent") == 0 || os_strncmp(cmd, "persistent ", 11) == 0) - return wpas_p2p_group_add(wpa_s, 1, freq, ht40); + return wpas_p2p_group_add(wpa_s, 1, freq, ht40, vht); if (os_strncmp(cmd, "freq=", 5) == 0) - return wpas_p2p_group_add(wpa_s, 0, freq, ht40); + return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht); if (ht40) - return wpas_p2p_group_add(wpa_s, 0, freq, ht40); + return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht); wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P_GROUP_ADD parameters '%s'", cmd); @@ -4049,7 +5558,7 @@ static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd, info->dev_capab, info->group_capab, info->level); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; @@ -4060,7 +5569,7 @@ static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd, res = os_snprintf(pos, end - pos, "sec_dev_type=%s\n", wps_dev_type_bin2str(t, devtype, sizeof(devtype))); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } @@ -4068,7 +5577,7 @@ static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd, 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) + if (os_snprintf_error(end - pos, res)) return pos - buf; pos += res; } @@ -4078,6 +5587,22 @@ static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd, return pos - buf; pos += res; + if (info->vendor_elems) { + res = os_snprintf(pos, end - pos, "vendor_elems="); + if (os_snprintf_error(end - pos, res)) + return pos - buf; + pos += res; + + pos += wpa_snprintf_hex(pos, end - pos, + wpabuf_head(info->vendor_elems), + wpabuf_len(info->vendor_elems)); + + res = os_snprintf(pos, end - pos, "\n"); + if (os_snprintf_error(end - pos, res)) + return pos - buf; + pos += res; + } + return pos - buf; } @@ -4085,48 +5610,21 @@ static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd, static int p2p_ctrl_disallow_freq(struct wpa_supplicant *wpa_s, const char *param) { - struct wpa_freq_range *freq = NULL, *n; - unsigned int count = 0, i; - const char *pos, *pos2, *pos3; + unsigned int i; if (wpa_s->global->p2p == NULL) return -1; - /* - * param includes comma separated frequency range. - * For example: 2412-2432,2462,5000-6000 - */ - pos = param; - while (pos && pos[0]) { - n = os_realloc_array(freq, count + 1, - sizeof(struct wpa_freq_range)); - if (n == NULL) { - os_free(freq); - return -1; - } - freq = n; - freq[count].min = atoi(pos); - pos2 = os_strchr(pos, '-'); - pos3 = os_strchr(pos, ','); - if (pos2 && (!pos3 || pos2 < pos3)) { - pos2++; - freq[count].max = atoi(pos2); - } else - freq[count].max = freq[count].min; - pos = pos3; - if (pos) - pos++; - count++; - } + if (freq_range_list_parse(&wpa_s->global->p2p_disallow_freq, param) < 0) + return -1; - for (i = 0; i < count; i++) { + for (i = 0; i < wpa_s->global->p2p_disallow_freq.num; i++) { + struct wpa_freq_range *freq; + freq = &wpa_s->global->p2p_disallow_freq.range[i]; wpa_printf(MSG_DEBUG, "P2P: Disallowed frequency range %u-%u", - freq[i].min, freq[i].max); + freq->min, freq->max); } - os_free(wpa_s->global->p2p_disallow_freq); - wpa_s->global->p2p_disallow_freq = freq; - wpa_s->global->num_p2p_disallow_freq = count; wpas_p2p_update_channel_list(wpa_s); return 0; } @@ -4157,7 +5655,7 @@ static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd) if (os_strcmp(cmd, "listen_channel") == 0) { return p2p_set_listen_channel(wpa_s->global->p2p, 81, - atoi(param)); + atoi(param), 1); } if (os_strcmp(cmd, "ssid_postfix") == 0) { @@ -4317,6 +5815,21 @@ static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd) max_disc_int, max_disc_tu); } + if (os_strcmp(cmd, "per_sta_psk") == 0) { + wpa_s->global->p2p_per_sta_psk = !!atoi(param); + return 0; + } + +#ifdef CONFIG_WPS_NFC + if (os_strcmp(cmd, "nfc_tag") == 0) + return wpas_p2p_nfc_tag_enabled(wpa_s, !!atoi(param)); +#endif /* CONFIG_WPS_NFC */ + + if (os_strcmp(cmd, "disable_ip_addr_req") == 0) { + wpa_s->p2p_disable_ip_addr_req = !!atoi(param); + return 0; + } + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'", cmd); @@ -4324,6 +5837,16 @@ static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd) } +static void p2p_ctrl_flush(struct wpa_supplicant *wpa_s) +{ + os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); + wpa_s->force_long_sd = 0; + wpas_p2p_stop_find(wpa_s); + if (wpa_s->global->p2p) + p2p_flush(wpa_s->global->p2p); +} + + static int p2p_ctrl_presence_req(struct wpa_supplicant *wpa_s, char *cmd) { char *pos, *pos2; @@ -4373,11 +5896,92 @@ static int p2p_ctrl_ext_listen(struct wpa_supplicant *wpa_s, char *cmd) return wpas_p2p_ext_listen(wpa_s, period, interval); } + +static int p2p_ctrl_remove_client(struct wpa_supplicant *wpa_s, const char *cmd) +{ + const char *pos; + u8 peer[ETH_ALEN]; + int iface_addr = 0; + + pos = cmd; + if (os_strncmp(pos, "iface=", 6) == 0) { + iface_addr = 1; + pos += 6; + } + if (hwaddr_aton(pos, peer)) + return -1; + + wpas_p2p_remove_client(wpa_s, peer, iface_addr); + return 0; +} + #endif /* CONFIG_P2P */ +static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s, char *val) +{ + struct wpa_freq_range_list ranges; + int *freqs = NULL; + struct hostapd_hw_modes *mode; + u16 i; + + if (wpa_s->hw.modes == NULL) + return NULL; + + os_memset(&ranges, 0, sizeof(ranges)); + if (freq_range_list_parse(&ranges, val) < 0) + return NULL; + + for (i = 0; i < wpa_s->hw.num_modes; i++) { + int j; + + mode = &wpa_s->hw.modes[i]; + for (j = 0; j < mode->num_channels; j++) { + unsigned int freq; + + if (mode->channels[j].flag & HOSTAPD_CHAN_DISABLED) + continue; + + freq = mode->channels[j].freq; + if (!freq_range_list_includes(&ranges, freq)) + continue; + + int_array_add_unique(&freqs, freq); + } + } + + os_free(ranges.range); + return freqs; +} + + #ifdef CONFIG_INTERWORKING -static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst) + +static int ctrl_interworking_select(struct wpa_supplicant *wpa_s, char *param) +{ + int auto_sel = 0; + int *freqs = NULL; + + if (param) { + char *pos; + + auto_sel = os_strstr(param, "auto") != NULL; + + pos = os_strstr(param, "freq="); + if (pos) { + freqs = freq_range_to_channel_list(wpa_s, pos + 5); + if (freqs == NULL) + return -1; + } + + } + + return interworking_select(wpa_s, auto_sel, freqs); +} + + +static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst, + int only_add) { u8 bssid[ETH_ALEN]; struct wpa_bss *bss; @@ -4394,7 +5998,28 @@ static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst) return -1; } - return interworking_connect(wpa_s, bss); + if (bss->ssid_len == 0) { + int found = 0; + + wpa_printf(MSG_DEBUG, "Selected BSS entry for " MACSTR + " does not have SSID information", MAC2STR(bssid)); + + dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, + list) { + if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 && + bss->ssid_len > 0) { + found = 1; + break; + } + } + + if (!found) + return -1; + wpa_printf(MSG_DEBUG, + "Found another matching BSS entry with SSID"); + } + + return interworking_connect(wpa_s, bss, only_add); } @@ -4406,15 +6031,29 @@ static int get_anqp(struct wpa_supplicant *wpa_s, char *dst) #define MAX_ANQP_INFO_ID 100 u16 id[MAX_ANQP_INFO_ID]; size_t num_id = 0; + u32 subtypes = 0; used = hwaddr_aton2(dst, dst_addr); if (used < 0) return -1; pos = dst + used; + if (*pos == ' ') + pos++; while (num_id < MAX_ANQP_INFO_ID) { - id[num_id] = atoi(pos); - if (id[num_id]) - num_id++; + if (os_strncmp(pos, "hs20:", 5) == 0) { +#ifdef CONFIG_HS20 + int num = atoi(pos + 5); + if (num <= 0 || num > 31) + return -1; + subtypes |= BIT(num); +#else /* CONFIG_HS20 */ + return -1; +#endif /* CONFIG_HS20 */ + } else { + id[num_id] = atoi(pos); + if (id[num_id]) + num_id++; + } pos = os_strchr(pos + 1, ','); if (pos == NULL) break; @@ -4424,7 +6063,7 @@ static int get_anqp(struct wpa_supplicant *wpa_s, char *dst) if (num_id == 0) return -1; - return anqp_send_req(wpa_s, dst_addr, id, num_id); + return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes); } @@ -4500,9 +6139,8 @@ static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf, int used; char *pos; size_t resp_len, start, requested_len; - - if (!wpa_s->last_gas_resp) - return -1; + struct wpabuf *resp; + int ret; used = hwaddr_aton2(cmd, addr); if (used < 0) @@ -4513,11 +6151,18 @@ static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf, pos++; dialog_token = atoi(pos); - if (os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) != 0 || - dialog_token != wpa_s->last_gas_dialog_token) + if (wpa_s->last_gas_resp && + os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) == 0 && + dialog_token == wpa_s->last_gas_dialog_token) + resp = wpa_s->last_gas_resp; + else if (wpa_s->prev_gas_resp && + os_memcmp(addr, wpa_s->prev_gas_addr, ETH_ALEN) == 0 && + dialog_token == wpa_s->prev_gas_dialog_token) + resp = wpa_s->prev_gas_resp; + else return -1; - resp_len = wpabuf_len(wpa_s->last_gas_resp); + resp_len = wpabuf_len(resp); start = 0; requested_len = resp_len; @@ -4538,9 +6183,24 @@ static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf, if (requested_len * 2 + 1 > buflen) return os_snprintf(buf, buflen, "FAIL-Too long response"); - return wpa_snprintf_hex(buf, buflen, - wpabuf_head_u8(wpa_s->last_gas_resp) + start, - requested_len); + ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(resp) + start, + requested_len); + + if (start + requested_len == resp_len) { + /* + * Free memory by dropping the response after it has been + * fetched. + */ + if (resp == wpa_s->prev_gas_resp) { + wpabuf_free(wpa_s->prev_gas_resp); + wpa_s->prev_gas_resp = NULL; + } else { + wpabuf_free(wpa_s->last_gas_resp); + wpa_s->last_gas_resp = NULL; + } + } + + return ret; } #endif /* CONFIG_INTERWORKING */ @@ -4558,6 +6218,8 @@ static int get_hs20_anqp(struct wpa_supplicant *wpa_s, char *dst) if (used < 0) return -1; pos = dst + used; + if (*pos == ' ') + pos++; for (;;) { int num = atoi(pos); if (num <= 0 || num > 31) @@ -4628,7 +6290,7 @@ static int hs20_get_nai_home_realm_list(struct wpa_supplicant *wpa_s, if (len == 0 && cred && cred->realm) return hs20_nai_home_realm_list(wpa_s, dst_addr, cred->realm); - if (len % 1) + if (len & 1) return -1; len /= 2; buf = os_malloc(len); @@ -4647,16 +6309,28 @@ static int hs20_get_nai_home_realm_list(struct wpa_supplicant *wpa_s, return ret; } -#endif /* CONFIG_HS20 */ - -static int wpa_supplicant_ctrl_iface_sta_autoconnect( - struct wpa_supplicant *wpa_s, char *cmd) +static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd) { - wpa_s->auto_reconnect_disabled = atoi(cmd) == 0 ? 1 : 0; - return 0; + u8 dst_addr[ETH_ALEN]; + int used; + char *icon; + + used = hwaddr_aton2(cmd, dst_addr); + if (used < 0) + return -1; + + while (cmd[used] == ' ') + used++; + icon = &cmd[used]; + + wpa_s->fetch_osu_icon_in_progress = 0; + return hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_ICON_REQUEST), + (u8 *) icon, os_strlen(icon)); } +#endif /* CONFIG_HS20 */ + #ifdef CONFIG_AUTOSCAN @@ -4739,6 +6413,19 @@ static int wpas_ctrl_iface_wnm_sleep(struct wpa_supplicant *wpa_s, char *cmd) return ret; } + +static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd) +{ + int query_reason; + + query_reason = atoi(cmd); + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d", + query_reason); + + return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason); +} + #endif /* CONFIG_WNM */ @@ -4747,18 +6434,49 @@ static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, { struct wpa_signal_info si; int ret; + char *pos, *end; ret = wpa_drv_signal_poll(wpa_s, &si); if (ret) return -1; - ret = os_snprintf(buf, buflen, "RSSI=%d\nLINKSPEED=%d\n" + pos = buf; + end = buf + buflen; + + ret = os_snprintf(pos, end - pos, "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) + if (os_snprintf_error(end - pos, ret)) return -1; - return ret; + pos += ret; + + if (si.chanwidth != CHAN_WIDTH_UNKNOWN) { + ret = os_snprintf(pos, end - pos, "WIDTH=%s\n", + channel_width_to_string(si.chanwidth)); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + + if (si.center_frq1 > 0 && si.center_frq2 > 0) { + ret = os_snprintf(pos, end - pos, + "CENTER_FRQ1=%d\nCENTER_FRQ2=%d\n", + si.center_frq1, si.center_frq2); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + + if (si.avg_signal) { + ret = os_snprintf(pos, end - pos, + "AVG_RSSI=%d\n", si.avg_signal); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + + return pos - buf; } @@ -4774,32 +6492,1346 @@ static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf, ret = os_snprintf(buf, buflen, "TXGOOD=%lu\nTXBAD=%lu\nRXGOOD=%lu\n", sta.tx_packets, sta.tx_retry_failed, sta.rx_packets); - if (ret < 0 || (size_t) ret > buflen) + if (os_snprintf_error(buflen, ret)) + return -1; + return ret; +} + + +#ifdef ANDROID +static int wpa_supplicant_driver_cmd(struct wpa_supplicant *wpa_s, char *cmd, + char *buf, size_t buflen) +{ + int ret; + + ret = wpa_drv_driver_cmd(wpa_s, cmd, buf, buflen); + if (ret == 0) { + if (os_strncasecmp(cmd, "COUNTRY", 7) == 0) { + struct p2p_data *p2p = wpa_s->global->p2p; + if (p2p) { + char country[3]; + country[0] = cmd[8]; + country[1] = cmd[9]; + country[2] = 0x04; + p2p_set_country(p2p, country); + } + } + ret = os_snprintf(buf, buflen, "%s\n", "OK"); + if (os_snprintf_error(buflen, ret)) + ret = -1; + } + return ret; +} +#endif /* ANDROID */ + + +static int wpa_supplicant_vendor_cmd(struct wpa_supplicant *wpa_s, char *cmd, + char *buf, size_t buflen) +{ + int ret; + char *pos; + u8 *data = NULL; + unsigned int vendor_id, subcmd; + struct wpabuf *reply; + size_t data_len = 0; + + /* cmd: <vendor id> <subcommand id> [<hex formatted data>] */ + vendor_id = strtoul(cmd, &pos, 16); + if (!isblank(*pos)) + return -EINVAL; + + subcmd = strtoul(pos, &pos, 10); + + if (*pos != '\0') { + if (!isblank(*pos++)) + return -EINVAL; + data_len = os_strlen(pos); + } + + if (data_len) { + data_len /= 2; + data = os_malloc(data_len); + if (!data) + return -1; + + if (hexstr2bin(pos, data, data_len)) { + wpa_printf(MSG_DEBUG, + "Vendor command: wrong parameter format"); + os_free(data); + return -EINVAL; + } + } + + reply = wpabuf_alloc((buflen - 1) / 2); + if (!reply) { + os_free(data); + return -1; + } + + ret = wpa_drv_vendor_cmd(wpa_s, vendor_id, subcmd, data, data_len, + reply); + + if (ret == 0) + ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply), + wpabuf_len(reply)); + + wpabuf_free(reply); + os_free(data); + + return ret; +} + + +static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_P2P + struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s ? + wpa_s->global->p2p_init_wpa_s : wpa_s; +#endif /* CONFIG_P2P */ + + wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state"); + +#ifdef CONFIG_P2P + wpas_p2p_cancel(p2p_wpa_s); + p2p_ctrl_flush(p2p_wpa_s); + wpas_p2p_group_remove(p2p_wpa_s, "*"); + wpas_p2p_service_flush(p2p_wpa_s); + p2p_wpa_s->global->p2p_disabled = 0; + p2p_wpa_s->global->p2p_per_sta_psk = 0; + p2p_wpa_s->conf->num_sec_device_types = 0; + p2p_wpa_s->p2p_disable_ip_addr_req = 0; + os_free(p2p_wpa_s->global->p2p_go_avoid_freq.range); + p2p_wpa_s->global->p2p_go_avoid_freq.range = NULL; + p2p_wpa_s->global->pending_p2ps_group = 0; +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_WPS_TESTING + wps_version_number = 0x20; + wps_testing_dummy_cred = 0; + wps_corrupt_pkhash = 0; +#endif /* CONFIG_WPS_TESTING */ +#ifdef CONFIG_WPS + wpa_s->wps_fragment_size = 0; + wpas_wps_cancel(wpa_s); + wps_registrar_flush(wpa_s->wps->registrar); +#endif /* CONFIG_WPS */ + wpa_s->after_wps = 0; + wpa_s->known_wps_freq = 0; + +#ifdef CONFIG_TDLS +#ifdef CONFIG_TDLS_TESTING + extern unsigned int tdls_testing; + tdls_testing = 0; +#endif /* CONFIG_TDLS_TESTING */ + wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL); + wpa_tdls_enable(wpa_s->wpa, 1); +#endif /* CONFIG_TDLS */ + + eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL); + wpa_supplicant_stop_countermeasures(wpa_s, NULL); + + wpa_s->no_keep_alive = 0; + wpa_s->own_disconnect_req = 0; + + os_free(wpa_s->disallow_aps_bssid); + wpa_s->disallow_aps_bssid = NULL; + wpa_s->disallow_aps_bssid_count = 0; + os_free(wpa_s->disallow_aps_ssid); + wpa_s->disallow_aps_ssid = NULL; + wpa_s->disallow_aps_ssid_count = 0; + + wpa_s->set_sta_uapsd = 0; + wpa_s->sta_uapsd = 0; + + wpa_drv_radio_disable(wpa_s, 0); + wpa_blacklist_clear(wpa_s); + wpa_s->extra_blacklist_count = 0; + wpa_supplicant_ctrl_iface_remove_network(wpa_s, "all"); + wpa_supplicant_ctrl_iface_remove_cred(wpa_s, "all"); + wpa_config_flush_blobs(wpa_s->conf); + wpa_s->conf->auto_interworking = 0; + wpa_s->conf->okc = 0; + + wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL); + rsn_preauth_deinit(wpa_s->wpa); + + wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, 43200); + wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, 70); + wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, 60); + eapol_sm_notify_logoff(wpa_s->eapol, FALSE); + + radio_remove_works(wpa_s, NULL, 1); + wpa_s->ext_work_in_progress = 0; + + wpa_s->next_ssid = NULL; + +#ifdef CONFIG_INTERWORKING + hs20_cancel_fetch_osu(wpa_s); +#endif /* CONFIG_INTERWORKING */ + + wpa_s->ext_mgmt_frame_handling = 0; + wpa_s->ext_eapol_frame_io = 0; +#ifdef CONFIG_TESTING_OPTIONS + wpa_s->extra_roc_dur = 0; + wpa_s->test_failure = WPAS_TEST_FAILURE_NONE; +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_s->disconnected = 0; + os_free(wpa_s->next_scan_freqs); + wpa_s->next_scan_freqs = NULL; + + wpa_bss_flush(wpa_s); + if (!dl_list_empty(&wpa_s->bss)) { + wpa_printf(MSG_DEBUG, + "BSS table not empty after flush: %u entries, current_bss=%p bssid=" + MACSTR " pending_bssid=" MACSTR, + dl_list_len(&wpa_s->bss), wpa_s->current_bss, + MAC2STR(wpa_s->bssid), + MAC2STR(wpa_s->pending_bssid)); + } +} + + +static int wpas_ctrl_radio_work_show(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) +{ + struct wpa_radio_work *work; + char *pos, *end; + struct os_reltime now, diff; + + pos = buf; + end = buf + buflen; + + os_get_reltime(&now); + + dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list) + { + int ret; + + os_reltime_sub(&now, &work->time, &diff); + ret = os_snprintf(pos, end - pos, "%s@%s:%u:%u:%ld.%06ld\n", + work->type, work->wpa_s->ifname, work->freq, + work->started, diff.sec, diff.usec); + if (os_snprintf_error(end - pos, ret)) + break; + pos += ret; + } + + return pos - buf; +} + + +static void wpas_ctrl_radio_work_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_radio_work *work = eloop_ctx; + struct wpa_external_work *ework = work->ctx; + + wpa_dbg(work->wpa_s, MSG_DEBUG, + "Timing out external radio work %u (%s)", + ework->id, work->type); + wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_TIMEOUT "%u", ework->id); + work->wpa_s->ext_work_in_progress = 0; + radio_work_done(work); + os_free(ework); +} + + +static void wpas_ctrl_radio_work_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_external_work *ework = work->ctx; + + if (deinit) { + if (work->started) + eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, + work, NULL); + + os_free(ework); + return; + } + + wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting external radio work %u (%s)", + ework->id, ework->type); + wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_START "%u", ework->id); + work->wpa_s->ext_work_in_progress = 1; + if (!ework->timeout) + ework->timeout = 10; + eloop_register_timeout(ework->timeout, 0, wpas_ctrl_radio_work_timeout, + work, NULL); +} + + +static int wpas_ctrl_radio_work_add(struct wpa_supplicant *wpa_s, char *cmd, + char *buf, size_t buflen) +{ + struct wpa_external_work *ework; + char *pos, *pos2; + size_t type_len; + int ret; + unsigned int freq = 0; + + /* format: <name> [freq=<MHz>] [timeout=<seconds>] */ + + ework = os_zalloc(sizeof(*ework)); + if (ework == NULL) + return -1; + + pos = os_strchr(cmd, ' '); + if (pos) { + type_len = pos - cmd; + pos++; + + pos2 = os_strstr(pos, "freq="); + if (pos2) + freq = atoi(pos2 + 5); + + pos2 = os_strstr(pos, "timeout="); + if (pos2) + ework->timeout = atoi(pos2 + 8); + } else { + type_len = os_strlen(cmd); + } + if (4 + type_len >= sizeof(ework->type)) + type_len = sizeof(ework->type) - 4 - 1; + os_strlcpy(ework->type, "ext:", sizeof(ework->type)); + os_memcpy(ework->type + 4, cmd, type_len); + ework->type[4 + type_len] = '\0'; + + wpa_s->ext_work_id++; + if (wpa_s->ext_work_id == 0) + wpa_s->ext_work_id++; + ework->id = wpa_s->ext_work_id; + + if (radio_add_work(wpa_s, freq, ework->type, 0, wpas_ctrl_radio_work_cb, + ework) < 0) { + os_free(ework); + return -1; + } + + ret = os_snprintf(buf, buflen, "%u", ework->id); + if (os_snprintf_error(buflen, ret)) + return -1; + return ret; +} + + +static int wpas_ctrl_radio_work_done(struct wpa_supplicant *wpa_s, char *cmd) +{ + struct wpa_radio_work *work; + unsigned int id = atoi(cmd); + + dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list) + { + struct wpa_external_work *ework; + + if (os_strncmp(work->type, "ext:", 4) != 0) + continue; + ework = work->ctx; + if (id && ework->id != id) + continue; + wpa_dbg(wpa_s, MSG_DEBUG, + "Completed external radio work %u (%s)", + ework->id, ework->type); + eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, work, NULL); + wpa_s->ext_work_in_progress = 0; + radio_work_done(work); + os_free(ework); + return 3; /* "OK\n" */ + } + + return -1; +} + + +static int wpas_ctrl_radio_work(struct wpa_supplicant *wpa_s, char *cmd, + char *buf, size_t buflen) +{ + if (os_strcmp(cmd, "show") == 0) + return wpas_ctrl_radio_work_show(wpa_s, buf, buflen); + if (os_strncmp(cmd, "add ", 4) == 0) + return wpas_ctrl_radio_work_add(wpa_s, cmd + 4, buf, buflen); + if (os_strncmp(cmd, "done ", 5) == 0) + return wpas_ctrl_radio_work_done(wpa_s, cmd + 4); + return -1; +} + + +void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s) +{ + struct wpa_radio_work *work, *tmp; + + if (!wpa_s || !wpa_s->radio) + return; + + dl_list_for_each_safe(work, tmp, &wpa_s->radio->work, + struct wpa_radio_work, list) { + struct wpa_external_work *ework; + + if (os_strncmp(work->type, "ext:", 4) != 0) + continue; + ework = work->ctx; + wpa_dbg(wpa_s, MSG_DEBUG, + "Flushing%s external radio work %u (%s)", + work->started ? " started" : "", ework->id, + ework->type); + if (work->started) + eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, + work, NULL); + radio_work_done(work); + os_free(ework); + } +} + + +static void wpas_ctrl_eapol_response(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + eapol_sm_notify_ctrl_response(wpa_s->eapol); +} + + +static int scan_id_list_parse(struct wpa_supplicant *wpa_s, const char *value, + unsigned int *scan_id_count, int scan_id[]) +{ + const char *pos = value; + + while (pos) { + if (*pos == ' ' || *pos == '\0') + break; + if (*scan_id_count == MAX_SCAN_ID) + return -1; + scan_id[(*scan_id_count)++] = atoi(pos); + pos = os_strchr(pos, ','); + if (pos) + pos++; + } + + return 0; +} + + +static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, + char *reply, int reply_size, int *reply_len) +{ + char *pos; + unsigned int manual_scan_passive = 0; + unsigned int manual_scan_use_id = 0; + unsigned int manual_scan_only_new = 0; + unsigned int scan_only = 0; + unsigned int scan_id_count = 0; + int scan_id[MAX_SCAN_ID]; + void (*scan_res_handler)(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res); + int *manual_scan_freqs = NULL; + + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { + *reply_len = -1; + return; + } + + if (radio_work_pending(wpa_s, "scan")) { + wpa_printf(MSG_DEBUG, + "Pending scan scheduled - reject new request"); + *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n"); + return; + } + + if (params) { + if (os_strncasecmp(params, "TYPE=ONLY", 9) == 0) + scan_only = 1; + + pos = os_strstr(params, "freq="); + if (pos) { + manual_scan_freqs = freq_range_to_channel_list(wpa_s, + pos + 5); + if (manual_scan_freqs == NULL) { + *reply_len = -1; + goto done; + } + } + + pos = os_strstr(params, "passive="); + if (pos) + manual_scan_passive = !!atoi(pos + 8); + + pos = os_strstr(params, "use_id="); + if (pos) + manual_scan_use_id = atoi(pos + 7); + + pos = os_strstr(params, "only_new=1"); + if (pos) + manual_scan_only_new = 1; + + pos = os_strstr(params, "scan_id="); + if (pos && scan_id_list_parse(wpa_s, pos + 8, &scan_id_count, + scan_id) < 0) { + *reply_len = -1; + goto done; + } + } + + if (scan_only) + scan_res_handler = scan_only_handler; + else if (wpa_s->scan_res_handler == scan_only_handler) + scan_res_handler = NULL; + else + scan_res_handler = wpa_s->scan_res_handler; + + if (!wpa_s->sched_scanning && !wpa_s->scanning && + ((wpa_s->wpa_state <= WPA_SCANNING) || + (wpa_s->wpa_state == WPA_COMPLETED))) { + wpa_s->manual_scan_passive = manual_scan_passive; + wpa_s->manual_scan_use_id = manual_scan_use_id; + wpa_s->manual_scan_only_new = manual_scan_only_new; + wpa_s->scan_id_count = scan_id_count; + os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int)); + wpa_s->scan_res_handler = scan_res_handler; + os_free(wpa_s->manual_scan_freqs); + wpa_s->manual_scan_freqs = manual_scan_freqs; + manual_scan_freqs = NULL; + + wpa_s->normal_scans = 0; + wpa_s->scan_req = MANUAL_SCAN_REQ; + wpa_s->after_wps = 0; + wpa_s->known_wps_freq = 0; + wpa_supplicant_req_scan(wpa_s, 0, 0); + if (wpa_s->manual_scan_use_id) { + wpa_s->manual_scan_id++; + wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u", + wpa_s->manual_scan_id); + *reply_len = os_snprintf(reply, reply_size, "%u\n", + wpa_s->manual_scan_id); + } + } else if (wpa_s->sched_scanning) { + wpa_s->manual_scan_passive = manual_scan_passive; + wpa_s->manual_scan_use_id = manual_scan_use_id; + wpa_s->manual_scan_only_new = manual_scan_only_new; + wpa_s->scan_id_count = scan_id_count; + os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int)); + wpa_s->scan_res_handler = scan_res_handler; + os_free(wpa_s->manual_scan_freqs); + wpa_s->manual_scan_freqs = manual_scan_freqs; + manual_scan_freqs = NULL; + + wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to allow requested full scan to proceed"); + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_s->scan_req = MANUAL_SCAN_REQ; + wpa_supplicant_req_scan(wpa_s, 0, 0); + if (wpa_s->manual_scan_use_id) { + wpa_s->manual_scan_id++; + *reply_len = os_snprintf(reply, reply_size, "%u\n", + wpa_s->manual_scan_id); + wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u", + wpa_s->manual_scan_id); + } + } else { + wpa_printf(MSG_DEBUG, "Ongoing scan action - reject new request"); + *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n"); + } + +done: + os_free(manual_scan_freqs); +} + + +#ifdef CONFIG_TESTING_OPTIONS + +static void wpas_ctrl_iface_mgmt_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) +{ + wpa_msg(wpa_s, MSG_INFO, "MGMT-TX-STATUS freq=%u dst=" MACSTR + " src=" MACSTR " bssid=" MACSTR " result=%s", + freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid), + result == OFFCHANNEL_SEND_ACTION_SUCCESS ? + "SUCCESS" : (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? + "NO_ACK" : "FAILED")); +} + + +static int wpas_ctrl_iface_mgmt_tx(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos, *param; + size_t len; + u8 *buf, da[ETH_ALEN], bssid[ETH_ALEN]; + int res, used; + int freq = 0, no_cck = 0, wait_time = 0; + + /* <DA> <BSSID> [freq=<MHz>] [wait_time=<ms>] [no_cck=1] + * <action=Action frame payload> */ + + wpa_printf(MSG_DEBUG, "External MGMT TX: %s", cmd); + + pos = cmd; + used = hwaddr_aton2(pos, da); + if (used < 0) + return -1; + pos += used; + while (*pos == ' ') + pos++; + used = hwaddr_aton2(pos, bssid); + if (used < 0) + return -1; + pos += used; + + param = os_strstr(pos, " freq="); + if (param) { + param += 6; + freq = atoi(param); + } + + param = os_strstr(pos, " no_cck="); + if (param) { + param += 8; + no_cck = atoi(param); + } + + param = os_strstr(pos, " wait_time="); + if (param) { + param += 11; + wait_time = atoi(param); + } + + param = os_strstr(pos, " action="); + if (param == NULL) + return -1; + param += 8; + + len = os_strlen(param); + if (len & 1) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(param, buf, len) < 0) { + os_free(buf); + return -1; + } + + res = offchannel_send_action(wpa_s, freq, da, wpa_s->own_addr, bssid, + buf, len, wait_time, + wpas_ctrl_iface_mgmt_tx_cb, no_cck); + os_free(buf); + return res; +} + + +static void wpas_ctrl_iface_mgmt_tx_done(struct wpa_supplicant *wpa_s) +{ + wpa_printf(MSG_DEBUG, "External MGMT TX - done waiting"); + offchannel_send_action_done(wpa_s); +} + + +static int wpas_ctrl_iface_driver_event(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos, *param; + union wpa_event_data event; + enum wpa_event_type ev; + + /* <event name> [parameters..] */ + + wpa_dbg(wpa_s, MSG_DEBUG, "Testing - external driver event: %s", cmd); + + pos = cmd; + param = os_strchr(pos, ' '); + if (param) + *param++ = '\0'; + + os_memset(&event, 0, sizeof(event)); + + if (os_strcmp(cmd, "INTERFACE_ENABLED") == 0) { + ev = EVENT_INTERFACE_ENABLED; + } else if (os_strcmp(cmd, "INTERFACE_DISABLED") == 0) { + ev = EVENT_INTERFACE_DISABLED; + } else if (os_strcmp(cmd, "AVOID_FREQUENCIES") == 0) { + ev = EVENT_AVOID_FREQUENCIES; + if (param == NULL) + param = ""; + if (freq_range_list_parse(&event.freq_range, param) < 0) + return -1; + wpa_supplicant_event(wpa_s, ev, &event); + os_free(event.freq_range.range); + return 0; + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "Testing - unknown driver event: %s", + cmd); + return -1; + } + + wpa_supplicant_event(wpa_s, ev, &event); + + return 0; +} + + +static int wpas_ctrl_iface_eapol_rx(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos; + u8 src[ETH_ALEN], *buf; + int used; + size_t len; + + wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd); + + pos = cmd; + used = hwaddr_aton2(pos, src); + if (used < 0) + return -1; + pos += used; + while (*pos == ' ') + pos++; + + len = os_strlen(pos); + if (len & 1) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(pos, buf, len) < 0) { + os_free(buf); + return -1; + } + + wpa_supplicant_rx_eapol(wpa_s, src, buf, len); + os_free(buf); + + return 0; +} + + +static u16 ipv4_hdr_checksum(const void *buf, size_t len) +{ + size_t i; + u32 sum = 0; + const u16 *pos = buf; + + for (i = 0; i < len / 2; i++) + sum += *pos++; + + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + return sum ^ 0xffff; +} + + +#define HWSIM_PACKETLEN 1500 +#define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header)) + +void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct wpa_supplicant *wpa_s = ctx; + const struct ether_header *eth; + const struct iphdr *ip; + const u8 *pos; + unsigned int i; + + if (len != HWSIM_PACKETLEN) + return; + + eth = (const struct ether_header *) buf; + ip = (const struct iphdr *) (eth + 1); + pos = (const u8 *) (ip + 1); + + if (ip->ihl != 5 || ip->version != 4 || + ntohs(ip->tot_len) != HWSIM_IP_LEN) + return; + + for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) { + if (*pos != (u8) i) + return; + pos++; + } + + wpa_msg(wpa_s, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR, + MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost)); +} + + +static int wpas_ctrl_iface_data_test_config(struct wpa_supplicant *wpa_s, + char *cmd) +{ + int enabled = atoi(cmd); + + if (!enabled) { + if (wpa_s->l2_test) { + l2_packet_deinit(wpa_s->l2_test); + wpa_s->l2_test = NULL; + wpa_dbg(wpa_s, MSG_DEBUG, "test data: Disabled"); + } + return 0; + } + + if (wpa_s->l2_test) + return 0; + + wpa_s->l2_test = l2_packet_init(wpa_s->ifname, wpa_s->own_addr, + ETHERTYPE_IP, wpas_data_test_rx, + wpa_s, 1); + if (wpa_s->l2_test == NULL) + return -1; + + wpa_dbg(wpa_s, MSG_DEBUG, "test data: Enabled"); + + return 0; +} + + +static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd) +{ + u8 dst[ETH_ALEN], src[ETH_ALEN]; + char *pos; + int used; + long int val; + u8 tos; + u8 buf[HWSIM_PACKETLEN]; + struct ether_header *eth; + struct iphdr *ip; + u8 *dpos; + unsigned int i; + + if (wpa_s->l2_test == NULL) + return -1; + + /* format: <dst> <src> <tos> */ + + pos = cmd; + used = hwaddr_aton2(pos, dst); + if (used < 0) + return -1; + pos += used; + while (*pos == ' ') + pos++; + used = hwaddr_aton2(pos, src); + if (used < 0) + return -1; + pos += used; + + val = strtol(pos, NULL, 0); + if (val < 0 || val > 0xff) + return -1; + tos = val; + + eth = (struct ether_header *) buf; + os_memcpy(eth->ether_dhost, dst, ETH_ALEN); + os_memcpy(eth->ether_shost, src, ETH_ALEN); + eth->ether_type = htons(ETHERTYPE_IP); + ip = (struct iphdr *) (eth + 1); + os_memset(ip, 0, sizeof(*ip)); + ip->ihl = 5; + ip->version = 4; + ip->ttl = 64; + ip->tos = tos; + ip->tot_len = htons(HWSIM_IP_LEN); + ip->protocol = 1; + ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1); + ip->daddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 2); + ip->check = ipv4_hdr_checksum(ip, sizeof(*ip)); + dpos = (u8 *) (ip + 1); + for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) + *dpos++ = i; + + if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, buf, + HWSIM_PACKETLEN) < 0) + return -1; + + wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX dst=" MACSTR " src=" MACSTR + " tos=0x%x", MAC2STR(dst), MAC2STR(src), tos); + + return 0; +} + + +static int wpas_ctrl_iface_data_test_frame(struct wpa_supplicant *wpa_s, + char *cmd) +{ + u8 *buf; + struct ether_header *eth; + struct l2_packet_data *l2 = NULL; + size_t len; + u16 ethertype; + int res = -1; + + len = os_strlen(cmd); + if (len & 1 || len < ETH_HLEN * 2) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) return -1; + + if (hexstr2bin(cmd, buf, len) < 0) + goto done; + + eth = (struct ether_header *) buf; + ethertype = ntohs(eth->ether_type); + + l2 = l2_packet_init(wpa_s->ifname, wpa_s->own_addr, ethertype, + wpas_data_test_rx, wpa_s, 1); + if (l2 == NULL) + goto done; + + res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len); + wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX frame res=%d", res); +done: + if (l2) + l2_packet_deinit(l2); + os_free(buf); + + return res < 0 ? -1 : 0; +} + + +static int wpas_ctrl_test_alloc_fail(struct wpa_supplicant *wpa_s, char *cmd) +{ +#ifdef WPA_TRACE_BFD + extern char wpa_trace_fail_func[256]; + extern unsigned int wpa_trace_fail_after; + char *pos; + + wpa_trace_fail_after = atoi(cmd); + pos = os_strchr(cmd, ':'); + if (pos) { + pos++; + os_strlcpy(wpa_trace_fail_func, pos, + sizeof(wpa_trace_fail_func)); + } else { + wpa_trace_fail_after = 0; + } + return 0; +#else /* WPA_TRACE_BFD */ + return -1; +#endif /* WPA_TRACE_BFD */ +} + + +static int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) +{ +#ifdef WPA_TRACE_BFD + extern char wpa_trace_fail_func[256]; + extern unsigned int wpa_trace_fail_after; + + return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after, + wpa_trace_fail_func); +#else /* WPA_TRACE_BFD */ + return -1; +#endif /* WPA_TRACE_BFD */ +} + +#endif /* CONFIG_TESTING_OPTIONS */ + + +static void wpas_ctrl_vendor_elem_update(struct wpa_supplicant *wpa_s) +{ + unsigned int i; + char buf[30]; + + wpa_printf(MSG_DEBUG, "Update vendor elements"); + + for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) { + if (wpa_s->vendor_elem[i]) { + int res; + + res = os_snprintf(buf, sizeof(buf), "frame[%u]", i); + if (!os_snprintf_error(sizeof(buf), res)) { + wpa_hexdump_buf(MSG_DEBUG, buf, + wpa_s->vendor_elem[i]); + } + } + } + +#ifdef CONFIG_P2P + if (wpa_s->parent == wpa_s && + wpa_s->global->p2p && + !wpa_s->global->p2p_disabled) + p2p_set_vendor_elems(wpa_s->global->p2p, wpa_s->vendor_elem); +#endif /* CONFIG_P2P */ +} + + +static struct wpa_supplicant * +wpas_ctrl_vendor_elem_iface(struct wpa_supplicant *wpa_s, + enum wpa_vendor_elem_frame frame) +{ + switch (frame) { +#ifdef CONFIG_P2P + case VENDOR_ELEM_PROBE_REQ_P2P: + case VENDOR_ELEM_PROBE_RESP_P2P: + case VENDOR_ELEM_PROBE_RESP_P2P_GO: + case VENDOR_ELEM_BEACON_P2P_GO: + case VENDOR_ELEM_P2P_PD_REQ: + case VENDOR_ELEM_P2P_PD_RESP: + case VENDOR_ELEM_P2P_GO_NEG_REQ: + case VENDOR_ELEM_P2P_GO_NEG_RESP: + case VENDOR_ELEM_P2P_GO_NEG_CONF: + case VENDOR_ELEM_P2P_INV_REQ: + case VENDOR_ELEM_P2P_INV_RESP: + case VENDOR_ELEM_P2P_ASSOC_REQ: + return wpa_s->parent; +#endif /* CONFIG_P2P */ + default: + return wpa_s; + } +} + + +static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos = cmd; + int frame; + size_t len; + struct wpabuf *buf; + struct ieee802_11_elems elems; + + frame = atoi(pos); + if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES) + return -1; + wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame); + + pos = os_strchr(pos, ' '); + if (pos == NULL) + return -1; + pos++; + + len = os_strlen(pos); + if (len == 0) + return 0; + if (len & 1) + return -1; + len /= 2; + + buf = wpabuf_alloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) { + wpabuf_free(buf); + return -1; + } + + if (ieee802_11_parse_elems(wpabuf_head_u8(buf), len, &elems, 0) == + ParseFailed) { + wpabuf_free(buf); + return -1; + } + + if (wpa_s->vendor_elem[frame] == NULL) { + wpa_s->vendor_elem[frame] = buf; + wpas_ctrl_vendor_elem_update(wpa_s); + return 0; + } + + if (wpabuf_resize(&wpa_s->vendor_elem[frame], len) < 0) { + wpabuf_free(buf); + return -1; + } + + wpabuf_put_buf(wpa_s->vendor_elem[frame], buf); + wpabuf_free(buf); + wpas_ctrl_vendor_elem_update(wpa_s); + + return 0; +} + + +static int wpas_ctrl_vendor_elem_get(struct wpa_supplicant *wpa_s, char *cmd, + char *buf, size_t buflen) +{ + int frame = atoi(cmd); + + if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES) + return -1; + wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame); + + if (wpa_s->vendor_elem[frame] == NULL) + return 0; + + return wpa_snprintf_hex(buf, buflen, + wpabuf_head_u8(wpa_s->vendor_elem[frame]), + wpabuf_len(wpa_s->vendor_elem[frame])); +} + + +static int wpas_ctrl_vendor_elem_remove(struct wpa_supplicant *wpa_s, char *cmd) +{ + char *pos = cmd; + int frame; + size_t len; + u8 *buf; + struct ieee802_11_elems elems; + u8 *ie, *end; + + frame = atoi(pos); + if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES) + return -1; + wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame); + + pos = os_strchr(pos, ' '); + if (pos == NULL) + return -1; + pos++; + + if (*pos == '*') { + wpabuf_free(wpa_s->vendor_elem[frame]); + wpa_s->vendor_elem[frame] = NULL; + wpas_ctrl_vendor_elem_update(wpa_s); + return 0; + } + + if (wpa_s->vendor_elem[frame] == NULL) + return -1; + + len = os_strlen(pos); + if (len == 0) + return 0; + if (len & 1) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(pos, buf, len) < 0) { + os_free(buf); + return -1; + } + + if (ieee802_11_parse_elems(buf, len, &elems, 0) == ParseFailed) { + os_free(buf); + return -1; + } + + ie = wpabuf_mhead_u8(wpa_s->vendor_elem[frame]); + end = ie + wpabuf_len(wpa_s->vendor_elem[frame]); + + for (; ie + 1 < end; ie += 2 + ie[1]) { + if (ie + len > end) + break; + if (os_memcmp(ie, buf, len) != 0) + continue; + + if (wpabuf_len(wpa_s->vendor_elem[frame]) == len) { + wpabuf_free(wpa_s->vendor_elem[frame]); + wpa_s->vendor_elem[frame] = NULL; + } else { + os_memmove(ie, ie + len, + end - (ie + len)); + wpa_s->vendor_elem[frame]->used -= len; + } + os_free(buf); + wpas_ctrl_vendor_elem_update(wpa_s); + return 0; + } + + os_free(buf); + + return -1; +} + + +static void wpas_ctrl_neighbor_rep_cb(void *ctx, struct wpabuf *neighbor_rep) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (neighbor_rep) { + wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_RXED + "length=%u", + (unsigned int) wpabuf_len(neighbor_rep)); + wpabuf_free(neighbor_rep); + } else { + wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_FAILED); + } +} + + +static int wpas_ctrl_iface_send_neigbor_rep(struct wpa_supplicant *wpa_s, + char *cmd) +{ + struct wpa_ssid ssid; + struct wpa_ssid *ssid_p = NULL; + int ret = 0; + + if (os_strncmp(cmd, " ssid=", 6) == 0) { + ssid.ssid_len = os_strlen(cmd + 6); + if (ssid.ssid_len > 32) + return -1; + ssid.ssid = (u8 *) (cmd + 6); + ssid_p = &ssid; + } + + ret = wpas_rrm_send_neighbor_rep_request(wpa_s, ssid_p, + wpas_ctrl_neighbor_rep_cb, + wpa_s); + return ret; } +static int wpas_ctrl_iface_erp_flush(struct wpa_supplicant *wpa_s) +{ + eapol_sm_erp_flush(wpa_s->eapol); + return 0; +} + + +static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *token, *context = NULL; + unsigned int enable = ~0, type = 0; + u8 _addr[ETH_ALEN], _mask[ETH_ALEN]; + u8 *addr = NULL, *mask = NULL; + + while ((token = str_token(cmd, " ", &context))) { + if (os_strcasecmp(token, "scan") == 0) { + type |= MAC_ADDR_RAND_SCAN; + } else if (os_strcasecmp(token, "sched") == 0) { + type |= MAC_ADDR_RAND_SCHED_SCAN; + } else if (os_strcasecmp(token, "pno") == 0) { + type |= MAC_ADDR_RAND_PNO; + } else if (os_strcasecmp(token, "all") == 0) { + type = wpa_s->mac_addr_rand_supported; + } else if (os_strncasecmp(token, "enable=", 7) == 0) { + enable = atoi(token + 7); + } else if (os_strncasecmp(token, "addr=", 5) == 0) { + addr = _addr; + if (hwaddr_aton(token + 5, addr)) { + wpa_printf(MSG_INFO, + "CTRL: Invalid MAC address: %s", + token); + return -1; + } + } else if (os_strncasecmp(token, "mask=", 5) == 0) { + mask = _mask; + if (hwaddr_aton(token + 5, mask)) { + wpa_printf(MSG_INFO, + "CTRL: Invalid MAC address mask: %s", + token); + return -1; + } + } else { + wpa_printf(MSG_INFO, + "CTRL: Invalid MAC_RAND_SCAN parameter: %s", + token); + return -1; + } + } + + if (!type) { + wpa_printf(MSG_INFO, "CTRL: MAC_RAND_SCAN no type specified"); + return -1; + } + + if ((wpa_s->mac_addr_rand_supported & type) != type) { + wpa_printf(MSG_INFO, + "CTRL: MAC_RAND_SCAN types=%u != supported=%u", + type, wpa_s->mac_addr_rand_supported); + return -1; + } + + if (enable > 1) { + wpa_printf(MSG_INFO, + "CTRL: MAC_RAND_SCAN enable=<0/1> not specified"); + return -1; + } + + if (!enable) { + wpas_mac_addr_rand_scan_clear(wpa_s, type); + if (wpa_s->pno) { + if (type & MAC_ADDR_RAND_PNO) { + wpas_stop_pno(wpa_s); + wpas_start_pno(wpa_s); + } + } else if (wpa_s->sched_scanning && + (type & MAC_ADDR_RAND_SCHED_SCAN)) { + /* simulate timeout to restart the sched scan */ + wpa_s->sched_scan_timed_out = 1; + wpa_s->prev_sched_ssid = NULL; + wpa_supplicant_cancel_sched_scan(wpa_s); + } + return 0; + } + + if ((addr && !mask) || (!addr && mask)) { + wpa_printf(MSG_INFO, + "CTRL: MAC_RAND_SCAN invalid addr/mask combination"); + return -1; + } + + if (addr && mask && (!(mask[0] & 0x01) || (addr[0] & 0x01))) { + wpa_printf(MSG_INFO, + "CTRL: MAC_RAND_SCAN cannot allow multicast address"); + return -1; + } + + if (type & MAC_ADDR_RAND_SCAN) { + wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCAN, + addr, mask); + } + + if (type & MAC_ADDR_RAND_SCHED_SCAN) { + wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCHED_SCAN, + addr, mask); + + if (wpa_s->sched_scanning && !wpa_s->pno) { + /* simulate timeout to restart the sched scan */ + wpa_s->sched_scan_timed_out = 1; + wpa_s->prev_sched_ssid = NULL; + wpa_supplicant_cancel_sched_scan(wpa_s); + } + } + + if (type & MAC_ADDR_RAND_PNO) { + wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_PNO, + addr, mask); + if (wpa_s->pno) { + wpas_stop_pno(wpa_s); + wpas_start_pno(wpa_s); + } + } + + return 0; +} + + 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 || - os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 || - os_strncmp(buf, "NFC_RX_HANDOVER_SEL", 19) == 0) { + os_strncmp(buf, "SET_NETWORK ", 12) == 0) { + if (wpa_debug_show_keys) + wpa_dbg(wpa_s, MSG_DEBUG, + "Control interface command '%s'", buf); + else + wpa_dbg(wpa_s, MSG_DEBUG, + "Control interface command '%s [REMOVED]'", + os_strncmp(buf, WPA_CTRL_RSP, + os_strlen(WPA_CTRL_RSP)) == 0 ? + WPA_CTRL_RSP : "SET_NETWORK"); + } else if (os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 || + os_strncmp(buf, "NFC_REPORT_HANDOVER", 19) == 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)); wpa_dbg(wpa_s, level, "Control interface command '%s'", buf); } @@ -4826,13 +7858,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } 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; + reply_len += eapol_sm_get_mib(wpa_s->eapol, + reply + reply_len, + reply_size - reply_len); } } else if (os_strncmp(buf, "STATUS", 6) == 0) { reply_len = wpa_supplicant_ctrl_iface_status( @@ -4840,9 +7868,14 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strcmp(buf, "PMKSA") == 0) { reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, reply, reply_size); + } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) { + wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL); } 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, "DUMP", 4) == 0) { + reply_len = wpa_config_dump_values(wpa_s->conf, + reply, reply_size); } else if (os_strncmp(buf, "GET ", 4) == 0) { reply_len = wpa_supplicant_ctrl_iface_get(wpa_s, buf + 4, reply, reply_size); @@ -4855,6 +7888,14 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = -1; else wpas_request_connection(wpa_s); + } else if (os_strcmp(buf, "REATTACH") == 0) { + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED || + !wpa_s->current_ssid) + reply_len = -1; + else { + wpa_s->reattach = 1; + wpas_request_connection(wpa_s); + } } else if (os_strcmp(buf, "RECONNECT") == 0) { if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) reply_len = -1; @@ -4907,6 +7948,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "WPS_NFC ", 8) == 0) { if (wpa_supplicant_ctrl_iface_wps_nfc(wpa_s, buf + 8)) reply_len = -1; + } else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) { + reply_len = wpa_supplicant_ctrl_iface_wps_nfc_config_token( + wpa_s, buf + 21, reply, reply_size); } else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) { reply_len = wpa_supplicant_ctrl_iface_wps_nfc_token( wpa_s, buf + 14, reply, reply_size); @@ -4920,11 +7964,8 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) { reply_len = wpas_ctrl_nfc_get_handover_sel( wpa_s, buf + 21, reply, reply_size); - } else if (os_strncmp(buf, "NFC_RX_HANDOVER_REQ ", 20) == 0) { - reply_len = wpas_ctrl_nfc_rx_handover_req( - wpa_s, buf + 20, reply, reply_size); - } else if (os_strncmp(buf, "NFC_RX_HANDOVER_SEL ", 20) == 0) { - if (wpas_ctrl_nfc_rx_handover_sel(wpa_s, buf + 20)) + } else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) { + if (wpas_ctrl_nfc_report_handover(wpa_s, buf + 20)) reply_len = -1; #endif /* CONFIG_WPS_NFC */ } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) { @@ -4943,8 +7984,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, 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)) - reply_len = -1; + wpas_wps_er_stop(wpa_s); } else if (os_strncmp(buf, "WPS_ER_PIN ", 11) == 0) { if (wpa_supplicant_ctrl_iface_wps_er_pin(wpa_s, buf + 11)) reply_len = -1; @@ -4983,15 +8023,36 @@ 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_MESH + } else if (os_strncmp(buf, "MESH_INTERFACE_ADD ", 19) == 0) { + reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add( + wpa_s, buf + 19, reply, reply_size); + } else if (os_strcmp(buf, "MESH_INTERFACE_ADD") == 0) { + reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add( + wpa_s, "", reply, reply_size); + } else if (os_strncmp(buf, "MESH_GROUP_ADD ", 15) == 0) { + if (wpa_supplicant_ctrl_iface_mesh_group_add(wpa_s, buf + 15)) + reply_len = -1; + } else if (os_strncmp(buf, "MESH_GROUP_REMOVE ", 18) == 0) { + if (wpa_supplicant_ctrl_iface_mesh_group_remove(wpa_s, + buf + 18)) + reply_len = -1; +#endif /* CONFIG_MESH */ #ifdef CONFIG_P2P } else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) { - if (p2p_ctrl_find(wpa_s, buf + 9)) + if (p2p_ctrl_find(wpa_s, buf + 8)) 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_ASP_PROVISION ", 18) == 0) { + if (p2p_ctrl_asp_provision(wpa_s, buf + 18)) + reply_len = -1; + } else if (os_strncmp(buf, "P2P_ASP_PROVISION_RESP ", 23) == 0) { + if (p2p_ctrl_asp_provision_resp(wpa_s, buf + 23)) + reply_len = -1; } else if (os_strncmp(buf, "P2P_CONNECT ", 12) == 0) { reply_len = p2p_ctrl_connect(wpa_s, buf + 12, reply, reply_size); @@ -5005,7 +8066,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, 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, 0)) + if (wpas_p2p_group_add(wpa_s, 0, 0, 0, 0)) reply_len = -1; } else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) { if (p2p_ctrl_group_add(wpa_s, buf + 14)) @@ -5037,6 +8098,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } 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_SERVICE_REP ", 16) == 0) { + if (p2p_ctrl_service_replace(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; @@ -5050,10 +8114,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, 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); + p2p_ctrl_flush(wpa_s); } else if (os_strncmp(buf, "P2P_UNAUTHORIZE ", 16) == 0) { if (wpas_p2p_unauthorize(wpa_s, buf + 16) < 0) reply_len = -1; @@ -5072,6 +8133,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strcmp(buf, "P2P_EXT_LISTEN") == 0) { if (p2p_ctrl_ext_listen(wpa_s, "") < 0) reply_len = -1; + } else if (os_strncmp(buf, "P2P_REMOVE_CLIENT ", 18) == 0) { + if (p2p_ctrl_remove_client(wpa_s, buf + 18) < 0) + reply_len = -1; #endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY } else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) { @@ -5087,13 +8151,26 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, 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) + } else if (os_strcmp(buf, "INTERWORKING_SELECT") == 0) { + if (ctrl_interworking_select(wpa_s, NULL) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "INTERWORKING_SELECT ", 20) == 0) { + if (ctrl_interworking_select(wpa_s, buf + 20) < 0) reply_len = -1; } else if (os_strncmp(buf, "INTERWORKING_CONNECT ", 21) == 0) { - if (ctrl_interworking_connect(wpa_s, buf + 21) < 0) + if (ctrl_interworking_connect(wpa_s, buf + 21, 0) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "INTERWORKING_ADD_NETWORK ", 25) == 0) { + int id; + + id = ctrl_interworking_connect(wpa_s, buf + 25, 1); + if (id < 0) reply_len = -1; + else { + reply_len = os_snprintf(reply, reply_size, "%d\n", id); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } } else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) { if (get_anqp(wpa_s, buf + 9) < 0) reply_len = -1; @@ -5111,14 +8188,28 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "HS20_GET_NAI_HOME_REALM_LIST ", 29) == 0) { if (hs20_get_nai_home_realm_list(wpa_s, buf + 29) < 0) reply_len = -1; + } else if (os_strncmp(buf, "HS20_ICON_REQUEST ", 18) == 0) { + if (hs20_icon_request(wpa_s, buf + 18) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "FETCH_OSU") == 0) { + if (hs20_fetch_osu(wpa_s) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "CANCEL_FETCH_OSU") == 0) { + hs20_cancel_fetch_osu(wpa_s); #endif /* CONFIG_HS20 */ } else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0) { if (wpa_supplicant_ctrl_iface_ctrl_rsp( wpa_s, buf + os_strlen(WPA_CTRL_RSP))) reply_len = -1; - else - ctrl_rsp = 1; + else { + /* + * Notify response from timeout to allow the control + * interface response to be sent first. + */ + eloop_register_timeout(0, 0, wpas_ctrl_eapol_response, + wpa_s, NULL); + } } else if (os_strcmp(buf, "RECONFIGURE") == 0) { if (wpa_supplicant_reload_configuration(wpa_s)) reply_len = -1; @@ -5133,9 +8224,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } 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_strncmp(buf, "LIST_NETWORKS ", 14) == 0) { + reply_len = wpa_supplicant_ctrl_iface_list_networks( + wpa_s, buf + 14, reply, reply_size); } else if (os_strcmp(buf, "LIST_NETWORKS") == 0) { reply_len = wpa_supplicant_ctrl_iface_list_networks( - wpa_s, reply, reply_size); + wpa_s, NULL, reply, reply_size); } else if (os_strcmp(buf, "DISCONNECT") == 0) { #ifdef CONFIG_SME wpa_s->sme.prev_bssid_set = 0; @@ -5147,29 +8241,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); } else if (os_strcmp(buf, "SCAN") == 0) { - if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) - reply_len = -1; - else { - if (!wpa_s->sched_scanning && !wpa_s->scanning && - ((wpa_s->wpa_state <= WPA_SCANNING) || - (wpa_s->wpa_state == WPA_COMPLETED))) { - wpa_s->normal_scans = 0; - wpa_s->scan_req = MANUAL_SCAN_REQ; - wpa_supplicant_req_scan(wpa_s, 0, 0); - } else if (wpa_s->sched_scanning) { - wpa_printf(MSG_DEBUG, "Stop ongoing " - "sched_scan to allow requested " - "full scan to proceed"); - wpa_supplicant_cancel_sched_scan(wpa_s); - wpa_s->scan_req = MANUAL_SCAN_REQ; - 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"); - } - } + wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, &reply_len); + } else if (os_strncmp(buf, "SCAN ", 5) == 0) { + wpas_ctrl_scan(wpa_s, buf + 5, reply, reply_size, &reply_len); } else if (os_strcmp(buf, "SCAN_RESULTS") == 0) { reply_len = wpa_supplicant_ctrl_iface_scan_results( wpa_s, reply, reply_size); @@ -5194,6 +8268,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "GET_NETWORK ", 12) == 0) { reply_len = wpa_supplicant_ctrl_iface_get_network( wpa_s, buf + 12, reply, reply_size); + } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) { + if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12)) + reply_len = -1; } else if (os_strcmp(buf, "LIST_CREDS") == 0) { reply_len = wpa_supplicant_ctrl_iface_list_creds( wpa_s, reply, reply_size); @@ -5206,6 +8283,10 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "SET_CRED ", 9) == 0) { if (wpa_supplicant_ctrl_iface_set_cred(wpa_s, buf + 9)) reply_len = -1; + } else if (os_strncmp(buf, "GET_CRED ", 9) == 0) { + reply_len = wpa_supplicant_ctrl_iface_get_cred(wpa_s, buf + 9, + reply, + reply_size); #ifndef CONFIG_NO_CONFIG_WRITE } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) { if (wpa_supplicant_ctrl_iface_save_config(wpa_s)) @@ -5244,19 +8325,26 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) { if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13)) reply_len = -1; + } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) { + if (ap_ctrl_iface_chanswitch(wpa_s, buf + 12)) + reply_len = -1; + } else if (os_strcmp(buf, "STOP_AP") == 0) { + if (wpas_ap_stop_ap(wpa_s)) + reply_len = -1; #endif /* CONFIG_AP */ } else if (os_strcmp(buf, "SUSPEND") == 0) { wpas_notify_suspend(wpa_s->global); } else if (os_strcmp(buf, "RESUME") == 0) { wpas_notify_resume(wpa_s->global); +#ifdef CONFIG_TESTING_OPTIONS } else if (os_strcmp(buf, "DROP_SA") == 0) { wpa_supplicant_ctrl_iface_drop_sa(wpa_s); +#endif /* CONFIG_TESTING_OPTIONS */ } 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; + wpa_s->auto_reconnect_disabled = atoi(buf + 16) == 0; } 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; @@ -5265,8 +8353,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, buf + 17)) reply_len = -1; } else if (os_strncmp(buf, "BSS_FLUSH ", 10) == 0) { - if (wpa_supplicant_ctrl_iface_bss_flush(wpa_s, buf + 10)) - reply_len = -1; + wpa_supplicant_ctrl_iface_bss_flush(wpa_s, buf + 10); #ifdef CONFIG_TDLS } else if (os_strncmp(buf, "TDLS_DISCOVER ", 14) == 0) { if (wpa_supplicant_ctrl_iface_tdls_discover(wpa_s, buf + 14)) @@ -5277,7 +8364,23 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "TDLS_TEARDOWN ", 14) == 0) { if (wpa_supplicant_ctrl_iface_tdls_teardown(wpa_s, buf + 14)) reply_len = -1; + } else if (os_strncmp(buf, "TDLS_CHAN_SWITCH ", 17) == 0) { + if (wpa_supplicant_ctrl_iface_tdls_chan_switch(wpa_s, + buf + 17)) + reply_len = -1; + } else if (os_strncmp(buf, "TDLS_CANCEL_CHAN_SWITCH ", 24) == 0) { + if (wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(wpa_s, + buf + 24)) + reply_len = -1; #endif /* CONFIG_TDLS */ + } else if (os_strcmp(buf, "WMM_AC_STATUS") == 0) { + reply_len = wpas_wmm_ac_status(wpa_s, reply, reply_size); + } else if (os_strncmp(buf, "WMM_AC_ADDTS ", 13) == 0) { + if (wmm_ac_ctrl_addts(wpa_s, buf + 13)) + reply_len = -1; + } else if (os_strncmp(buf, "WMM_AC_DELTS ", 13) == 0) { + if (wmm_ac_ctrl_delts(wpa_s, buf + 13)) + reply_len = -1; } else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) { reply_len = wpa_supplicant_signal_poll(wpa_s, reply, reply_size); @@ -5289,6 +8392,14 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpa_supplicant_ctrl_iface_autoscan(wpa_s, buf + 9)) reply_len = -1; #endif /* CONFIG_AUTOSCAN */ +#ifdef ANDROID + } else if (os_strncmp(buf, "DRIVER ", 7) == 0) { + reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, reply, + reply_size); +#endif /* ANDROID */ + } else if (os_strncmp(buf, "VENDOR ", 7) == 0) { + reply_len = wpa_supplicant_vendor_cmd(wpa_s, buf + 7, reply, + reply_size); } else if (os_strcmp(buf, "REAUTHENTICATE") == 0) { pmksa_cache_clear_current(wpa_s->wpa); eapol_sm_request_reauth(wpa_s->eapol); @@ -5296,7 +8407,59 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "WNM_SLEEP ", 10) == 0) { if (wpas_ctrl_iface_wnm_sleep(wpa_s, buf + 10)) reply_len = -1; + } else if (os_strncmp(buf, "WNM_BSS_QUERY ", 14) == 0) { + if (wpas_ctrl_iface_wnm_bss_query(wpa_s, buf + 14)) + reply_len = -1; #endif /* CONFIG_WNM */ + } else if (os_strcmp(buf, "FLUSH") == 0) { + wpa_supplicant_ctrl_iface_flush(wpa_s); + } else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) { + reply_len = wpas_ctrl_radio_work(wpa_s, buf + 11, reply, + reply_size); +#ifdef CONFIG_TESTING_OPTIONS + } else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) { + if (wpas_ctrl_iface_mgmt_tx(wpa_s, buf + 8) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "MGMT_TX_DONE") == 0) { + wpas_ctrl_iface_mgmt_tx_done(wpa_s); + } else if (os_strncmp(buf, "DRIVER_EVENT ", 13) == 0) { + if (wpas_ctrl_iface_driver_event(wpa_s, buf + 13) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) { + if (wpas_ctrl_iface_eapol_rx(wpa_s, buf + 9) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DATA_TEST_CONFIG ", 17) == 0) { + if (wpas_ctrl_iface_data_test_config(wpa_s, buf + 17) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DATA_TEST_TX ", 13) == 0) { + if (wpas_ctrl_iface_data_test_tx(wpa_s, buf + 13) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) { + if (wpas_ctrl_iface_data_test_frame(wpa_s, buf + 16) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) { + if (wpas_ctrl_test_alloc_fail(wpa_s, buf + 16) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) { + reply_len = wpas_ctrl_get_alloc_fail(wpa_s, reply, reply_size); +#endif /* CONFIG_TESTING_OPTIONS */ + } else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) { + if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "VENDOR_ELEM_GET ", 16) == 0) { + reply_len = wpas_ctrl_vendor_elem_get(wpa_s, buf + 16, reply, + reply_size); + } else if (os_strncmp(buf, "VENDOR_ELEM_REMOVE ", 19) == 0) { + if (wpas_ctrl_vendor_elem_remove(wpa_s, buf + 19) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "NEIGHBOR_REP_REQUEST", 20) == 0) { + if (wpas_ctrl_iface_send_neigbor_rep(wpa_s, buf + 20)) + reply_len = -1; + } else if (os_strcmp(buf, "ERP_FLUSH") == 0) { + wpas_ctrl_iface_erp_flush(wpa_s); + } else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) { + if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14)) + reply_len = -1; } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -5307,9 +8470,6 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = 5; } - if (ctrl_rsp) - eapol_sm_notify_ctrl_response(wpa_s->eapol); - *resp_len = reply_len; return reply; } @@ -5388,7 +8548,7 @@ static int wpa_supplicant_global_iface_add(struct wpa_global *global, if (wpa_supplicant_get_iface(global, iface.ifname)) return -1; - return wpa_supplicant_add_iface(global, &iface) ? 0 : -1; + return wpa_supplicant_add_iface(global, &iface, NULL) ? 0 : -1; } @@ -5450,7 +8610,7 @@ static int wpa_supplicant_global_iface_list(struct wpa_global *global, res = os_snprintf(pos, end - pos, "%s\t%s\t%s\n", tmp->drv_name, tmp->ifname, tmp->desc ? tmp->desc : ""); - if (res < 0 || res >= end - pos) { + if (os_snprintf_error(end - pos, res)) { *pos = '\0'; break; } @@ -5476,7 +8636,7 @@ static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, while (wpa_s) { res = os_snprintf(pos, end - pos, "%s\n", wpa_s->ifname); - if (res < 0 || res >= end - pos) { + if (os_snprintf_error(end - pos, res)) { *pos = '\0'; break; } @@ -5487,6 +8647,247 @@ static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, } +static char * wpas_global_ctrl_iface_ifname(struct wpa_global *global, + const char *ifname, + char *cmd, size_t *resp_len) +{ + struct wpa_supplicant *wpa_s; + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (os_strcmp(ifname, wpa_s->ifname) == 0) + break; + } + + if (wpa_s == NULL) { + char *resp = os_strdup("FAIL-NO-IFNAME-MATCH\n"); + if (resp) + *resp_len = os_strlen(resp); + else + *resp_len = 1; + return resp; + } + + return wpa_supplicant_ctrl_iface_process(wpa_s, cmd, resp_len); +} + + +static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global, + char *buf, size_t *resp_len) +{ +#ifdef CONFIG_P2P + static const char * cmd[] = { + "LIST_NETWORKS", + "P2P_FIND", + "P2P_STOP_FIND", + "P2P_LISTEN", + "P2P_GROUP_ADD", + "P2P_GET_PASSPHRASE", + "P2P_SERVICE_UPDATE", + "P2P_SERVICE_FLUSH", + "P2P_FLUSH", + "P2P_CANCEL", + "P2P_PRESENCE_REQ", + "P2P_EXT_LISTEN", + NULL + }; + static const char * prefix[] = { +#ifdef ANDROID + "DRIVER ", +#endif /* ANDROID */ + "GET_NETWORK ", + "REMOVE_NETWORK ", + "P2P_FIND ", + "P2P_CONNECT ", + "P2P_LISTEN ", + "P2P_GROUP_REMOVE ", + "P2P_GROUP_ADD ", + "P2P_PROV_DISC ", + "P2P_SERV_DISC_REQ ", + "P2P_SERV_DISC_CANCEL_REQ ", + "P2P_SERV_DISC_RESP ", + "P2P_SERV_DISC_EXTERNAL ", + "P2P_SERVICE_ADD ", + "P2P_SERVICE_DEL ", + "P2P_SERVICE_REP ", + "P2P_REJECT ", + "P2P_INVITE ", + "P2P_PEER ", + "P2P_SET ", + "P2P_UNAUTHORIZE ", + "P2P_PRESENCE_REQ ", + "P2P_EXT_LISTEN ", + "P2P_REMOVE_CLIENT ", + "WPS_NFC_TOKEN ", + "WPS_NFC_TAG_READ ", + "NFC_GET_HANDOVER_SEL ", + "NFC_GET_HANDOVER_REQ ", + "NFC_REPORT_HANDOVER ", + "P2P_ASP_PROVISION ", + "P2P_ASP_PROVISION_RESP ", + NULL + }; + int found = 0; + int i; + + if (global->p2p_init_wpa_s == NULL) + return NULL; + + for (i = 0; !found && cmd[i]; i++) { + if (os_strcmp(buf, cmd[i]) == 0) + found = 1; + } + + for (i = 0; !found && prefix[i]; i++) { + if (os_strncmp(buf, prefix[i], os_strlen(prefix[i])) == 0) + found = 1; + } + + if (found) + return wpa_supplicant_ctrl_iface_process(global->p2p_init_wpa_s, + buf, resp_len); +#endif /* CONFIG_P2P */ + return NULL; +} + + +static char * wpas_global_ctrl_iface_redir_wfd(struct wpa_global *global, + char *buf, size_t *resp_len) +{ +#ifdef CONFIG_WIFI_DISPLAY + if (global->p2p_init_wpa_s == NULL) + return NULL; + if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0 || + os_strncmp(buf, "WFD_SUBELEM_GET ", 16) == 0) + return wpa_supplicant_ctrl_iface_process(global->p2p_init_wpa_s, + buf, resp_len); +#endif /* CONFIG_WIFI_DISPLAY */ + return NULL; +} + + +static char * wpas_global_ctrl_iface_redir(struct wpa_global *global, + char *buf, size_t *resp_len) +{ + char *ret; + + ret = wpas_global_ctrl_iface_redir_p2p(global, buf, resp_len); + if (ret) + return ret; + + ret = wpas_global_ctrl_iface_redir_wfd(global, buf, resp_len); + if (ret) + return ret; + + return NULL; +} + + +static int wpas_global_ctrl_iface_set(struct wpa_global *global, char *cmd) +{ + char *value; + + value = os_strchr(cmd, ' '); + if (value == NULL) + return -1; + *value++ = '\0'; + + wpa_printf(MSG_DEBUG, "GLOBAL_CTRL_IFACE SET '%s'='%s'", cmd, value); + +#ifdef CONFIG_WIFI_DISPLAY + if (os_strcasecmp(cmd, "wifi_display") == 0) { + wifi_display_enable(global, !!atoi(value)); + return 0; + } +#endif /* CONFIG_WIFI_DISPLAY */ + + /* Restore cmd to its original value to allow redirection */ + value[-1] = ' '; + + return -1; +} + + +#ifndef CONFIG_NO_CONFIG_WRITE +static int wpas_global_ctrl_iface_save_config(struct wpa_global *global) +{ + int ret = 0, saved = 0; + struct wpa_supplicant *wpa_s; + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (!wpa_s->conf->update_config) { + wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed to update configuration (update_config=0)"); + continue; + } + + if (wpa_config_write(wpa_s->confname, wpa_s->conf)) { + wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to update configuration"); + ret = 1; + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration updated"); + saved++; + } + } + + if (!saved && !ret) { + wpa_dbg(wpa_s, MSG_DEBUG, + "CTRL_IFACE: SAVE_CONFIG - No configuration files could be updated"); + ret = 1; + } + + return ret; +} +#endif /* CONFIG_NO_CONFIG_WRITE */ + + +static int wpas_global_ctrl_iface_status(struct wpa_global *global, + char *buf, size_t buflen) +{ + char *pos, *end; + int ret; + struct wpa_supplicant *wpa_s; + + pos = buf; + end = buf + buflen; + +#ifdef CONFIG_P2P + if (global->p2p && !global->p2p_disabled) { + ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR + "\n" + "p2p_state=%s\n", + MAC2STR(global->p2p_dev_addr), + p2p_get_state_txt(global->p2p)); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } else if (global->p2p) { + ret = os_snprintf(pos, end - pos, "p2p_state=DISABLED\n"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_WIFI_DISPLAY + ret = os_snprintf(pos, end - pos, "wifi_display=%d\n", + !!global->wifi_display); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; +#endif /* CONFIG_WIFI_DISPLAY */ + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + ret = os_snprintf(pos, end - pos, "ifname=%s\n" + "address=" MACSTR "\n", + wpa_s->ifname, MAC2STR(wpa_s->own_addr)); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + return pos - buf; +} + + char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, char *buf, size_t *resp_len) { @@ -5495,6 +8896,20 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, int reply_len; int level = MSG_DEBUG; + if (os_strncmp(buf, "IFNAME=", 7) == 0) { + char *pos = os_strchr(buf + 7, ' '); + if (pos) { + *pos++ = '\0'; + return wpas_global_ctrl_iface_ifname(global, + buf + 7, pos, + resp_len); + } + } + + reply = wpas_global_ctrl_iface_redir(global, buf, resp_len); + if (reply) + return reply; + if (os_strcmp(buf, "PING") == 0) level = MSG_EXCESSIVE; wpa_hexdump_ascii(level, "RX global ctrl_iface", @@ -5530,6 +8945,37 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global, wpas_notify_suspend(global); } else if (os_strcmp(buf, "RESUME") == 0) { wpas_notify_resume(global); + } else if (os_strncmp(buf, "SET ", 4) == 0) { + if (wpas_global_ctrl_iface_set(global, buf + 4)) { +#ifdef CONFIG_P2P + if (global->p2p_init_wpa_s) { + os_free(reply); + /* Check if P2P redirection would work for this + * command. */ + return wpa_supplicant_ctrl_iface_process( + global->p2p_init_wpa_s, + buf, resp_len); + } +#endif /* CONFIG_P2P */ + reply_len = -1; + } +#ifndef CONFIG_NO_CONFIG_WRITE + } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) { + if (wpas_global_ctrl_iface_save_config(global)) + reply_len = -1; +#endif /* CONFIG_NO_CONFIG_WRITE */ + } else if (os_strcmp(buf, "STATUS") == 0) { + reply_len = wpas_global_ctrl_iface_status(global, reply, + reply_size); +#ifdef CONFIG_MODULE_TESTS + } else if (os_strcmp(buf, "MODULE_TESTS") == 0) { + int wpas_module_tests(void); + if (wpas_module_tests() < 0) + reply_len = -1; +#endif /* CONFIG_MODULE_TESTS */ + } else if (os_strncmp(buf, "RELOG", 5) == 0) { + if (wpa_debug_reopen_file() < 0) + reply_len = -1; } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; diff --git a/wpa_supplicant/ctrl_iface.h b/wpa_supplicant/ctrl_iface.h index a329ef32a239d..d54cc076c4476 100644 --- a/wpa_supplicant/ctrl_iface.h +++ b/wpa_supplicant/ctrl_iface.h @@ -32,7 +32,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, char *buf, size_t *resp_len); /** - * wpa_supplicant_ctrl_iface_process - Process global ctrl_iface command + * wpa_supplicant_global_ctrl_iface_process - Process global ctrl_iface command * @global: Pointer to global data from wpa_supplicant_init() * @buf: Received command buffer (nul terminated string) * @resp_len: Variable to be set to the response length @@ -113,6 +113,8 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global); void wpa_supplicant_global_ctrl_iface_deinit( struct ctrl_iface_global_priv *priv); +void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s); + #else /* CONFIG_CTRL_IFACE */ static inline struct ctrl_iface_priv * @@ -148,6 +150,10 @@ wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) { } +static inline void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s) +{ +} + #endif /* CONFIG_CTRL_IFACE */ #endif /* CTRL_IFACE_H */ diff --git a/wpa_supplicant/ctrl_iface_named_pipe.c b/wpa_supplicant/ctrl_iface_named_pipe.c index fd417ff5178d4..dc02db213a486 100644 --- a/wpa_supplicant/ctrl_iface_named_pipe.c +++ b/wpa_supplicant/ctrl_iface_named_pipe.c @@ -423,7 +423,7 @@ static int ctrl_iface_parse(struct ctrl_iface_priv *priv, const char *params) } -static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, +static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global, const char *txt, size_t len) { struct wpa_supplicant *wpa_s = ctx; diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c index 994f9b18c091b..bf6a3df6c3c51 100644 --- a/wpa_supplicant/ctrl_iface_udp.c +++ b/wpa_supplicant/ctrl_iface_udp.c @@ -30,7 +30,11 @@ */ struct wpa_ctrl_dst { struct wpa_ctrl_dst *next; +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + struct sockaddr_in6 addr; +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ struct sockaddr_in addr; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ socklen_t addrlen; int debug_level; int errors; @@ -51,43 +55,73 @@ static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv, +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + struct sockaddr_in6 *from, +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ struct sockaddr_in *from, +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ socklen_t fromlen) { struct wpa_ctrl_dst *dst; +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + char addr[INET6_ADDRSTRLEN]; +#endif /* CONFIG_UDP_IPV6 */ dst = os_zalloc(sizeof(*dst)); if (dst == NULL) return -1; - os_memcpy(&dst->addr, from, sizeof(struct sockaddr_in)); + os_memcpy(&dst->addr, from, sizeof(*from)); dst->addrlen = fromlen; dst->debug_level = MSG_INFO; dst->next = priv->ctrl_dst; priv->ctrl_dst = dst; +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d", + inet_ntop(AF_INET6, &from->sin6_addr, addr, sizeof(*from)), + ntohs(from->sin6_port)); +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d", inet_ntoa(from->sin_addr), ntohs(from->sin_port)); +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ return 0; } static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv, +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + struct sockaddr_in6 *from, +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ struct sockaddr_in *from, +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ socklen_t fromlen) { struct wpa_ctrl_dst *dst, *prev = NULL; +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + char addr[INET6_ADDRSTRLEN]; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ dst = priv->ctrl_dst; while (dst) { +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + if (from->sin6_port == dst->addr.sin6_port && + !os_memcmp(&from->sin6_addr, &dst->addr.sin6_addr, + sizeof(from->sin6_addr))) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached %s:%d", + inet_ntop(AF_INET6, &from->sin6_addr, addr, + sizeof(*from)), + ntohs(from->sin6_port)); +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr && from->sin_port == dst->addr.sin_port) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached " + "%s:%d", inet_ntoa(from->sin_addr), + ntohs(from->sin_port)); +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ if (prev == NULL) priv->ctrl_dst = dst->next; else prev->next = dst->next; os_free(dst); - wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached " - "%s:%d", inet_ntoa(from->sin_addr), - ntohs(from->sin_port)); return 0; } prev = dst; @@ -98,21 +132,38 @@ static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv, static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv, +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + struct sockaddr_in6 *from, +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ struct sockaddr_in *from, +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ socklen_t fromlen, char *level) { struct wpa_ctrl_dst *dst; +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + char addr[INET6_ADDRSTRLEN]; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level); dst = priv->ctrl_dst; while (dst) { +#if CONFIG_CTRL_IFACE_UDP_IPV6 + if (from->sin6_port == dst->addr.sin6_port && + !os_memcmp(&from->sin6_addr, &dst->addr.sin6_addr, + sizeof(from->sin6_addr))) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor level %s:%d", + inet_ntop(AF_INET6, &from->sin6_addr, addr, + sizeof(*from)), + ntohs(from->sin6_port)); +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr && from->sin_port == dst->addr.sin_port) { wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor " "level %s:%d", inet_ntoa(from->sin_addr), ntohs(from->sin_port)); +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ dst->debug_level = atoi(level); return 0; } @@ -150,7 +201,14 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, struct ctrl_iface_priv *priv = sock_ctx; char buf[256], *pos; int res; +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + struct sockaddr_in6 from; +#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE + char addr[INET6_ADDRSTRLEN]; +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ struct sockaddr_in from; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ socklen_t fromlen = sizeof(from); char *reply = NULL; size_t reply_len = 0; @@ -160,11 +218,19 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { - perror("recvfrom(ctrl_iface)"); + wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", + strerror(errno)); return; } #ifndef CONFIG_CTRL_IFACE_UDP_REMOTE +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + inet_ntop(AF_INET6, &from.sin6_addr, addr, sizeof(from)); + if (os_strcmp(addr, "::1")) { + wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected source %s", + addr); + } +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) { /* * The OS networking stack is expected to drop this kind of @@ -176,6 +242,7 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, "source %s", inet_ntoa(from.sin_addr)); return; } +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ buf[res] = '\0'; @@ -255,7 +322,7 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, } -static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, +static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global, const char *txt, size_t len) { struct wpa_supplicant *wpa_s = ctx; @@ -269,8 +336,14 @@ struct ctrl_iface_priv * wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) { struct ctrl_iface_priv *priv; - struct sockaddr_in addr; int port = WPA_CTRL_IFACE_PORT; +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + struct sockaddr_in6 addr; + int domain = PF_INET6; +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + struct sockaddr_in addr; + int domain = PF_INET; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ priv = os_zalloc(sizeof(*priv)); if (priv == NULL) @@ -282,26 +355,39 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) if (wpa_s->conf->ctrl_interface == NULL) return priv; - priv->sock = socket(PF_INET, SOCK_DGRAM, 0); + priv->sock = socket(domain, SOCK_DGRAM, 0); if (priv->sock < 0) { - perror("socket(PF_INET)"); + wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); goto fail; } os_memset(&addr, 0, sizeof(addr)); +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + addr.sin6_family = AF_INET6; +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + addr.sin6_addr = in6addr_any; +#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + inet_pton(AF_INET6, "::1", &addr.sin6_addr); +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ addr.sin_family = AF_INET; #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE addr.sin_addr.s_addr = INADDR_ANY; #else /* CONFIG_CTRL_IFACE_UDP_REMOTE */ addr.sin_addr.s_addr = htonl((127 << 24) | 1); #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ try_again: +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + addr.sin6_port = htons(port); +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ addr.sin_port = htons(port); +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { port--; if ((WPA_CTRL_IFACE_PORT - port) < WPA_CTRL_IFACE_PORT_LIMIT) goto try_again; - perror("bind(AF_INET)"); + wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno)); goto fail; } @@ -331,13 +417,13 @@ void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) eloop_unregister_read_sock(priv->sock); if (priv->ctrl_dst) { /* - * Wait a second before closing the control socket if + * Wait before closing the control socket if * there are any attached monitors in order to allow * them to receive any pending messages. */ wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached " "monitors to receive messages"); - os_sleep(1, 0); + os_sleep(0, 100000); } close(priv->sock); priv->sock = -1; @@ -362,6 +448,9 @@ static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, int idx; char *sbuf; int llen; +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + char addr[INET6_ADDRSTRLEN]; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ dst = priv->ctrl_dst; if (priv->sock < 0 || dst == NULL) @@ -381,13 +470,22 @@ static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, while (dst) { next = dst->next; if (level >= dst->debug_level) { +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d", + inet_ntop(AF_INET6, &dst->addr.sin6_addr, + addr, sizeof(dst->addr)), + ntohs(dst->addr.sin6_port)); +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d", inet_ntoa(dst->addr.sin_addr), ntohs(dst->addr.sin_port)); +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ if (sendto(priv->sock, sbuf, llen + len, 0, (struct sockaddr *) &dst->addr, sizeof(dst->addr)) < 0) { - perror("sendto(CTRL_IFACE monitor)"); + wpa_printf(MSG_ERROR, + "sendto(CTRL_IFACE monitor): %s", + strerror(errno)); dst->errors++; if (dst->errors > 10) { wpa_supplicant_ctrl_iface_detach( @@ -456,7 +554,8 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { - perror("recvfrom(ctrl_iface)"); + wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", + strerror(errno)); return; } @@ -539,7 +638,7 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) priv->sock = socket(PF_INET, SOCK_DGRAM, 0); if (priv->sock < 0) { - perror("socket(PF_INET)"); + wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); goto fail; } @@ -557,7 +656,7 @@ try_again: if ((port - WPA_GLOBAL_CTRL_IFACE_PORT) < WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT) goto try_again; - perror("bind(AF_INET)"); + wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno)); goto fail; } diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c index f79286369755b..b1ac76668897e 100644 --- a/wpa_supplicant/ctrl_iface_unix.c +++ b/wpa_supplicant/ctrl_iface_unix.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / UNIX domain socket -based control interface - * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -47,19 +47,37 @@ struct ctrl_iface_priv { struct wpa_supplicant *wpa_s; int sock; struct dl_list ctrl_dst; + int android_control_socket; }; -static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, +struct ctrl_iface_global_priv { + struct wpa_global *global; + int sock; + struct dl_list ctrl_dst; + int android_control_socket; +}; + + +static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, + const char *ifname, int sock, + struct dl_list *ctrl_dst, int level, const char *buf, - size_t len); + size_t len, + struct ctrl_iface_priv *priv, + struct ctrl_iface_global_priv *gp); +static int wpas_ctrl_iface_reinit(struct wpa_supplicant *wpa_s, + struct ctrl_iface_priv *priv); +static int wpas_ctrl_iface_global_reinit(struct wpa_global *global, + struct ctrl_iface_global_priv *priv); -static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv, +static int wpa_supplicant_ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_un *from, - socklen_t fromlen) + socklen_t fromlen, int global) { struct wpa_ctrl_dst *dst; + char addr_txt[200]; dst = os_zalloc(sizeof(*dst)); if (dst == NULL) @@ -67,31 +85,36 @@ static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv, os_memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); dst->addrlen = fromlen; dst->debug_level = MSG_INFO; - dl_list_add(&priv->ctrl_dst, &dst->list); - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached", - (u8 *) from->sun_path, - fromlen - offsetof(struct sockaddr_un, sun_path)); + dl_list_add(ctrl_dst, &dst->list); + printf_encode(addr_txt, sizeof(addr_txt), + (u8 *) from->sun_path, + fromlen - offsetof(struct sockaddr_un, sun_path)); + wpa_printf(MSG_DEBUG, "CTRL_IFACE %smonitor attached %s", + global ? "global " : "", addr_txt); return 0; } -static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv, +static int wpa_supplicant_ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_un *from, socklen_t fromlen) { struct wpa_ctrl_dst *dst; - dl_list_for_each(dst, &priv->ctrl_dst, struct wpa_ctrl_dst, list) { + dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) { if (fromlen == dst->addrlen && os_memcmp(from->sun_path, dst->addr.sun_path, fromlen - offsetof(struct sockaddr_un, sun_path)) == 0) { + char addr_txt[200]; + printf_encode(addr_txt, sizeof(addr_txt), + (u8 *) from->sun_path, + fromlen - + offsetof(struct sockaddr_un, sun_path)); + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached %s", + addr_txt); dl_list_del(&dst->list); os_free(dst); - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached", - (u8 *) from->sun_path, - fromlen - - offsetof(struct sockaddr_un, sun_path)); return 0; } } @@ -113,11 +136,13 @@ static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv, os_memcmp(from->sun_path, dst->addr.sun_path, fromlen - offsetof(struct sockaddr_un, sun_path)) == 0) { - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor " - "level", (u8 *) from->sun_path, - fromlen - - offsetof(struct sockaddr_un, sun_path)); + char addr_txt[200]; dst->debug_level = atoi(level); + printf_encode(addr_txt, sizeof(addr_txt), + (u8 *) from->sun_path, fromlen - + offsetof(struct sockaddr_un, sun_path)); + wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor level to %d for %s", + dst->debug_level, addr_txt); return 0; } } @@ -135,27 +160,30 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, int res; struct sockaddr_un from; socklen_t fromlen = sizeof(from); - char *reply = NULL; + char *reply = NULL, *reply_buf = NULL; size_t reply_len = 0; int new_attached = 0; res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { - perror("recvfrom(ctrl_iface)"); + wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", + strerror(errno)); return; } buf[res] = '\0'; if (os_strcmp(buf, "ATTACH") == 0) { - if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen)) + if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, &from, + fromlen, 0)) reply_len = 1; else { new_attached = 1; reply_len = 2; } } else if (os_strcmp(buf, "DETACH") == 0) { - if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen)) + if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst, &from, + fromlen)) reply_len = 1; else reply_len = 2; @@ -166,21 +194,49 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, else reply_len = 2; } else { - reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf, - &reply_len); + reply_buf = wpa_supplicant_ctrl_iface_process(wpa_s, buf, + &reply_len); + reply = reply_buf; + } + + if (!reply && reply_len == 1) { + reply = "FAIL\n"; + reply_len = 5; + } else if (!reply && reply_len == 2) { + reply = "OK\n"; + reply_len = 3; } if (reply) { - sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, - fromlen); - os_free(reply); - } else if (reply_len == 1) { - sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, - fromlen); - } else if (reply_len == 2) { - sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from, - fromlen); + if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, + fromlen) < 0) { + int _errno = errno; + wpa_dbg(wpa_s, MSG_DEBUG, + "ctrl_iface sendto failed: %d - %s", + _errno, strerror(_errno)); + if (_errno == ENOBUFS || _errno == EAGAIN) { + /* + * The socket send buffer could be full. This + * may happen if client programs are not + * receiving their pending messages. Close and + * reopen the socket as a workaround to avoid + * getting stuck being unable to send any new + * responses. + */ + sock = wpas_ctrl_iface_reinit(wpa_s, priv); + if (sock < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "Failed to reinitialize ctrl_iface socket"); + } + } + if (new_attached) { + wpa_dbg(wpa_s, MSG_DEBUG, "Failed to send response to ATTACH - detaching"); + new_attached = 0; + wpa_supplicant_ctrl_iface_detach( + &priv->ctrl_dst, &from, fromlen); + } + } } + os_free(reply_buf); if (new_attached) eapol_sm_notify_ctrl_attached(wpa_s->eapol); @@ -191,7 +247,7 @@ static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s) { char *buf; size_t len; - char *pbuf, *dir = NULL, *gid_str = NULL; + char *pbuf, *dir = NULL; int res; if (wpa_s->conf->ctrl_interface == NULL) @@ -201,12 +257,11 @@ static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s) if (pbuf == NULL) return NULL; if (os_strncmp(pbuf, "DIR=", 4) == 0) { + char *gid_str; dir = pbuf + 4; gid_str = os_strstr(dir, " GROUP="); - if (gid_str) { + if (gid_str) *gid_str = '\0'; - gid_str += 7; - } } else dir = pbuf; @@ -218,7 +273,7 @@ static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s) } res = os_snprintf(buf, len, "%s/%s", dir, wpa_s->ifname); - if (res < 0 || (size_t) res >= len) { + if (os_snprintf_error(len, res)) { os_free(pbuf); os_free(buf); return NULL; @@ -240,20 +295,38 @@ static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s) } -static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, +static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global, const char *txt, size_t len) { struct wpa_supplicant *wpa_s = ctx; - if (wpa_s == NULL || wpa_s->ctrl_iface == NULL) + + if (wpa_s == NULL) return; - wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len); + + if (global != 2 && wpa_s->global->ctrl_iface) { + struct ctrl_iface_global_priv *priv = wpa_s->global->ctrl_iface; + if (!dl_list_empty(&priv->ctrl_dst)) { + wpa_supplicant_ctrl_iface_send(wpa_s, global ? NULL : + wpa_s->ifname, + priv->sock, + &priv->ctrl_dst, + level, txt, len, NULL, + priv); + } + } + + if (wpa_s->ctrl_iface == NULL) + return; + wpa_supplicant_ctrl_iface_send(wpa_s, NULL, wpa_s->ctrl_iface->sock, + &wpa_s->ctrl_iface->ctrl_dst, + level, txt, len, wpa_s->ctrl_iface, + NULL); } -struct ctrl_iface_priv * -wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) +static int wpas_ctrl_iface_open_sock(struct wpa_supplicant *wpa_s, + struct ctrl_iface_priv *priv) { - struct ctrl_iface_priv *priv; struct sockaddr_un addr; char *fname = NULL; gid_t gid = 0; @@ -263,16 +336,6 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) char *endp; int flags; - priv = os_zalloc(sizeof(*priv)); - if (priv == NULL) - return NULL; - dl_list_init(&priv->ctrl_dst); - priv->wpa_s = wpa_s; - priv->sock = -1; - - if (wpa_s->conf->ctrl_interface == NULL) - return priv; - buf = os_strdup(wpa_s->conf->ctrl_interface); if (buf == NULL) goto fail; @@ -280,8 +343,10 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) 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) + if (priv->sock >= 0) { + priv->android_control_socket = 1; goto havesock; + } #endif /* ANDROID */ if (os_strncmp(buf, "DIR=", 4) == 0) { dir = buf + 4; @@ -300,7 +365,8 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) wpa_printf(MSG_DEBUG, "Using existing control " "interface directory."); } else { - perror("mkdir[ctrl_interface]"); + wpa_printf(MSG_ERROR, "mkdir[ctrl_interface=%s]: %s", + dir, strerror(errno)); goto fail; } } @@ -344,7 +410,8 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) } if (gid_set && chown(dir, -1, gid) < 0) { - perror("chown[ctrl_interface]"); + wpa_printf(MSG_ERROR, "chown[ctrl_interface=%s,gid=%d]: %s", + dir, (int) gid, strerror(errno)); goto fail; } @@ -364,7 +431,7 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0); if (priv->sock < 0) { - perror("socket(PF_UNIX)"); + wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno)); goto fail; } @@ -386,15 +453,15 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) " allow connections - assuming it was left" "over from forced program termination"); if (unlink(fname) < 0) { - perror("unlink[ctrl_iface]"); - wpa_printf(MSG_ERROR, "Could not unlink " - "existing ctrl_iface socket '%s'", - fname); + wpa_printf(MSG_ERROR, + "Could not unlink existing ctrl_iface socket '%s': %s", + fname, strerror(errno)); goto fail; } if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("supp-ctrl-iface-init: bind(PF_UNIX)"); + wpa_printf(MSG_ERROR, "supp-ctrl-iface-init: bind(PF_UNIX): %s", + strerror(errno)); goto fail; } wpa_printf(MSG_DEBUG, "Successfully replaced leftover " @@ -411,12 +478,14 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) } if (gid_set && chown(fname, -1, gid) < 0) { - perror("chown[ctrl_interface/ifname]"); + wpa_printf(MSG_ERROR, "chown[ctrl_interface=%s,gid=%d]: %s", + fname, (int) gid, strerror(errno)); goto fail; } if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { - perror("chmod[ctrl_interface/ifname]"); + wpa_printf(MSG_ERROR, "chmod[ctrl_interface=%s]: %s", + fname, strerror(errno)); goto fail; } os_free(fname); @@ -433,7 +502,8 @@ havesock: if (flags >= 0) { flags |= O_NONBLOCK; if (fcntl(priv->sock, F_SETFL, flags) < 0) { - perror("fcntl(ctrl, O_NONBLOCK)"); + wpa_printf(MSG_INFO, "fcntl(ctrl, O_NONBLOCK): %s", + strerror(errno)); /* Not fatal, continue on.*/ } } @@ -443,18 +513,71 @@ havesock: wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); os_free(buf); - return priv; + return 0; fail: - if (priv->sock >= 0) + if (priv->sock >= 0) { close(priv->sock); - os_free(priv); + priv->sock = -1; + } if (fname) { unlink(fname); os_free(fname); } os_free(buf); - return NULL; + return -1; +} + + +struct ctrl_iface_priv * +wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s) +{ + struct ctrl_iface_priv *priv; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + dl_list_init(&priv->ctrl_dst); + priv->wpa_s = wpa_s; + priv->sock = -1; + + if (wpa_s->conf->ctrl_interface == NULL) + return priv; + + if (wpas_ctrl_iface_open_sock(wpa_s, priv) < 0) { + os_free(priv); + return NULL; + } + + return priv; +} + + +static int wpas_ctrl_iface_reinit(struct wpa_supplicant *wpa_s, + struct ctrl_iface_priv *priv) +{ + int res; + + if (priv->sock <= 0) + return -1; + + /* + * On Android, the control socket being used may be the socket + * that is created when wpa_supplicant is started as a /init.*.rc + * service. Such a socket is maintained as a key-value pair in + * Android's environment. Closing this control socket would leave us + * in a bad state with an invalid socket descriptor. + */ + if (priv->android_control_socket) + return priv->sock; + + eloop_unregister_read_sock(priv->sock); + close(priv->sock); + priv->sock = -1; + res = wpas_ctrl_iface_open_sock(wpa_s, priv); + if (res < 0) + return -1; + return priv->sock; } @@ -464,17 +587,17 @@ void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) if (priv->sock > -1) { char *fname; - char *buf, *dir = NULL, *gid_str = NULL; + char *buf, *dir = NULL; eloop_unregister_read_sock(priv->sock); if (!dl_list_empty(&priv->ctrl_dst)) { /* - * Wait a second before closing the control socket if + * Wait before closing the control socket if * there are any attached monitors in order to allow * them to receive any pending messages. */ wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached " "monitors to receive messages"); - os_sleep(1, 0); + os_sleep(0, 100000); } close(priv->sock); priv->sock = -1; @@ -484,16 +607,17 @@ void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) os_free(fname); } + if (priv->wpa_s->conf->ctrl_interface == NULL) + goto free_dst; buf = os_strdup(priv->wpa_s->conf->ctrl_interface); if (buf == NULL) goto free_dst; if (os_strncmp(buf, "DIR=", 4) == 0) { + char *gid_str; dir = buf + 4; gid_str = os_strstr(dir, " GROUP="); - if (gid_str) { + if (gid_str) *gid_str = '\0'; - gid_str += 7; - } } else dir = buf; @@ -503,7 +627,9 @@ void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv) "directory not empty - leaving it " "behind"); } else { - perror("rmdir[ctrl_interface]"); + wpa_printf(MSG_ERROR, + "rmdir[ctrl_interface=%s]: %s", + dir, strerror(errno)); } } os_free(buf); @@ -519,63 +645,109 @@ free_dst: /** * wpa_supplicant_ctrl_iface_send - Send a control interface packet to monitors - * @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init() + * @ifname: Interface name for global control socket or %NULL + * @sock: Local socket fd + * @ctrl_dst: List of attached listeners * @level: Priority level of the message * @buf: Message data * @len: Message length * * Send a packet to all monitor programs attached to the control interface. */ -static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, +static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s, + const char *ifname, int sock, + struct dl_list *ctrl_dst, int level, const char *buf, - size_t len) + size_t len, + struct ctrl_iface_priv *priv, + struct ctrl_iface_global_priv *gp) { struct wpa_ctrl_dst *dst, *next; char levelstr[10]; int idx, res; struct msghdr msg; - struct iovec io[2]; + struct iovec io[5]; - if (priv->sock < 0 || dl_list_empty(&priv->ctrl_dst)) + if (sock < 0 || dl_list_empty(ctrl_dst)) return; res = os_snprintf(levelstr, sizeof(levelstr), "<%d>", level); - if (res < 0 || (size_t) res >= sizeof(levelstr)) + if (os_snprintf_error(sizeof(levelstr), res)) return; - io[0].iov_base = levelstr; - io[0].iov_len = os_strlen(levelstr); - io[1].iov_base = (char *) buf; - io[1].iov_len = len; + idx = 0; + if (ifname) { + io[idx].iov_base = "IFNAME="; + io[idx].iov_len = 7; + idx++; + io[idx].iov_base = (char *) ifname; + io[idx].iov_len = os_strlen(ifname); + idx++; + io[idx].iov_base = " "; + io[idx].iov_len = 1; + idx++; + } + io[idx].iov_base = levelstr; + io[idx].iov_len = os_strlen(levelstr); + idx++; + io[idx].iov_base = (char *) buf; + io[idx].iov_len = len; + idx++; os_memset(&msg, 0, sizeof(msg)); msg.msg_iov = io; - msg.msg_iovlen = 2; + msg.msg_iovlen = idx; - idx = 0; - dl_list_for_each_safe(dst, next, &priv->ctrl_dst, struct wpa_ctrl_dst, - list) { - if (level >= dst->debug_level) { - wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send", - (u8 *) dst->addr.sun_path, dst->addrlen - - offsetof(struct sockaddr_un, sun_path)); - msg.msg_name = (void *) &dst->addr; - msg.msg_namelen = dst->addrlen; - if (sendmsg(priv->sock, &msg, 0) < 0) { - int _errno = errno; - wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: " - "%d - %s", - idx, errno, strerror(errno)); - dst->errors++; - if (dst->errors > 1000 || - (_errno != ENOBUFS && dst->errors > 10) || - _errno == ENOENT) { - wpa_supplicant_ctrl_iface_detach( - priv, &dst->addr, - dst->addrlen); - } - } else - dst->errors = 0; + dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) { + int _errno; + char addr_txt[200]; + + if (level < dst->debug_level) + continue; + + printf_encode(addr_txt, sizeof(addr_txt), + (u8 *) dst->addr.sun_path, dst->addrlen - + offsetof(struct sockaddr_un, sun_path)); + msg.msg_name = (void *) &dst->addr; + msg.msg_namelen = dst->addrlen; + if (sendmsg(sock, &msg, MSG_DONTWAIT) >= 0) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor sent successfully to %s", + addr_txt); + dst->errors = 0; + continue; + } + + _errno = errno; + wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor[%s]: %d - %s", + addr_txt, errno, strerror(errno)); + dst->errors++; + + if (dst->errors > 10 || _errno == ENOENT || _errno == EPERM) { + wpa_printf(MSG_INFO, "CTRL_IFACE: Detach monitor %s that cannot receive messages", + addr_txt); + wpa_supplicant_ctrl_iface_detach(ctrl_dst, &dst->addr, + dst->addrlen); + } + + if (_errno == ENOBUFS || _errno == EAGAIN) { + /* + * The socket send buffer could be full. This may happen + * if client programs are not receiving their pending + * messages. Close and reopen the socket as a workaround + * to avoid getting stuck being unable to send any new + * responses. + */ + if (priv) + sock = wpas_ctrl_iface_reinit(wpa_s, priv); + else if (gp) + sock = wpas_ctrl_iface_global_reinit( + wpa_s->global, gp); + else + break; + if (sock < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Failed to reinitialize ctrl_iface socket"); + break; + } } - idx++; } } @@ -595,27 +767,41 @@ void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) res = recvfrom(priv->sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { - perror("recvfrom(ctrl_iface)"); + wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", + strerror(errno)); continue; } buf[res] = '\0'; if (os_strcmp(buf, "ATTACH") == 0) { /* handle ATTACH signal of first monitor interface */ - if (!wpa_supplicant_ctrl_iface_attach(priv, &from, - fromlen)) { - sendto(priv->sock, "OK\n", 3, 0, - (struct sockaddr *) &from, fromlen); + if (!wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, + &from, fromlen, + 0)) { + if (sendto(priv->sock, "OK\n", 3, 0, + (struct sockaddr *) &from, fromlen) < + 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s", + strerror(errno)); + } /* OK to continue */ return; } else { - sendto(priv->sock, "FAIL\n", 5, 0, - (struct sockaddr *) &from, fromlen); + if (sendto(priv->sock, "FAIL\n", 5, 0, + (struct sockaddr *) &from, fromlen) < + 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s", + strerror(errno)); + } } } else { /* return FAIL for all other signals */ - sendto(priv->sock, "FAIL\n", 5, 0, - (struct sockaddr *) &from, fromlen); + if (sendto(priv->sock, "FAIL\n", 5, 0, + (struct sockaddr *) &from, fromlen) < 0) { + wpa_printf(MSG_DEBUG, + "ctrl_iface sendto failed: %s", + strerror(errno)); + } } } } @@ -623,72 +809,107 @@ void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv) /* Global ctrl_iface */ -struct ctrl_iface_global_priv { - struct wpa_global *global; - int sock; -}; - - static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct wpa_global *global = eloop_ctx; - char buf[256]; + struct ctrl_iface_global_priv *priv = sock_ctx; + char buf[4096]; int res; struct sockaddr_un from; socklen_t fromlen = sizeof(from); - char *reply; + char *reply = NULL, *reply_buf = NULL; size_t reply_len; res = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { - perror("recvfrom(ctrl_iface)"); + wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", + strerror(errno)); return; } buf[res] = '\0'; - reply = wpa_supplicant_global_ctrl_iface_process(global, buf, - &reply_len); + if (os_strcmp(buf, "ATTACH") == 0) { + if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, &from, + fromlen, 1)) + reply_len = 1; + else + reply_len = 2; + } else if (os_strcmp(buf, "DETACH") == 0) { + if (wpa_supplicant_ctrl_iface_detach(&priv->ctrl_dst, &from, + fromlen)) + reply_len = 1; + else + reply_len = 2; + } else { + reply_buf = wpa_supplicant_global_ctrl_iface_process( + global, buf, &reply_len); + reply = reply_buf; + } + + if (!reply && reply_len == 1) { + reply = "FAIL\n"; + reply_len = 5; + } else if (!reply && reply_len == 2) { + reply = "OK\n"; + reply_len = 3; + } if (reply) { - sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, - fromlen); - os_free(reply); - } else if (reply_len) { - sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, - fromlen); + if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s", + strerror(errno)); + } } + os_free(reply_buf); } -struct ctrl_iface_global_priv * -wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) +static int wpas_global_ctrl_iface_open_sock(struct wpa_global *global, + struct ctrl_iface_global_priv *priv) { - struct ctrl_iface_global_priv *priv; struct sockaddr_un addr; + const char *ctrl = global->params.ctrl_interface; + int flags; - priv = os_zalloc(sizeof(*priv)); - if (priv == NULL) - return NULL; - priv->global = global; - priv->sock = -1; - - if (global->params.ctrl_interface == NULL) - return priv; + wpa_printf(MSG_DEBUG, "Global control interface '%s'", ctrl); #ifdef ANDROID - priv->sock = android_get_control_socket(global->params.ctrl_interface); - if (priv->sock >= 0) + if (os_strncmp(ctrl, "@android:", 9) == 0) { + priv->sock = android_get_control_socket(ctrl + 9); + if (priv->sock < 0) { + wpa_printf(MSG_ERROR, "Failed to open Android control " + "socket '%s'", ctrl + 9); + goto fail; + } + wpa_printf(MSG_DEBUG, "Using Android control socket '%s'", + ctrl + 9); + priv->android_control_socket = 1; goto havesock; -#endif /* ANDROID */ + } - wpa_printf(MSG_DEBUG, "Global control interface '%s'", - global->params.ctrl_interface); + if (os_strncmp(ctrl, "@abstract:", 10) != 0) { + /* + * Backwards compatibility - try to open an Android control + * socket and if that fails, assume this was a UNIX domain + * socket instead. + */ + priv->sock = android_get_control_socket(ctrl); + if (priv->sock >= 0) { + wpa_printf(MSG_DEBUG, + "Using Android control socket '%s'", + ctrl); + priv->android_control_socket = 1; + goto havesock; + } + } +#endif /* ANDROID */ priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0); if (priv->sock < 0) { - perror("socket(PF_UNIX)"); + wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno)); goto fail; } @@ -697,66 +918,203 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) addr.sun_len = sizeof(addr); #endif /* __FreeBSD__ */ addr.sun_family = AF_UNIX; - os_strlcpy(addr.sun_path, global->params.ctrl_interface, - sizeof(addr.sun_path)); + + if (os_strncmp(ctrl, "@abstract:", 10) == 0) { + addr.sun_path[0] = '\0'; + os_strlcpy(addr.sun_path + 1, ctrl + 10, + sizeof(addr.sun_path) - 1); + if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < + 0) { + wpa_printf(MSG_ERROR, "supp-global-ctrl-iface-init: " + "bind(PF_UNIX;%s) failed: %s", + ctrl, strerror(errno)); + goto fail; + } + wpa_printf(MSG_DEBUG, "Using Abstract control socket '%s'", + ctrl + 10); + goto havesock; + } + + os_strlcpy(addr.sun_path, ctrl, sizeof(addr.sun_path)); if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("supp-global-ctrl-iface-init (will try fixup): " - "bind(PF_UNIX)"); + wpa_printf(MSG_INFO, "supp-global-ctrl-iface-init(%s) (will try fixup): bind(PF_UNIX): %s", + ctrl, strerror(errno)); if (connect(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" " allow connections - assuming it was left" "over from forced program termination"); - if (unlink(global->params.ctrl_interface) < 0) { - perror("unlink[ctrl_iface]"); - wpa_printf(MSG_ERROR, "Could not unlink " - "existing ctrl_iface socket '%s'", - global->params.ctrl_interface); + if (unlink(ctrl) < 0) { + wpa_printf(MSG_ERROR, + "Could not unlink existing ctrl_iface socket '%s': %s", + ctrl, strerror(errno)); goto fail; } if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("supp-glb-iface-init: bind(PF_UNIX)"); + wpa_printf(MSG_ERROR, "supp-glb-iface-init: bind(PF_UNIX;%s): %s", + ctrl, strerror(errno)); goto fail; } wpa_printf(MSG_DEBUG, "Successfully replaced leftover " "ctrl_iface socket '%s'", - global->params.ctrl_interface); + ctrl); } else { wpa_printf(MSG_INFO, "ctrl_iface exists and seems to " "be in use - cannot override it"); wpa_printf(MSG_INFO, "Delete '%s' manually if it is " "not used anymore", - global->params.ctrl_interface); + ctrl); goto fail; } } -#ifdef ANDROID + wpa_printf(MSG_DEBUG, "Using UNIX control socket '%s'", ctrl); + + if (global->params.ctrl_interface_group) { + char *gid_str = global->params.ctrl_interface_group; + gid_t gid = 0; + struct group *grp; + char *endp; + + grp = getgrnam(gid_str); + if (grp) { + gid = grp->gr_gid; + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d" + " (from group name '%s')", + (int) gid, gid_str); + } else { + /* Group name not found - try to parse this as gid */ + gid = strtol(gid_str, &endp, 10); + if (*gid_str == '\0' || *endp != '\0') { + wpa_printf(MSG_ERROR, "CTRL: Invalid group " + "'%s'", gid_str); + goto fail; + } + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", + (int) gid); + } + if (chown(ctrl, -1, gid) < 0) { + wpa_printf(MSG_ERROR, + "chown[global_ctrl_interface=%s,gid=%d]: %s", + ctrl, (int) gid, strerror(errno)); + goto fail; + } + + if (chmod(ctrl, S_IRWXU | S_IRWXG) < 0) { + wpa_printf(MSG_ERROR, + "chmod[global_ctrl_interface=%s]: %s", + ctrl, strerror(errno)); + goto fail; + } + } else { + if (chmod(ctrl, S_IRWXU) < 0) { + wpa_printf(MSG_DEBUG, + "chmod[global_ctrl_interface=%s](S_IRWXU): %s", + ctrl, strerror(errno)); + /* continue anyway since group change was not required + */ + } + } + havesock: -#endif /* ANDROID */ + + /* + * Make socket non-blocking so that we don't hang forever if + * target dies unexpectedly. + */ + flags = fcntl(priv->sock, F_GETFL); + if (flags >= 0) { + flags |= O_NONBLOCK; + if (fcntl(priv->sock, F_SETFL, flags) < 0) { + wpa_printf(MSG_INFO, "fcntl(ctrl, O_NONBLOCK): %s", + strerror(errno)); + /* Not fatal, continue on.*/ + } + } + eloop_register_read_sock(priv->sock, wpa_supplicant_global_ctrl_iface_receive, - global, NULL); + global, priv); - return priv; + return 0; fail: - if (priv->sock >= 0) + if (priv->sock >= 0) { close(priv->sock); - os_free(priv); - return NULL; + priv->sock = -1; + } + return -1; +} + + +struct ctrl_iface_global_priv * +wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global) +{ + struct ctrl_iface_global_priv *priv; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + dl_list_init(&priv->ctrl_dst); + priv->global = global; + priv->sock = -1; + + if (global->params.ctrl_interface == NULL) + return priv; + + if (wpas_global_ctrl_iface_open_sock(global, priv) < 0) { + os_free(priv); + return NULL; + } + + wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb); + + return priv; +} + + +static int wpas_ctrl_iface_global_reinit(struct wpa_global *global, + struct ctrl_iface_global_priv *priv) +{ + int res; + + if (priv->sock <= 0) + return -1; + + /* + * On Android, the control socket being used may be the socket + * that is created when wpa_supplicant is started as a /init.*.rc + * service. Such a socket is maintained as a key-value pair in + * Android's environment. Closing this control socket would leave us + * in a bad state with an invalid socket descriptor. + */ + if (priv->android_control_socket) + return priv->sock; + + eloop_unregister_read_sock(priv->sock); + close(priv->sock); + priv->sock = -1; + res = wpas_global_ctrl_iface_open_sock(global, priv); + if (res < 0) + return -1; + return priv->sock; } void wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv) { + struct wpa_ctrl_dst *dst, *prev; + if (priv->sock >= 0) { eloop_unregister_read_sock(priv->sock); close(priv->sock); } if (priv->global->params.ctrl_interface) unlink(priv->global->params.ctrl_interface); + dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst, + list) + os_free(dst); os_free(priv); } diff --git a/wpa_supplicant/dbus/Makefile b/wpa_supplicant/dbus/Makefile index d64c65ca2f6c2..f355ebef51d23 100644 --- a/wpa_supplicant/dbus/Makefile +++ b/wpa_supplicant/dbus/Makefile @@ -1,7 +1,7 @@ all: libwpadbus.a clean: - rm -f *~ *.o *.d + rm -f *~ *.o *.d *.gcno *.gcda *.gcov rm -f libwpadbus.a install: diff --git a/wpa_supplicant/dbus/dbus_common.c b/wpa_supplicant/dbus/dbus_common.c index 5d0e31e276c77..7ef6cad62aaf9 100644 --- a/wpa_supplicant/dbus/dbus_common.c +++ b/wpa_supplicant/dbus/dbus_common.c @@ -17,6 +17,7 @@ #include "dbus_common_i.h" #include "dbus_new.h" #include "dbus_old.h" +#include "../wpa_supplicant_i.h" #ifndef SIGPOLL @@ -164,6 +165,7 @@ static void process_timeout(void *eloop_ctx, void *sock_ctx) static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) { struct wpas_dbus_priv *priv = data; + if (!dbus_timeout_get_enabled(timeout)) return TRUE; @@ -179,6 +181,7 @@ static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) static void remove_timeout(DBusTimeout *timeout, void *data) { struct wpas_dbus_priv *priv = data; + eloop_cancel_timeout(process_timeout, priv, timeout); dbus_timeout_set_data(timeout, NULL, NULL); } @@ -243,8 +246,7 @@ static int integrate_with_eloop(struct wpas_dbus_priv *priv) remove_timeout, timeout_toggled, priv, NULL)) { - wpa_printf(MSG_ERROR, "dbus: Failed to set callback " - "functions"); + wpa_printf(MSG_ERROR, "dbus: Failed to set callback functions"); return -1; } @@ -257,6 +259,22 @@ static int integrate_with_eloop(struct wpas_dbus_priv *priv) } +static DBusHandlerResult disconnect_filter(DBusConnection *conn, + DBusMessage *message, void *data) +{ + struct wpas_dbus_priv *priv = data; + + if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, + "Disconnected")) { + wpa_printf(MSG_DEBUG, "dbus: bus disconnected, terminating"); + dbus_connection_set_exit_on_disconnect(conn, FALSE); + wpa_supplicant_terminate_proc(priv->global); + return DBUS_HANDLER_RESULT_HANDLED; + } else + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + + static int wpas_dbus_init_common(struct wpas_dbus_priv *priv) { DBusError error; @@ -265,9 +283,13 @@ static int wpas_dbus_init_common(struct wpas_dbus_priv *priv) /* Get a reference to the system bus */ dbus_error_init(&error); priv->con = dbus_bus_get(DBUS_BUS_SYSTEM, &error); - if (!priv->con) { - wpa_printf(MSG_ERROR, "dbus: Could not acquire the system " - "bus: %s - %s", error.name, error.message); + if (priv->con) { + dbus_connection_add_filter(priv->con, disconnect_filter, priv, + NULL); + } else { + wpa_printf(MSG_ERROR, + "dbus: Could not acquire the system bus: %s - %s", + error.name, error.message); ret = -1; } dbus_error_free(&error); @@ -289,7 +311,7 @@ static int wpas_dbus_init_common_finish(struct wpas_dbus_priv *priv) * FIXME: is there a better solution to this problem? */ eloop_register_timeout(0, 50, dispatch_initial_dbus_messages, - priv->con, NULL); + priv->con, NULL); return 0; } @@ -300,10 +322,15 @@ static void wpas_dbus_deinit_common(struct wpas_dbus_priv *priv) if (priv->con) { eloop_cancel_timeout(dispatch_initial_dbus_messages, priv->con, NULL); + eloop_cancel_timeout(process_timeout, priv, ELOOP_ALL_CTX); + dbus_connection_set_watch_functions(priv->con, NULL, NULL, NULL, NULL, NULL); dbus_connection_set_timeout_functions(priv->con, NULL, NULL, NULL, NULL, NULL); + dbus_connection_remove_filter(priv->con, disconnect_filter, + priv); + dbus_connection_unref(priv->con); } @@ -320,26 +347,14 @@ struct wpas_dbus_priv * wpas_dbus_init(struct wpa_global *global) return NULL; priv->global = global; - if (wpas_dbus_init_common(priv) < 0) { - wpas_dbus_deinit(priv); - return NULL; - } - + if (wpas_dbus_init_common(priv) < 0 || #ifdef CONFIG_CTRL_IFACE_DBUS_NEW - if (wpas_dbus_ctrl_iface_init(priv) < 0) { - wpas_dbus_deinit(priv); - return NULL; - } + wpas_dbus_ctrl_iface_init(priv) < 0 || #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ - #ifdef CONFIG_CTRL_IFACE_DBUS - if (wpa_supplicant_dbus_ctrl_iface_init(priv) < 0) { - wpas_dbus_deinit(priv); - return NULL; - } + wpa_supplicant_dbus_ctrl_iface_init(priv) < 0 || #endif /* CONFIG_CTRL_IFACE_DBUS */ - - if (wpas_dbus_init_common_finish(priv) < 0) { + wpas_dbus_init_common_finish(priv) < 0) { wpas_dbus_deinit(priv); return NULL; } diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c index 61a94304bcbe3..a0c44ebfa41d9 100644 --- a/wpa_supplicant/dbus/dbus_dict_helpers.c +++ b/wpa_supplicant/dbus/dbus_dict_helpers.c @@ -66,7 +66,7 @@ dbus_bool_t wpa_dbus_dict_close_write(DBusMessageIter *iter, const char * wpa_dbus_type_as_string(const int type) { - switch(type) { + switch (type) { case DBUS_TYPE_BYTE: return DBUS_TYPE_BYTE_AS_STRING; case DBUS_TYPE_BOOLEAN: @@ -106,11 +106,8 @@ static dbus_bool_t _wpa_dbus_add_dict_entry_start( iter_dict_entry)) return FALSE; - if (!dbus_message_iter_append_basic(iter_dict_entry, DBUS_TYPE_STRING, - &key)) - return FALSE; - - return TRUE; + return dbus_message_iter_append_basic(iter_dict_entry, DBUS_TYPE_STRING, + &key); } @@ -120,10 +117,8 @@ static dbus_bool_t _wpa_dbus_add_dict_entry_end( { if (!dbus_message_iter_close_container(iter_dict_entry, iter_dict_val)) return FALSE; - if (!dbus_message_iter_close_container(iter_dict, iter_dict_entry)) - return FALSE; - return TRUE; + return dbus_message_iter_close_container(iter_dict, iter_dict_entry); } @@ -143,22 +138,15 @@ static dbus_bool_t _wpa_dbus_add_dict_entry_basic(DBusMessageIter *iter_dict, return FALSE; if (!_wpa_dbus_add_dict_entry_start(iter_dict, &iter_dict_entry, - key, value_type)) - return FALSE; - - if (!dbus_message_iter_open_container(&iter_dict_entry, + key, value_type) || + !dbus_message_iter_open_container(&iter_dict_entry, DBUS_TYPE_VARIANT, - type_as_string, &iter_dict_val)) - return FALSE; - - if (!dbus_message_iter_append_basic(&iter_dict_val, value_type, value)) + type_as_string, &iter_dict_val) || + !dbus_message_iter_append_basic(&iter_dict_val, value_type, value)) return FALSE; - if (!_wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry, - &iter_dict_val)) - return FALSE; - - return TRUE; + return _wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry, + &iter_dict_val); } @@ -170,17 +158,13 @@ static dbus_bool_t _wpa_dbus_add_dict_entry_byte_array( dbus_uint32_t i; if (!_wpa_dbus_add_dict_entry_start(iter_dict, &iter_dict_entry, - key, DBUS_TYPE_ARRAY)) - return FALSE; - - if (!dbus_message_iter_open_container(&iter_dict_entry, + key, DBUS_TYPE_ARRAY) || + !dbus_message_iter_open_container(&iter_dict_entry, DBUS_TYPE_VARIANT, DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING, - &iter_dict_val)) - return FALSE; - - if (!dbus_message_iter_open_container(&iter_dict_val, DBUS_TYPE_ARRAY, + &iter_dict_val) || + !dbus_message_iter_open_container(&iter_dict_val, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &iter_array)) return FALSE; @@ -195,11 +179,8 @@ static dbus_bool_t _wpa_dbus_add_dict_entry_byte_array( if (!dbus_message_iter_close_container(&iter_dict_val, &iter_array)) return FALSE; - if (!_wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry, - &iter_dict_val)) - return FALSE; - - return TRUE; + return _wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry, + &iter_dict_val); } @@ -428,9 +409,7 @@ dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict, const char *value, const dbus_uint32_t value_len) { - if (!key) - return FALSE; - if (!value && (value_len != 0)) + if (!key || (!value && value_len != 0)) return FALSE; return _wpa_dbus_add_dict_entry_byte_array(iter_dict, key, value, value_len); @@ -465,27 +444,20 @@ dbus_bool_t wpa_dbus_dict_begin_array(DBusMessageIter *iter_dict, err = os_snprintf(array_type, sizeof(array_type), DBUS_TYPE_ARRAY_AS_STRING "%s", type); - if (err < 0 || err > (int) sizeof(array_type)) + if (os_snprintf_error(sizeof(array_type), err)) return FALSE; - if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array) - return FALSE; - - if (!_wpa_dbus_add_dict_entry_start(iter_dict, iter_dict_entry, - key, DBUS_TYPE_ARRAY)) - return FALSE; - - if (!dbus_message_iter_open_container(iter_dict_entry, + if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array || + !_wpa_dbus_add_dict_entry_start(iter_dict, iter_dict_entry, + key, DBUS_TYPE_ARRAY) || + !dbus_message_iter_open_container(iter_dict_entry, DBUS_TYPE_VARIANT, array_type, iter_dict_val)) return FALSE; - if (!dbus_message_iter_open_container(iter_dict_val, DBUS_TYPE_ARRAY, - type, iter_array)) - return FALSE; - - return TRUE; + return dbus_message_iter_open_container(iter_dict_val, DBUS_TYPE_ARRAY, + type, iter_array); } @@ -542,10 +514,8 @@ dbus_bool_t wpa_dbus_dict_bin_array_add_element(DBusMessageIter *iter_array, DBusMessageIter iter_bytes; size_t i; - if (!iter_array || !value) - return FALSE; - - if (!dbus_message_iter_open_container(iter_array, DBUS_TYPE_ARRAY, + if (!iter_array || !value || + !dbus_message_iter_open_container(iter_array, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &iter_bytes)) return FALSE; @@ -557,10 +527,7 @@ dbus_bool_t wpa_dbus_dict_bin_array_add_element(DBusMessageIter *iter_array, return FALSE; } - if (!dbus_message_iter_close_container(iter_array, &iter_bytes)) - return FALSE; - - return TRUE; + return dbus_message_iter_close_container(iter_array, &iter_bytes); } @@ -586,17 +553,12 @@ dbus_bool_t wpa_dbus_dict_end_array(DBusMessageIter *iter_dict, DBusMessageIter *iter_dict_val, DBusMessageIter *iter_array) { - if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array) - return FALSE; - - if (!dbus_message_iter_close_container(iter_dict_val, iter_array)) + if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array || + !dbus_message_iter_close_container(iter_dict_val, iter_array)) return FALSE; - if (!_wpa_dbus_add_dict_entry_end(iter_dict, iter_dict_entry, - iter_dict_val)) - return FALSE; - - return TRUE; + return _wpa_dbus_add_dict_entry_end(iter_dict, iter_dict_entry, + iter_dict_val); } @@ -619,12 +581,8 @@ dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict, 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_string_array(iter_dict, key, + if (!key || (!items && num_items != 0) || + !wpa_dbus_dict_begin_string_array(iter_dict, key, &iter_dict_entry, &iter_dict_val, &iter_array)) return FALSE; @@ -635,11 +593,8 @@ dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict, return FALSE; } - if (!wpa_dbus_dict_end_string_array(iter_dict, &iter_dict_entry, - &iter_dict_val, &iter_array)) - return FALSE; - - return TRUE; + return wpa_dbus_dict_end_string_array(iter_dict, &iter_dict_entry, + &iter_dict_val, &iter_array); } @@ -662,12 +617,9 @@ dbus_bool_t wpa_dbus_dict_append_wpabuf_array(DBusMessageIter *iter_dict, 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, + if (!key || + (!items && num_items != 0) || + !wpa_dbus_dict_begin_array(iter_dict, key, DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING, &iter_dict_entry, &iter_dict_val, @@ -681,11 +633,8 @@ dbus_bool_t wpa_dbus_dict_append_wpabuf_array(DBusMessageIter *iter_dict, return FALSE; } - if (!wpa_dbus_dict_end_array(iter_dict, &iter_dict_entry, - &iter_dict_val, &iter_array)) - return FALSE; - - return TRUE; + return wpa_dbus_dict_end_array(iter_dict, &iter_dict_entry, + &iter_dict_val, &iter_array); } @@ -707,16 +656,25 @@ dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter, DBusMessageIter *iter_dict, DBusError *error) { + int type; + + wpa_printf(MSG_MSGDUMP, "%s: start reading a dict entry", __func__); if (!iter || !iter_dict) { dbus_set_error_const(error, DBUS_ERROR_FAILED, - "[internal] missing message iterators"); + "[internal] missing message iterators"); return FALSE; } - if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY || + type = dbus_message_iter_get_arg_type(iter); + if (type != DBUS_TYPE_ARRAY || dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY) { + wpa_printf(MSG_DEBUG, + "%s: unexpected message argument types (arg=%c element=%c)", + __func__, type, + type != DBUS_TYPE_ARRAY ? '?' : + dbus_message_iter_get_element_type(iter)); dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS, - "unexpected message argument types"); + "unexpected message argument types"); return FALSE; } @@ -742,7 +700,6 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_byte_array( if (!buffer) return FALSE; - entry->bytearray_value = buffer; entry->array_len = 0; while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_BYTE) { char byte; @@ -753,21 +710,22 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_byte_array( BYTE_ARRAY_ITEM_SIZE); if (nbuffer == NULL) { os_free(buffer); - wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_" - "entry_get_byte_array out of " - "memory trying to retrieve the " - "string array"); + wpa_printf(MSG_ERROR, + "dbus: %s out of memory trying to retrieve the string array", + __func__); goto done; } buffer = nbuffer; } - entry->bytearray_value = buffer; dbus_message_iter_get_basic(iter, &byte); - entry->bytearray_value[count] = byte; + buffer[count] = byte; entry->array_len = ++count; dbus_message_iter_next(iter); } + entry->bytearray_value = buffer; + wpa_hexdump_key(MSG_MSGDUMP, "dbus: byte array contents", + entry->bytearray_value, entry->array_len); /* Zero-length arrays are valid. */ if (entry->array_len == 0) { @@ -790,18 +748,16 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_string_array( struct wpa_dbus_dict_entry *entry) { dbus_uint32_t count = 0; - dbus_bool_t success = FALSE; char **buffer, **nbuffer; entry->strarray_value = NULL; + entry->array_len = 0; entry->array_type = DBUS_TYPE_STRING; buffer = os_calloc(STR_ARRAY_CHUNK_SIZE, STR_ARRAY_ITEM_SIZE); if (buffer == NULL) return FALSE; - entry->strarray_value = buffer; - entry->array_len = 0; while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) { const char *value; char *str; @@ -811,29 +767,31 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_string_array( buffer, count + STR_ARRAY_CHUNK_SIZE, STR_ARRAY_ITEM_SIZE); if (nbuffer == NULL) { - os_free(buffer); - wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_" - "entry_get_string_array out of " - "memory trying to retrieve the " - "string array"); - goto done; + wpa_printf(MSG_ERROR, + "dbus: %s out of memory trying to retrieve the string array", + __func__); + goto fail; } buffer = nbuffer; } - entry->strarray_value = buffer; dbus_message_iter_get_basic(iter, &value); + wpa_printf(MSG_MSGDUMP, "%s: string_array value: %s", + __func__, wpa_debug_show_keys ? value : "[omitted]"); str = os_strdup(value); if (str == NULL) { - wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_entry_get_" - "string_array out of memory trying to " - "duplicate the string array"); - goto done; + wpa_printf(MSG_ERROR, + "dbus: %s out of memory trying to duplicate the string array", + __func__); + goto fail; } - entry->strarray_value[count] = str; - entry->array_len = ++count; + buffer[count++] = str; dbus_message_iter_next(iter); } + entry->strarray_value = buffer; + entry->array_len = count; + wpa_printf(MSG_MSGDUMP, "%s: string_array length %u", + __func__, entry->array_len); /* Zero-length arrays are valid. */ if (entry->array_len == 0) { @@ -841,10 +799,15 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_string_array( entry->strarray_value = NULL; } - success = TRUE; + return TRUE; -done: - return success; +fail: + while (count > 0) { + count--; + os_free(buffer[count]); + } + os_free(buffer); + return FALSE; } @@ -856,15 +819,31 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_binarray( { 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; + int i, type; entry->array_type = WPAS_DBUS_TYPE_BINARRAY; entry->array_len = 0; entry->binarray_value = NULL; + type = dbus_message_iter_get_arg_type(iter); + wpa_printf(MSG_MSGDUMP, "%s: parsing binarray type %c", __func__, type); + if (type == DBUS_TYPE_INVALID) { + /* Likely an empty array of arrays */ + return TRUE; + } + if (type != DBUS_TYPE_ARRAY) { + wpa_printf(MSG_DEBUG, "%s: not an array type: %c", + __func__, type); + return FALSE; + } + + type = dbus_message_iter_get_element_type(iter); + if (type != DBUS_TYPE_BYTE) { + wpa_printf(MSG_DEBUG, "%s: unexpected element type %c", + __func__, type); + return FALSE; + } + while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY) { DBusMessageIter iter_array; @@ -881,8 +860,10 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_binarray( } dbus_message_iter_recurse(iter, &iter_array); + os_memset(&tmpentry, 0, sizeof(tmpentry)); + tmpentry.type = DBUS_TYPE_ARRAY; if (_wpa_dbus_dict_entry_get_byte_array(&iter_array, &tmpentry) - == FALSE) + == FALSE) goto cleanup; entry->binarray_value[entry->array_len] = @@ -895,6 +876,8 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_binarray( entry->array_len++; dbus_message_iter_next(iter); } + wpa_printf(MSG_MSGDUMP, "%s: binarray length %u", + __func__, entry->array_len); return TRUE; @@ -915,12 +898,11 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_array( dbus_bool_t success = FALSE; DBusMessageIter iter_array; - if (!entry) - return FALSE; + wpa_printf(MSG_MSGDUMP, "%s: array_type %c", __func__, array_type); dbus_message_iter_recurse(iter_dict_val, &iter_array); - switch (array_type) { + switch (array_type) { case DBUS_TYPE_BYTE: success = _wpa_dbus_dict_entry_get_byte_array(&iter_array, entry); @@ -932,7 +914,10 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_array( break; case DBUS_TYPE_ARRAY: success = _wpa_dbus_dict_entry_get_binarray(&iter_array, entry); + break; default: + wpa_printf(MSG_MSGDUMP, "%s: unsupported array type %c", + __func__, array_type); break; } @@ -947,42 +932,72 @@ static dbus_bool_t _wpa_dbus_dict_fill_value_from_variant( switch (entry->type) { case DBUS_TYPE_OBJECT_PATH: + dbus_message_iter_get_basic(iter, &v); + wpa_printf(MSG_MSGDUMP, "%s: object path value: %s", + __func__, v); + entry->str_value = os_strdup(v); + if (entry->str_value == NULL) + return FALSE; + break; case DBUS_TYPE_STRING: dbus_message_iter_get_basic(iter, &v); + wpa_printf(MSG_MSGDUMP, "%s: string value: %s", + __func__, wpa_debug_show_keys ? v : "[omitted]"); entry->str_value = os_strdup(v); if (entry->str_value == NULL) return FALSE; break; case DBUS_TYPE_BOOLEAN: dbus_message_iter_get_basic(iter, &entry->bool_value); + wpa_printf(MSG_MSGDUMP, "%s: boolean value: %d", + __func__, entry->bool_value); break; case DBUS_TYPE_BYTE: dbus_message_iter_get_basic(iter, &entry->byte_value); + wpa_printf(MSG_MSGDUMP, "%s: byte value: %d", + __func__, entry->byte_value); break; case DBUS_TYPE_INT16: dbus_message_iter_get_basic(iter, &entry->int16_value); + wpa_printf(MSG_MSGDUMP, "%s: int16 value: %d", + __func__, entry->int16_value); break; case DBUS_TYPE_UINT16: dbus_message_iter_get_basic(iter, &entry->uint16_value); + wpa_printf(MSG_MSGDUMP, "%s: uint16 value: %d", + __func__, entry->uint16_value); break; case DBUS_TYPE_INT32: dbus_message_iter_get_basic(iter, &entry->int32_value); + wpa_printf(MSG_MSGDUMP, "%s: int32 value: %d", + __func__, entry->int32_value); break; case DBUS_TYPE_UINT32: dbus_message_iter_get_basic(iter, &entry->uint32_value); + wpa_printf(MSG_MSGDUMP, "%s: uint32 value: %d", + __func__, entry->uint32_value); break; case DBUS_TYPE_INT64: dbus_message_iter_get_basic(iter, &entry->int64_value); + wpa_printf(MSG_MSGDUMP, "%s: int64 value: %lld", + __func__, (long long int) entry->int64_value); break; case DBUS_TYPE_UINT64: dbus_message_iter_get_basic(iter, &entry->uint64_value); + wpa_printf(MSG_MSGDUMP, "%s: uint64 value: %llu", + __func__, + (unsigned long long int) entry->uint64_value); break; case DBUS_TYPE_DOUBLE: dbus_message_iter_get_basic(iter, &entry->double_value); + wpa_printf(MSG_MSGDUMP, "%s: double value: %f", + __func__, entry->double_value); break; case DBUS_TYPE_ARRAY: return _wpa_dbus_dict_entry_get_array(iter, entry); default: + wpa_printf(MSG_MSGDUMP, "%s: unsupported type %c", + __func__, entry->type); return FALSE; } @@ -1013,26 +1028,40 @@ dbus_bool_t wpa_dbus_dict_get_entry(DBusMessageIter *iter_dict, int type; const char *key; - if (!iter_dict || !entry) - goto error; - - if (dbus_message_iter_get_arg_type(iter_dict) != DBUS_TYPE_DICT_ENTRY) + if (!iter_dict || !entry || + dbus_message_iter_get_arg_type(iter_dict) != DBUS_TYPE_DICT_ENTRY) { + wpa_printf(MSG_DEBUG, "%s: not a dict entry", __func__); goto error; + } dbus_message_iter_recurse(iter_dict, &iter_dict_entry); dbus_message_iter_get_basic(&iter_dict_entry, &key); + wpa_printf(MSG_MSGDUMP, "%s: dict entry key: %s", __func__, key); entry->key = key; - if (!dbus_message_iter_next(&iter_dict_entry)) + if (!dbus_message_iter_next(&iter_dict_entry)) { + wpa_printf(MSG_DEBUG, "%s: no variant in dict entry", __func__); goto error; + } type = dbus_message_iter_get_arg_type(&iter_dict_entry); - if (type != DBUS_TYPE_VARIANT) + if (type != DBUS_TYPE_VARIANT) { + wpa_printf(MSG_DEBUG, + "%s: unexpected dict entry variant type: %c", + __func__, type); goto error; + } dbus_message_iter_recurse(&iter_dict_entry, &iter_dict_val); entry->type = dbus_message_iter_get_arg_type(&iter_dict_val); - if (!_wpa_dbus_dict_fill_value_from_variant(entry, &iter_dict_val)) + wpa_printf(MSG_MSGDUMP, "%s: dict entry variant content type: %c", + __func__, entry->type); + entry->array_type = DBUS_TYPE_INVALID; + if (!_wpa_dbus_dict_fill_value_from_variant(entry, &iter_dict_val)) { + wpa_printf(MSG_DEBUG, + "%s: failed to fetch dict values from variant", + __func__); goto error; + } dbus_message_iter_next(iter_dict); return TRUE; @@ -1087,6 +1116,8 @@ void wpa_dbus_dict_entry_clear(struct wpa_dbus_dict_entry *entry) os_free(entry->bytearray_value); break; case DBUS_TYPE_STRING: + if (!entry->strarray_value) + break; for (i = 0; i < entry->array_len; i++) os_free(entry->strarray_value[i]); os_free(entry->strarray_value); diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h index 96663494a30fb..b068431a74cc8 100644 --- a/wpa_supplicant/dbus/dbus_dict_helpers.h +++ b/wpa_supplicant/dbus/dbus_dict_helpers.h @@ -72,28 +72,28 @@ dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict, /* 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); + 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, - DBusMessageIter *iter_dict_val, - DBusMessageIter *iter_array); + const char *key, + DBusMessageIter *iter_dict_entry, + DBusMessageIter *iter_dict_val, + DBusMessageIter *iter_array); dbus_bool_t wpa_dbus_dict_string_array_add_element(DBusMessageIter *iter_array, - const char *elem); + const char *elem); 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); + 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, @@ -120,7 +120,11 @@ dbus_bool_t wpa_dbus_dict_append_wpabuf_array(DBusMessageIter *iter_dict, * Reading a dict from a DBusMessage */ -#define WPAS_DBUS_TYPE_BINARRAY (DBUS_NUMBER_OF_TYPES + 100) +/* + * Used only in struct wpa_dbus_dict_entry::array_type internally to identify + * special binary array case. + */ +#define WPAS_DBUS_TYPE_BINARRAY ((int) '@') struct wpa_dbus_dict_entry { int type; /** the dbus type of the dict entry's value */ diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c index 8bc6618ab6115..30ef03a7453b7 100644 --- a/wpa_supplicant/dbus/dbus_new.c +++ b/wpa_supplicant/dbus/dbus_new.c @@ -24,6 +24,7 @@ #include "dbus_common_i.h" #include "dbus_new_handlers_p2p.h" #include "p2p/p2p.h" +#include "../p2p_supplicant.h" #ifdef CONFIG_AP /* until needed by something else */ @@ -74,8 +75,7 @@ static DBusHandlerResult noc_filter(DBusConnection *conn, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - for (wpa_s = priv->global->ifaces; wpa_s; wpa_s = wpa_s->next) - { + for (wpa_s = priv->global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (wpa_s->preq_notify_peer != NULL && os_strcmp(name, wpa_s->preq_notify_peer) == 0 && (new_owner == NULL || os_strlen(new_owner) == 0)) { @@ -147,22 +147,14 @@ static void wpas_dbus_signal_interface(struct wpa_supplicant *wpa_s, dbus_message_iter_init_append(msg, &iter); if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, - &wpa_s->dbus_new_path)) - goto err; - - if (properties) { - if (!wpa_dbus_get_object_properties( - iface, wpa_s->dbus_new_path, - WPAS_DBUS_NEW_IFACE_INTERFACE, &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"); + &wpa_s->dbus_new_path) || + (properties && + !wpa_dbus_get_object_properties( + iface, wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, &iter))) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); dbus_message_unref(msg); } @@ -228,7 +220,7 @@ void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, int success) /** - * wpas_dbus_signal_blob - Send a BSS related event signal + * wpas_dbus_signal_bss - Send a BSS related event signal * @wpa_s: %wpa_supplicant network interface data * @bss_obj_path: BSS object path * @sig_name: signal name - BSSAdded or BSSRemoved @@ -258,22 +250,14 @@ static void wpas_dbus_signal_bss(struct wpa_supplicant *wpa_s, dbus_message_iter_init_append(msg, &iter); if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, - &bss_obj_path)) - goto err; - - if (properties) { - if (!wpa_dbus_get_object_properties(iface, bss_obj_path, - WPAS_DBUS_NEW_IFACE_BSS, - &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"); + &bss_obj_path) || + (properties && + !wpa_dbus_get_object_properties(iface, bss_obj_path, + WPAS_DBUS_NEW_IFACE_BSS, + &iter))) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); dbus_message_unref(msg); } @@ -406,23 +390,14 @@ static void wpas_dbus_signal_network(struct wpa_supplicant *wpa_s, dbus_message_iter_init_append(msg, &iter); path = net_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, net_obj_path, WPAS_DBUS_NEW_IFACE_NETWORK, - &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"); + &path) || + (properties && + !wpa_dbus_get_object_properties( + iface, net_obj_path, WPAS_DBUS_NEW_IFACE_NETWORK, + &iter))) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); dbus_message_unref(msg); } @@ -512,19 +487,12 @@ void wpas_dbus_signal_network_request(struct wpa_supplicant *wpa_s, 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"); + &net_ptr) || + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &field) || + !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &txt)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); dbus_message_unref(msg); } @@ -542,6 +510,7 @@ void wpas_dbus_signal_network_enabled_changed(struct wpa_supplicant *wpa_s, { char path[WPAS_DBUS_OBJECT_PATH_MAX]; + os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%d", wpa_s->dbus_new_path, ssid->id); @@ -709,9 +678,9 @@ void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s, DBusMessage *msg; DBusMessageIter iter, dict_iter; struct wpas_dbus_priv *iface; - char *auth_type[6]; /* we have six possible authorization types */ + char *auth_type[5]; /* we have five possible authentication types */ int at_num = 0; - char *encr_type[4]; /* we have four possible encryption types */ + char *encr_type[3]; /* we have three possible encryption types */ int et_num = 0; iface = wpa_s->global->dbus; @@ -734,34 +703,25 @@ void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s, auth_type[at_num++] = "open"; if (cred->auth_type & WPS_AUTH_WPAPSK) auth_type[at_num++] = "wpa-psk"; - if (cred->auth_type & WPS_AUTH_SHARED) - auth_type[at_num++] = "shared"; if (cred->auth_type & WPS_AUTH_WPA) auth_type[at_num++] = "wpa-eap"; if (cred->auth_type & WPS_AUTH_WPA2) auth_type[at_num++] = "wpa2-eap"; if (cred->auth_type & WPS_AUTH_WPA2PSK) - auth_type[at_num++] = - "wpa2-psk"; + auth_type[at_num++] = "wpa2-psk"; if (cred->encr_type & WPS_ENCR_NONE) encr_type[et_num++] = "none"; - if (cred->encr_type & WPS_ENCR_WEP) - encr_type[et_num++] = "wep"; if (cred->encr_type & WPS_ENCR_TKIP) encr_type[et_num++] = "tkip"; if (cred->encr_type & WPS_ENCR_AES) encr_type[et_num++] = "aes"; - if (wpa_s->current_ssid) { - if (!wpa_dbus_dict_append_byte_array( - &dict_iter, "BSSID", - (const char *) wpa_s->current_ssid->bssid, - ETH_ALEN)) - goto nomem; - } - - if (!wpa_dbus_dict_append_byte_array(&dict_iter, "SSID", + if ((wpa_s->current_ssid && + !wpa_dbus_dict_append_byte_array( + &dict_iter, "BSSID", + (const char *) wpa_s->current_ssid->bssid, ETH_ALEN)) || + !wpa_dbus_dict_append_byte_array(&dict_iter, "SSID", (const char *) cred->ssid, cred->ssid_len) || !wpa_dbus_dict_append_string_array(&dict_iter, "AuthType", @@ -788,6 +748,8 @@ nomem: void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s, int depth, const char *subject, + const char *altsubject[], + int num_altsubject, const char *cert_hash, const struct wpabuf *cert) { @@ -808,29 +770,23 @@ void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s, 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: + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_uint32(&dict_iter, "depth", depth) || + !wpa_dbus_dict_append_string(&dict_iter, "subject", subject) || + (altsubject && num_altsubject && + !wpa_dbus_dict_append_string_array(&dict_iter, "altsubject", + altsubject, num_altsubject)) || + (cert_hash && + !wpa_dbus_dict_append_string(&dict_iter, "cert_hash", + cert_hash)) || + (cert && + !wpa_dbus_dict_append_byte_array(&dict_iter, "cert", + wpabuf_head(cert), + wpabuf_len(cert))) || + !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); } @@ -856,16 +812,83 @@ void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s, dbus_message_iter_init_append(msg, &iter); - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &status) - || + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &status) || !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, ¶meter)) - goto nomem; + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); + dbus_message_unref(msg); +} - dbus_connection_send(iface->con, msg, NULL); -nomem: +/** + * wpas_dbus_signal_sta - Send a station related event signal + * @wpa_s: %wpa_supplicant network interface data + * @sta: station mac address + * @sig_name: signal name - StaAuthorized or StaDeauthorized + * + * Notify listeners about event related with station + */ +static void wpas_dbus_signal_sta(struct wpa_supplicant *wpa_s, + const u8 *sta, const char *sig_name) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + char sta_mac[WPAS_DBUS_OBJECT_PATH_MAX]; + char *dev_mac; + + os_snprintf(sta_mac, WPAS_DBUS_OBJECT_PATH_MAX, MACSTR, MAC2STR(sta)); + dev_mac = sta_mac; + + 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, sig_name); + if (msg == NULL) + return; + + if (dbus_message_append_args(msg, DBUS_TYPE_STRING, &dev_mac, + DBUS_TYPE_INVALID)) + dbus_connection_send(iface->con, msg, NULL); + else + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); dbus_message_unref(msg); + + wpa_printf(MSG_DEBUG, "dbus: Station MAC address '%s' '%s'", + sta_mac, sig_name); +} + + +/** + * wpas_dbus_signal_sta_authorized - Send a STA authorized signal + * @wpa_s: %wpa_supplicant network interface data + * @sta: station mac address + * + * Notify listeners a new station has been authorized + */ +void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s, + const u8 *sta) +{ + wpas_dbus_signal_sta(wpa_s, sta, "StaAuthorized"); +} + + +/** + * wpas_dbus_signal_sta_deauthorized - Send a STA deauthorized signal + * @wpa_s: %wpa_supplicant network interface data + * @sta: station mac address + * + * Notify listeners a station has been deauthorized + */ +void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s, + const u8 *sta) +{ + wpas_dbus_signal_sta(wpa_s, sta, "StaDeauthorized"); } @@ -880,37 +903,40 @@ nomem: void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s, const char *role) { - DBusMessage *msg; - DBusMessageIter iter; + DBusMessageIter iter, dict_iter; struct wpas_dbus_priv *iface = wpa_s->global->dbus; - char *ifname = wpa_s->ifname; + struct wpa_supplicant *parent; /* Do nothing if the control interface is not turned on */ if (iface == NULL) return; - msg = dbus_message_new_signal(wpa_s->dbus_new_path, + parent = wpa_s->parent; + if (parent->p2p_mgmt) + parent = parent->parent; + + if (!wpa_s->dbus_groupobj_path) + return; + + msg = dbus_message_new_signal(parent->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; - } - - 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 "); + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_object_path(&dict_iter, + "interface_object", + wpa_s->dbus_new_path) || + !wpa_dbus_dict_append_string(&dict_iter, "role", role) || + !wpa_dbus_dict_append_object_path(&dict_iter, "group_object", + wpa_s->dbus_groupobj_path) || + !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); - -err: dbus_message_unref(msg); } @@ -956,6 +982,9 @@ void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s, if (iface == NULL) return; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + if (request || !status) { if (config_methods & WPS_CONFIG_DISPLAY) _signal = request ? @@ -970,9 +999,10 @@ void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s, "ProvisionDiscoveryPBCResponse"; else return; /* Unknown or un-supported method */ - } else if (!request && status) + } else { /* Explicit check for failure response */ _signal = "ProvisionDiscoveryFailure"; + } add_pin = ((request && (config_methods & WPS_CONFIG_DISPLAY)) || (!request && !status && @@ -1041,6 +1071,9 @@ void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, if (iface == NULL) return; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + 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)); @@ -1086,6 +1119,69 @@ static int wpas_dbus_get_group_obj_path(struct wpa_supplicant *wpa_s, } +struct group_changed_data { + struct wpa_supplicant *wpa_s; + struct p2p_peer_info *info; +}; + + +static int match_group_where_peer_is_client(struct p2p_group *group, + void *user_data) +{ + struct group_changed_data *data = user_data; + const struct p2p_group_config *cfg; + struct wpa_supplicant *wpa_s_go; + + if (!p2p_group_is_client_connected(group, data->info->p2p_device_addr)) + return 1; + + cfg = p2p_group_get_config(group); + + wpa_s_go = wpas_get_p2p_go_iface(data->wpa_s, cfg->ssid, + cfg->ssid_len); + if (wpa_s_go != NULL && wpa_s_go == data->wpa_s) { + wpas_dbus_signal_peer_groups_changed( + data->wpa_s->parent, data->info->p2p_device_addr); + return 0; + } + + return 1; +} + + +static void signal_peer_groups_changed(struct p2p_peer_info *info, + void *user_data) +{ + struct group_changed_data *data = user_data; + struct wpa_supplicant *wpa_s_go; + + wpa_s_go = wpas_get_p2p_client_iface(data->wpa_s, + info->p2p_device_addr); + if (wpa_s_go != NULL && wpa_s_go == data->wpa_s) { + wpas_dbus_signal_peer_groups_changed(data->wpa_s->parent, + info->p2p_device_addr); + return; + } + + data->info = info; + p2p_loop_on_all_groups(data->wpa_s->global->p2p, + match_group_where_peer_is_client, data); + data->info = NULL; +} + + +static void peer_groups_changed(struct wpa_supplicant *wpa_s) +{ + struct group_changed_data data; + + os_memset(&data, 0, sizeof(data)); + data.wpa_s = wpa_s; + + p2p_loop_on_known_peers(wpa_s->global->p2p, + signal_peer_groups_changed, &data); +} + + /** * wpas_dbus_signal_p2p_group_started - Signals P2P group has * started. Emitted when a group is successfully started @@ -1104,58 +1200,56 @@ void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, DBusMessage *msg; DBusMessageIter iter, dict_iter; struct wpas_dbus_priv *iface; - char group_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + struct wpa_supplicant *parent; - iface = wpa_s->parent->global->dbus; + parent = wpa_s->parent; + if (parent->p2p_mgmt) + parent = parent->parent; + + iface = parent->global->dbus; /* 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) + if (wpa_s->dbus_groupobj_path == NULL) return; /* New interface has been created for this group */ - msg = dbus_message_new_signal(wpa_s->parent->dbus_new_path, + msg = dbus_message_new_signal(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; - - if (!wpa_dbus_dict_append_object_path(&dict_iter, "group_object", - group_obj_path) || - !wpa_dbus_dict_close_write(&iter, &dict_iter)) - goto nomem; - - dbus_connection_send(iface->con, msg, NULL); - -nomem: + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_object_path(&dict_iter, + "interface_object", + wpa_s->dbus_new_path) || + !wpa_dbus_dict_append_string(&dict_iter, "role", + client ? "client" : "GO") || + !wpa_dbus_dict_append_object_path(&dict_iter, "group_object", + wpa_s->dbus_groupobj_path) || + !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); + if (client) + peer_groups_changed(wpa_s); + } dbus_message_unref(msg); } /** * - * Method to emit GONeogtiation Success or Failure signals based + * Method to emit GONegotiation Success or Failure signals based * on status. * @status: Status of the GO neg request. 0 for success, other for errors. */ @@ -1173,6 +1267,9 @@ void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s, iface = wpa_s->global->dbus; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + os_memset(freqs, 0, sizeof(freqs)); /* Do nothing if the control interface is not turned on */ if (iface == NULL) @@ -1191,9 +1288,8 @@ void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s, 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", + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_object_path(&dict_iter, "peer_object", path) || !wpa_dbus_dict_append_int32(&dict_iter, "status", res->status)) goto err; @@ -1202,15 +1298,10 @@ void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s, 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", + if ((res->role_go && + !wpa_dbus_dict_append_string(&dict_iter, "passphrase", + res->passphrase)) || + !wpa_dbus_dict_append_string(&dict_iter, "role_go", res->role_go ? "GO" : "client") || !wpa_dbus_dict_append_int32(&dict_iter, "frequency", @@ -1245,22 +1336,16 @@ void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s, 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, + &iter_dict_array) || + !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, + freq_list_num) || + !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", + &iter_dict_array) || + !wpa_dbus_dict_append_int32(&dict_iter, "persistent_group", res->persistent_group) || !wpa_dbus_dict_append_uint32(&dict_iter, "peer_config_timeout", @@ -1292,13 +1377,16 @@ void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s, DBusMessageIter iter, dict_iter; struct wpas_dbus_priv *iface; - wpa_printf(MSG_INFO, "%s\n", __func__); + wpa_printf(MSG_DEBUG, "%s", __func__); iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ if (iface == NULL) return; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + msg = dbus_message_new_signal(wpa_s->dbus_new_path, WPAS_DBUS_NEW_IFACE_P2PDEVICE, "InvitationResult"); @@ -1307,23 +1395,16 @@ void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s, 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: + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_int32(&dict_iter, "status", status) || + (bssid && + !wpa_dbus_dict_append_byte_array(&dict_iter, "BSSID", + (const char *) bssid, + ETH_ALEN)) || + !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); } @@ -1335,15 +1416,16 @@ nomem: * 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 + * @peer_addr: P2P Device Address of the peer joining the group */ void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s, - const u8 *member) + const u8 *peer_addr) { struct wpas_dbus_priv *iface; DBusMessage *msg; DBusMessageIter iter; - char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + struct wpa_supplicant *parent; iface = wpa_s->global->dbus; @@ -1354,10 +1436,14 @@ void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s, if (!wpa_s->dbus_groupobj_path) return; - os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, - "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" + parent = wpa_s->parent; + if (parent->p2p_mgmt) + parent = parent->parent; + + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, - wpa_s->dbus_groupobj_path, MAC2STR(member)); + parent->dbus_new_path, MAC2STR(peer_addr)); msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path, WPAS_DBUS_NEW_IFACE_P2P_GROUP, @@ -1366,18 +1452,14 @@ void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s, return; dbus_message_iter_init_append(msg, &iter); - path = groupmember_obj_path; + path = peer_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"); + &path)) { + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + } else { + dbus_connection_send(iface->con, msg, NULL); + wpas_dbus_signal_peer_groups_changed(parent, peer_addr); + } dbus_message_unref(msg); } @@ -1386,18 +1468,19 @@ err: * * 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. + * constructed using the P2P Device Address of the peer. * * @wpa_s: %wpa_supplicant network interface data - * @member_addr: addr (p2p i/f) of the peer joining the group + * @peer_addr: P2P Device Address of the peer joining the group */ void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s, - const u8 *member) + const u8 *peer_addr) { struct wpas_dbus_priv *iface; DBusMessage *msg; DBusMessageIter iter; - char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + struct wpa_supplicant *parent; iface = wpa_s->global->dbus; @@ -1408,10 +1491,14 @@ void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s, if (!wpa_s->dbus_groupobj_path) return; - os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, - "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" + parent = wpa_s->parent; + if (parent->p2p_mgmt) + parent = parent->parent; + + os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, + "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, - wpa_s->dbus_groupobj_path, MAC2STR(member)); + parent->dbus_new_path, MAC2STR(peer_addr)); msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path, WPAS_DBUS_NEW_IFACE_P2P_GROUP, @@ -1420,19 +1507,15 @@ void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s, return; dbus_message_iter_init_append(msg, &iter); - path = groupmember_obj_path; + path = peer_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"); + &path)) { + wpa_printf(MSG_ERROR, + "dbus: Failed to construct PeerDisconnected signal"); + } else { + dbus_connection_send(iface->con, msg, NULL); + wpas_dbus_signal_peer_groups_changed(parent, peer_addr); + } dbus_message_unref(msg); } @@ -1459,22 +1542,26 @@ void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s, 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; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + + /* Check if this is a known peer */ + if (!p2p_peer_known(wpa_s->global->p2p, sa)) + 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)); @@ -1482,11 +1569,8 @@ void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s, 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", + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !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", @@ -1497,13 +1581,9 @@ void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s, (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"); + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); dbus_message_unref(msg); } @@ -1528,22 +1608,26 @@ void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s, 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; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + + /* Check if this is a known peer */ + if (!p2p_peer_known(wpa_s->global->p2p, sa)) + return; + msg = dbus_message_new_signal(wpa_s->dbus_new_path, WPAS_DBUS_NEW_IFACE_P2PDEVICE, - "ServiceDiscoveryResponse"); + "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)); @@ -1551,10 +1635,8 @@ void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s, 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", + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_object_path(&dict_iter, "peer_object", path) || !wpa_dbus_dict_append_uint16(&dict_iter, "update_indicator", update_indic) || @@ -1562,17 +1644,13 @@ void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s, (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"); + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); dbus_message_unref(msg); } + /** * wpas_dbus_signal_persistent_group - Send a persistent group related * event signal @@ -1598,6 +1676,9 @@ static void wpas_dbus_signal_persistent_group(struct wpa_supplicant *wpa_s, if (iface == NULL) return; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u", wpa_s->dbus_new_path, id); @@ -1611,23 +1692,15 @@ static void wpas_dbus_signal_persistent_group(struct wpa_supplicant *wpa_s, 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; + &path) || + (properties && + !wpa_dbus_get_object_properties( + iface, pgrp_obj_path, + WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, &iter))) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); -err: - wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); dbus_message_unref(msg); } @@ -1686,6 +1759,9 @@ void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s, if (iface == NULL) return; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + msg = dbus_message_new_signal(wpa_s->dbus_new_path, WPAS_DBUS_NEW_IFACE_P2PDEVICE, "WpsFailed"); @@ -1707,7 +1783,7 @@ void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s, dbus_message_unref(msg); } -#endif /*CONFIG_P2P*/ +#endif /* CONFIG_P2P */ /** @@ -1808,9 +1884,15 @@ void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s, case WPAS_DBUS_BSS_PROP_RSN: prop = "RSN"; break; + case WPAS_DBUS_BSS_PROP_WPS: + prop = "WPS"; + break; case WPAS_DBUS_BSS_PROP_IES: prop = "IEs"; break; + case WPAS_DBUS_BSS_PROP_AGE: + prop = "Age"; + break; default: wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d", __func__, property); @@ -1895,7 +1977,7 @@ static void wpas_dbus_register(struct wpa_dbus_object_desc *obj_desc, static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = { { "CreateInterface", WPAS_DBUS_NEW_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_create_interface, + (WPADBusMethodHandler) wpas_dbus_handler_create_interface, { { "args", "a{sv}", ARG_IN }, { "path", "o", ARG_OUT }, @@ -1903,29 +1985,20 @@ static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = { } }, { "RemoveInterface", WPAS_DBUS_NEW_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_remove_interface, + (WPADBusMethodHandler) wpas_dbus_handler_remove_interface, { { "path", "o", ARG_IN }, END_ARGS } }, { "GetInterface", WPAS_DBUS_NEW_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_get_interface, + (WPADBusMethodHandler) wpas_dbus_handler_get_interface, { { "ifname", "s", ARG_IN }, { "path", "o", ARG_OUT }, END_ARGS } }, -#ifdef CONFIG_AUTOSCAN - { "AutoScan", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_autoscan, - { - { "arg", "s", ARG_IN }, - END_ARGS - } - }, -#endif /* CONFIG_AUTOSCAN */ { NULL, NULL, NULL, { END_ARGS } } }; @@ -1954,6 +2027,12 @@ static const struct wpa_dbus_property_desc wpas_dbus_global_properties[] = { wpas_dbus_getter_global_capabilities, NULL }, +#ifdef CONFIG_WIFI_DISPLAY + { "WFDIEs", WPAS_DBUS_NEW_INTERFACE, "ay", + wpas_dbus_getter_global_wfd_ies, + wpas_dbus_setter_global_wfd_ies + }, +#endif /* CONFIG_WIFI_DISPLAY */ { NULL, NULL, NULL, NULL, NULL } }; @@ -1971,14 +2050,6 @@ static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = { 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, { @@ -2005,8 +2076,8 @@ int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv) obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); if (!obj_desc) { - wpa_printf(MSG_ERROR, "Not enough memory " - "to create object description"); + wpa_printf(MSG_ERROR, + "Not enough memory to create object description"); return -1; } @@ -2120,16 +2191,16 @@ int wpas_dbus_register_network(struct wpa_supplicant *wpa_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"); + 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"); + wpa_printf(MSG_ERROR, + "Not enough memory to create arguments for method"); goto err; } @@ -2244,6 +2315,10 @@ static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = { wpas_dbus_getter_bss_ies, NULL }, + { "Age", WPAS_DBUS_NEW_IFACE_BSS, "u", + wpas_dbus_getter_bss_age, + NULL + }, { NULL, NULL, NULL, NULL, NULL } }; @@ -2331,15 +2406,15 @@ int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s, obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); if (!obj_desc) { - wpa_printf(MSG_ERROR, "Not enough memory " - "to create object description"); + 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"); + wpa_printf(MSG_ERROR, + "Not enough memory to create arguments for handler"); goto err; } arg->wpa_s = wpa_s; @@ -2372,20 +2447,27 @@ err: static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { { "Scan", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_scan, + (WPADBusMethodHandler) wpas_dbus_handler_scan, { { "args", "a{sv}", ARG_IN }, END_ARGS } }, + { "SignalPoll", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_signal_poll, + { + { "args", "a{sv}", ARG_OUT }, + END_ARGS + } + }, { "Disconnect", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_disconnect, + (WPADBusMethodHandler) wpas_dbus_handler_disconnect, { END_ARGS } }, { "AddNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_add_network, + (WPADBusMethodHandler) wpas_dbus_handler_add_network, { { "args", "a{sv}", ARG_IN }, { "path", "o", ARG_OUT }, @@ -2393,33 +2475,39 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { } }, { "Reassociate", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_reassociate, + (WPADBusMethodHandler) wpas_dbus_handler_reassociate, + { + END_ARGS + } + }, + { "Reattach", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_reattach, { END_ARGS } }, { "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_remove_network, + (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, + (WPADBusMethodHandler) wpas_dbus_handler_remove_all_networks, { END_ARGS } }, { "SelectNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_select_network, + (WPADBusMethodHandler) wpas_dbus_handler_select_network, { { "path", "o", ARG_IN }, END_ARGS } }, { "NetworkReply", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_network_reply, + (WPADBusMethodHandler) wpas_dbus_handler_network_reply, { { "path", "o", ARG_IN }, { "field", "s", ARG_IN }, @@ -2427,8 +2515,9 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { END_ARGS } }, +#ifndef CONFIG_NO_CONFIG_BLOBS { "AddBlob", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_add_blob, + (WPADBusMethodHandler) wpas_dbus_handler_add_blob, { { "name", "s", ARG_IN }, { "data", "ay", ARG_IN }, @@ -2436,7 +2525,7 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { } }, { "GetBlob", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_get_blob, + (WPADBusMethodHandler) wpas_dbus_handler_get_blob, { { "name", "s", ARG_IN }, { "data", "ay", ARG_OUT }, @@ -2444,15 +2533,25 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { } }, { "RemoveBlob", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_remove_blob, + (WPADBusMethodHandler) wpas_dbus_handler_remove_blob, { { "name", "s", ARG_IN }, END_ARGS } }, +#endif /* CONFIG_NO_CONFIG_BLOBS */ + { "SetPKCS11EngineAndModulePath", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) + wpas_dbus_handler_set_pkcs11_engine_and_module_path, + { + { "pkcs11_engine_path", "s", ARG_IN }, + { "pkcs11_module_path", "s", ARG_IN }, + END_ARGS + } + }, #ifdef CONFIG_WPS { "Start", WPAS_DBUS_NEW_IFACE_WPS, - (WPADBusMethodHandler) &wpas_dbus_handler_wps_start, + (WPADBusMethodHandler) wpas_dbus_handler_wps_start, { { "args", "a{sv}", ARG_IN }, { "output", "a{sv}", ARG_OUT }, @@ -2462,41 +2561,41 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P { "Find", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_find, + (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, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_stop_find, { END_ARGS } }, { "Listen", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_listen, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_listen, { { "timeout", "i", ARG_IN }, END_ARGS } }, { "ExtendedListen", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_extendedlisten, + (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, + (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, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_prov_disc_req, { { "peer", "o", ARG_IN }, { "config_method", "s", ARG_IN }, @@ -2504,7 +2603,7 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { } }, { "Connect", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_connect, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_connect, { { "args", "a{sv}", ARG_IN }, { "generated_pin", "s", ARG_OUT }, @@ -2512,94 +2611,88 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { } }, { "GroupAdd", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_group_add, + (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, + (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, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_disconnect, { END_ARGS } }, { "RejectPeer", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_rejectpeer, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_rejectpeer, { { "peer", "o", ARG_IN }, END_ARGS } }, { "Flush", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_flush, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_flush, { END_ARGS } }, { "AddService", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_add_service, + (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, + (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, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_flush_service, { END_ARGS } }, { "ServiceDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_req, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_service_sd_req, { { "args", "a{sv}", ARG_IN }, + { "ref", "t", ARG_OUT }, END_ARGS } }, { "ServiceDiscoveryResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_res, + (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, + (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, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_service_update, { - { "arg", "i", ARG_IN }, END_ARGS } }, { "ServiceDiscoveryExternal", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - (WPADBusMethodHandler)wpas_dbus_handler_p2p_serv_disc_external, + (WPADBusMethodHandler) wpas_dbus_handler_p2p_serv_disc_external, { { "arg", "i", ARG_IN }, END_ARGS @@ -2629,7 +2722,7 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { }, #endif /* CONFIG_P2P */ { "FlushBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, - (WPADBusMethodHandler) &wpas_dbus_handler_flush_bss, + (WPADBusMethodHandler) wpas_dbus_handler_flush_bss, { { "age", "u", ARG_IN }, END_ARGS @@ -2649,6 +2742,58 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { } }, #endif /* CONFIG_AP */ + { "EAPLogoff", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_eap_logoff, + { + END_ARGS + } + }, + { "EAPLogon", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_eap_logon, + { + END_ARGS + } + }, +#ifdef CONFIG_AUTOSCAN + { "AutoScan", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_autoscan, + { + { "arg", "s", ARG_IN }, + END_ARGS + } + }, +#endif /* CONFIG_AUTOSCAN */ +#ifdef CONFIG_TDLS + { "TDLSDiscover", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_tdls_discover, + { + { "peer_address", "s", ARG_IN }, + END_ARGS + } + }, + { "TDLSSetup", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_tdls_setup, + { + { "peer_address", "s", ARG_IN }, + END_ARGS + } + }, + { "TDLSStatus", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_tdls_status, + { + { "peer_address", "s", ARG_IN }, + { "status", "s", ARG_OUT }, + END_ARGS + } + }, + { "TDLSTeardown", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_tdls_teardown, + { + { "peer_address", "s", ARG_IN }, + END_ARGS + } + }, +#endif /* CONFIG_TDLS */ { NULL, NULL, NULL, { END_ARGS } } }; @@ -2725,11 +2870,23 @@ static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = { wpas_dbus_getter_scan_interval, wpas_dbus_setter_scan_interval }, + { "PKCS11EnginePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", + wpas_dbus_getter_pkcs11_engine_path, + NULL + }, + { "PKCS11ModulePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s", + wpas_dbus_getter_pkcs11_module_path, + NULL + }, #ifdef CONFIG_WPS { "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b", wpas_dbus_getter_process_credentials, wpas_dbus_setter_process_credentials }, + { "ConfigMethods", WPAS_DBUS_NEW_IFACE_WPS, "s", + wpas_dbus_getter_config_methods, + wpas_dbus_setter_config_methods + }, #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P { "P2PDeviceConfig", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "a{sv}", @@ -2845,16 +3002,9 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { }, #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P - { "P2PStateChanged", WPAS_DBUS_NEW_IFACE_P2PDEVICE, - { - { "states", "a{ss}", ARG_OUT }, - END_ARGS - } - }, { "DeviceFound", WPAS_DBUS_NEW_IFACE_P2PDEVICE, { { "path", "o", ARG_OUT }, - { "properties", "a{sv}", ARG_OUT }, END_ARGS } }, @@ -2917,12 +3067,13 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { }, { "GONegotiationSuccess", WPAS_DBUS_NEW_IFACE_P2PDEVICE, { + { "properties", "a{sv}", ARG_OUT }, END_ARGS } }, { "GONegotiationFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE, { - { "status", "i", ARG_OUT }, + { "properties", "a{sv}", ARG_OUT }, END_ARGS } }, @@ -2941,8 +3092,7 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { }, { "GroupFinished", WPAS_DBUS_NEW_IFACE_P2PDEVICE, { - { "ifname", "s", ARG_OUT }, - { "role", "s", ARG_OUT }, + { "properties", "a{sv}", ARG_OUT }, END_ARGS } }, @@ -3000,6 +3150,26 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { END_ARGS } }, + { "StaAuthorized", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "name", "s", ARG_OUT }, + END_ARGS + } + }, + { "StaDeauthorized", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "name", "s", ARG_OUT }, + END_ARGS + } + }, + { "NetworkRequest", WPAS_DBUS_NEW_IFACE_INTERFACE, + { + { "path", "o", ARG_OUT }, + { "field", "s", ARG_OUT }, + { "text", "s", ARG_OUT }, + END_ARGS + } + }, { NULL, NULL, { END_ARGS } } }; @@ -3026,8 +3196,8 @@ int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s) obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc)); if (!obj_desc) { - wpa_printf(MSG_ERROR, "Not enough memory " - "to create object description"); + wpa_printf(MSG_ERROR, + "Not enough memory to create object description"); goto err; } @@ -3062,7 +3232,7 @@ int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s) if (wpa_s == NULL || wpa_s->global == NULL) return 0; ctrl_iface = wpa_s->global->dbus; - if (ctrl_iface == NULL) + if (ctrl_iface == NULL || wpa_s->dbus_new_path == NULL) return 0; wpa_printf(MSG_DEBUG, "dbus: Unregister interface object '%s'", @@ -3127,11 +3297,25 @@ static const struct wpa_dbus_property_desc wpas_dbus_p2p_peer_properties[] = { wpas_dbus_getter_p2p_peer_ies, NULL }, + { "DeviceAddress", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay", + wpas_dbus_getter_p2p_peer_device_address, + NULL + }, + { "Groups", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ao", + wpas_dbus_getter_p2p_peer_groups, + NULL + }, { NULL, NULL, NULL, NULL, NULL } }; static const struct wpa_dbus_signal_desc wpas_dbus_p2p_peer_signals[] = { - + /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */ + { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_P2P_PEER, + { + { "properties", "a{sv}", ARG_OUT }, + END_ARGS + } + }, { NULL, NULL, { END_ARGS } } }; @@ -3155,6 +3339,9 @@ static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s, DBusMessageIter iter; char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ @@ -3174,15 +3361,10 @@ static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s, path = peer_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; + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); -err: - wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); dbus_message_unref(msg); } @@ -3240,6 +3422,9 @@ int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr) if (ctrl_iface == NULL) return 0; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + 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)); @@ -3248,16 +3433,16 @@ int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr) peer_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"); + wpa_printf(MSG_ERROR, + "Not enough memory to create object description"); goto err; } /* 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 method"); + wpa_printf(MSG_ERROR, + "Not enough memory to create arguments for method"); goto err; } @@ -3299,6 +3484,10 @@ int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s, if (wpa_s == NULL || wpa_s->global == NULL || wpa_s->dbus_new_path == NULL) return 0; + + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) return 0; @@ -3315,6 +3504,23 @@ int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s, } +void wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s, + const u8 *dev_addr) +{ + char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX]; + + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + + 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_dbus_mark_property_changed(wpa_s->global->dbus, peer_obj_path, + WPAS_DBUS_NEW_IFACE_P2P_PEER, "Groups"); +} + + 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, @@ -3411,8 +3617,8 @@ void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_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"); + wpa_printf(MSG_ERROR, + "Not enough memory to create object description"); goto err; } @@ -3449,6 +3655,9 @@ void wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s, if (wpa_s == NULL || wpa_s->global == NULL) return; + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) return; @@ -3460,6 +3669,8 @@ void wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s, return; } + peer_groups_changed(wpa_s); + wpa_printf(MSG_DEBUG, "dbus: Unregister group object '%s'", wpa_s->dbus_groupobj_path); @@ -3471,109 +3682,6 @@ void wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s, } 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 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; - - 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) { - wpa_printf(MSG_ERROR, "Not enough memory " - "to create object description"); - goto err; - } - - /* 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; - } - - 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; - - 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, @@ -3610,6 +3718,9 @@ int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s, if (ssid->disabled != 2 && !ssid->p2p_persistent_group) return -1; /* should we return w/o complaining? */ + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) return 0; @@ -3626,8 +3737,8 @@ int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_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"); + wpa_printf(MSG_ERROR, + "dbus: Not enough memory to create object description"); goto err; } @@ -3638,8 +3749,8 @@ int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s, /* 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"); + wpa_printf(MSG_ERROR, + "dbus: Not enough memory to create arguments for method"); goto err; } @@ -3689,6 +3800,10 @@ int wpas_dbus_unregister_persistent_group(struct wpa_supplicant *wpa_s, if (wpa_s == NULL || wpa_s->global == NULL || wpa_s->dbus_new_path == NULL) return 0; + + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + ctrl_iface = wpa_s->global->dbus; if (ctrl_iface == NULL) return 0; diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h index 363a7e5d33c15..d162d2b663df6 100644 --- a/wpa_supplicant/dbus/dbus_new.h +++ b/wpa_supplicant/dbus/dbus_new.h @@ -41,6 +41,7 @@ enum wpas_dbus_bss_prop { WPAS_DBUS_BSS_PROP_RSN, WPAS_DBUS_BSS_PROP_WPS, WPAS_DBUS_BSS_PROP_IES, + WPAS_DBUS_BSS_PROP_AGE, }; #define WPAS_DBUS_OBJECT_PATH_MAX 150 @@ -79,11 +80,7 @@ enum wpas_dbus_bss_prop { #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 */ +/* Top-level Errors */ #define WPAS_DBUS_ERROR_UNKNOWN_ERROR \ WPAS_DBUS_NEW_INTERFACE ".UnknownError" #define WPAS_DBUS_ERROR_INVALID_ARGS \ @@ -91,6 +88,8 @@ enum wpas_dbus_bss_prop { #define WPAS_DBUS_ERROR_IFACE_EXISTS \ WPAS_DBUS_NEW_INTERFACE ".InterfaceExists" +#define WPAS_DBUS_ERROR_IFACE_DISABLED \ + WPAS_DBUS_NEW_INTERFACE ".InterfaceDisabled" #define WPAS_DBUS_ERROR_IFACE_UNKNOWN \ WPAS_DBUS_NEW_INTERFACE ".InterfaceUnknown" @@ -118,6 +117,9 @@ enum wpas_dbus_bss_prop { #define WPAS_DBUS_ERROR_SUBSCRIPTION_EPERM \ WPAS_DBUS_NEW_INTERFACE ".SubscriptionNotYou" +/* Interface-level errors */ +#define WPAS_DBUS_ERROR_IFACE_SCAN_ERROR \ + WPAS_DBUS_NEW_IFACE_INTERFACE ".ScanError" void wpas_dbus_subscribe_noc(struct wpas_dbus_priv *priv); void wpas_dbus_unsubscribe_noc(struct wpas_dbus_priv *priv); @@ -172,6 +174,8 @@ 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_peer_groups_changed(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, @@ -196,10 +200,6 @@ 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, @@ -215,6 +215,8 @@ 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 *altsubject[], + int num_altsubject, const char *cert_hash, const struct wpabuf *cert); void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s, @@ -222,6 +224,10 @@ void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s, const u8 *ie, size_t ie_len, u32 ssi_signal); void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s, const char *status, const char *parameter); +void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s, + const u8 *sta); +void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s, + const u8 *sta); #else /* CONFIG_CTRL_IFACE_DBUS_NEW */ @@ -351,6 +357,12 @@ static inline int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s, } static inline void +wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s, + const u8 *dev_addr) +{ +} + +static inline void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s, const char *role) { @@ -474,6 +486,8 @@ wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s, static inline void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s, int depth, const char *subject, + const char *altsubject[], + int num_altsubject, const char *cert_hash, const struct wpabuf *cert) { @@ -493,6 +507,18 @@ static inline void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s, { } +static inline +void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s, + const u8 *sta) +{ +} + +static inline +void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s, + const u8 *sta) +{ +} + #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 5e069326be9cd..f2e62ca963864 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/wpa_supplicant/dbus/dbus_new_handlers.c @@ -2,7 +2,7 @@ * WPA Supplicant / dbus-based control interface * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc. * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com> - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -27,18 +27,15 @@ #include "dbus_new_handlers.h" #include "dbus_dict_helpers.h" #include "dbus_common_i.h" +#include "drivers/driver.h" -extern int wpa_debug_level; -extern int wpa_debug_show_keys; -extern int wpa_debug_timestamp; - -static const char *debug_strings[] = { +static const char * const debug_strings[] = { "excessive", "msgdump", "debug", "info", "warning", "error", NULL }; /** - * wpas_dbus_error_unknown_error - Return a new InvalidArgs error message + * wpas_dbus_error_unknown_error - Return a new UnknownError error message * @message: Pointer to incoming dbus message this error refers to * @arg: Optional string appended to error message * Returns: a dbus error message @@ -48,20 +45,6 @@ static const char *debug_strings[] = { 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); } @@ -76,9 +59,9 @@ DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message, */ static DBusMessage * wpas_dbus_error_iface_unknown(DBusMessage *message) { - return dbus_message_new_error(message, WPAS_DBUS_ERROR_IFACE_UNKNOWN, - "wpa_supplicant knows nothing about " - "this interface."); + return dbus_message_new_error( + message, WPAS_DBUS_ERROR_IFACE_UNKNOWN, + "wpa_supplicant knows nothing about this interface."); } @@ -91,9 +74,9 @@ static DBusMessage * wpas_dbus_error_iface_unknown(DBusMessage *message) */ static DBusMessage * wpas_dbus_error_network_unknown(DBusMessage *message) { - return dbus_message_new_error(message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN, - "There is no such a network in this " - "interface."); + return dbus_message_new_error( + message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN, + "There is no such a network in this interface."); } @@ -109,9 +92,9 @@ DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message, { DBusMessage *reply; - reply = dbus_message_new_error(message, WPAS_DBUS_ERROR_INVALID_ARGS, - "Did not receive correct message " - "arguments."); + reply = dbus_message_new_error( + message, WPAS_DBUS_ERROR_INVALID_ARGS, + "Did not receive correct message arguments."); if (arg != NULL) dbus_message_append_args(reply, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID); @@ -120,7 +103,31 @@ DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message, } -static const char *dont_quote[] = { +/** + * wpas_dbus_error_scan_error - Return a new ScanError error message + * @message: Pointer to incoming dbus message this error refers to + * @error: Optional string to be used as the error message + * Returns: a dbus error message + * + * Convenience function to create and return a scan error + */ +static DBusMessage * wpas_dbus_error_scan_error(DBusMessage *message, + const char *error) +{ + return dbus_message_new_error(message, + WPAS_DBUS_ERROR_IFACE_SCAN_ERROR, + error); +} + + +DBusMessage * wpas_dbus_error_no_memory(DBusMessage *message) +{ + wpa_printf(MSG_DEBUG, "dbus: Failed to allocate memory"); + return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL); +} + + +static const char * const dont_quote[] = { "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap", "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path", "bssid", "scan_freq", "freq_list", NULL @@ -129,6 +136,7 @@ static const char *dont_quote[] = { static dbus_bool_t should_quote_opt(const char *key) { int i = 0; + while (dont_quote[i] != NULL) { if (os_strcmp(key, dont_quote[i]) == 0) return FALSE; @@ -215,7 +223,7 @@ dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s, ret = os_snprintf(value, size, "\"%s\"", entry.str_value); - if (ret < 0 || (size_t) ret != (size - 1)) + if (os_snprintf_error(size, ret)) goto error; } else { value = os_strdup(entry.str_value); @@ -229,7 +237,7 @@ dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s, ret = os_snprintf(value, size, "%u", entry.uint32_value); - if (ret <= 0) + if (os_snprintf_error(size, ret)) goto error; } else if (entry.type == DBUS_TYPE_INT32) { value = os_zalloc(size); @@ -238,7 +246,7 @@ dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s, ret = os_snprintf(value, size, "%d", entry.int32_value); - if (ret <= 0) + if (os_snprintf_error(size, ret)) goto error; } else goto error; @@ -246,6 +254,19 @@ dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s, if (wpa_config_set(ssid, entry.key, value, 0) < 0) goto error; + if (os_strcmp(entry.key, "bssid") != 0 && + os_strcmp(entry.key, "priority") != 0) + 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 or previously used configuration changes. + */ + eapol_sm_invalidate_cached_session(wpa_s->eapol); + } + if ((os_strcmp(entry.key, "psk") == 0 && value[0] == '"' && ssid->ssid_len) || (os_strcmp(entry.key, "ssid") == 0 && ssid->passphrase)) @@ -254,6 +275,7 @@ dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s, wpa_config_update_prio_list(wpa_s->conf); os_free(value); + value = NULL; wpa_dbus_dict_entry_clear(&entry); } @@ -287,27 +309,21 @@ dbus_bool_t wpas_dbus_simple_property_getter(DBusMessageIter *iter, if (!dbus_type_is_basic(type)) { dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: given type is not basic", __func__); + "%s: given type is not basic", __func__); return FALSE; } if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, - wpa_dbus_type_as_string(type), - &variant_iter)) - goto error; - - if (!dbus_message_iter_append_basic(&variant_iter, type, val)) - goto error; - - if (!dbus_message_iter_close_container(iter, &variant_iter)) - goto error; + 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)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: error constructing reply", __func__); + return FALSE; + } return TRUE; - -error: - dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: error constructing reply", __func__); - return FALSE; } @@ -370,7 +386,7 @@ dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter, if (!dbus_type_is_basic(type)) { dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: given type is not basic", __func__); + "%s: given type is not basic", __func__); return FALSE; } @@ -378,20 +394,15 @@ dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter, 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, + type_str, &variant_iter) || + !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, sub_type_str, &array_iter)) { dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to construct message 2", __func__); + "%s: failed to construct message", __func__); return FALSE; } - switch(type) { + switch (type) { case DBUS_TYPE_BYTE: case DBUS_TYPE_BOOLEAN: element_size = 1; @@ -417,24 +428,24 @@ dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter, break; default: dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: unknown element type %d", __func__, type); + "%s: unknown element type %d", __func__, type); return FALSE; } for (i = 0; i < array_len; i++) { - dbus_message_iter_append_basic(&array_iter, type, - array + i * element_size); - } - - 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; + if (!dbus_message_iter_append_basic(&array_iter, type, + array + i * element_size)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: failed to construct message 2.5", + __func__); + return FALSE; + } } - if (!dbus_message_iter_close_container(iter, &variant_iter)) { + if (!dbus_message_iter_close_container(&variant_iter, &array_iter) || + !dbus_message_iter_close_container(iter, &variant_iter)) { dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to construct message 4", __func__); + "%s: failed to construct message 3", __func__); return FALSE; } @@ -477,34 +488,25 @@ dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter, 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, + type_str, &variant_iter) || + !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__); + "%s: failed to construct message", __func__); return FALSE; } - for (i = 0; i < array_len; i++) { + for (i = 0; i < array_len && array[i]; 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)) { + if (!dbus_message_iter_close_container(&variant_iter, &array_iter) || + !dbus_message_iter_close_container(iter, &variant_iter)) { dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to close message 1", __func__); + "%s: failed to close message", __func__); return FALSE; } @@ -542,30 +544,34 @@ DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message, 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, "Driver") && - (entry.type == DBUS_TYPE_STRING)) { + if (os_strcmp(entry.key, "Driver") == 0 && + entry.type == DBUS_TYPE_STRING) { + os_free(driver); driver = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (driver == NULL) - goto error; - } else if (!os_strcmp(entry.key, "Ifname") && - (entry.type == DBUS_TYPE_STRING)) { + goto oom; + } else if (os_strcmp(entry.key, "Ifname") == 0 && + entry.type == DBUS_TYPE_STRING) { + os_free(ifname); ifname = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (ifname == NULL) - goto error; - } else if (!os_strcmp(entry.key, "ConfigFile") && - (entry.type == DBUS_TYPE_STRING)) { + goto oom; + } else if (os_strcmp(entry.key, "ConfigFile") == 0 && + entry.type == DBUS_TYPE_STRING) { + os_free(confname); confname = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (confname == NULL) - goto error; - } else if (!os_strcmp(entry.key, "BridgeIfname") && - (entry.type == DBUS_TYPE_STRING)) { + goto oom; + } else if (os_strcmp(entry.key, "BridgeIfname") == 0 && + entry.type == DBUS_TYPE_STRING) { + os_free(bridge_ifname); bridge_ifname = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (bridge_ifname == NULL) - goto error; + goto oom; } else { wpa_dbus_dict_entry_clear(&entry); goto error; @@ -580,28 +586,30 @@ DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message, * an error if we already control it. */ if (wpa_supplicant_get_iface(global, ifname) != NULL) { - reply = dbus_message_new_error(message, - WPAS_DBUS_ERROR_IFACE_EXISTS, - "wpa_supplicant already " - "controls this interface."); + reply = dbus_message_new_error( + message, WPAS_DBUS_ERROR_IFACE_EXISTS, + "wpa_supplicant already controls this interface."); } else { struct wpa_supplicant *wpa_s; struct wpa_interface iface; + 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))) { + wpa_s = wpa_supplicant_add_iface(global, &iface, NULL); + if (wpa_s) { const char *path = wpa_s->dbus_new_path; + reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, - &path, DBUS_TYPE_INVALID); + &path, DBUS_TYPE_INVALID); } else { reply = wpas_dbus_error_unknown_error( - message, "wpa_supplicant couldn't grab this " - "interface."); + message, + "wpa_supplicant couldn't grab this interface."); } } @@ -615,6 +623,9 @@ out: error: reply = wpas_dbus_error_invalid_args(message, NULL); goto out; +oom: + reply = wpas_dbus_error_no_memory(message); + goto out; } @@ -644,8 +655,8 @@ DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message, reply = wpas_dbus_error_iface_unknown(message); else if (wpa_supplicant_remove_iface(global, wpa_s, 0)) { reply = wpas_dbus_error_unknown_error( - message, "wpa_supplicant couldn't remove this " - "interface."); + message, + "wpa_supplicant couldn't remove this interface."); } return reply; @@ -679,13 +690,11 @@ DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message, path = wpa_s->dbus_new_path; reply = dbus_message_new_method_return(message); if (reply == NULL) - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + return wpas_dbus_error_no_memory(message); if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { dbus_message_unref(reply); - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + return wpas_dbus_error_no_memory(message); } return reply; @@ -728,8 +737,8 @@ dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter, * Getter for "DebugTimestamp" property. */ dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter, - DBusError *error, - void *user_data) + DBusError *error, + void *user_data) { return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN, &wpa_debug_timestamp, error); @@ -784,8 +793,8 @@ dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter, if (val < 0 || wpa_supplicant_set_debug_params(global, val, wpa_debug_timestamp, wpa_debug_show_keys)) { - dbus_set_error_const(error, DBUS_ERROR_FAILED, "wrong debug " - "level value"); + dbus_set_error_const(error, DBUS_ERROR_FAILED, + "wrong debug level value"); return FALSE; } @@ -935,8 +944,8 @@ dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter, * and P2P that are determined at compile time. */ dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter, - DBusError *error, - void *user_data) + DBusError *error, + void *user_data) { const char *capabilities[5] = { NULL, NULL, NULL, NULL, NULL }; size_t num_items = 0; @@ -965,8 +974,8 @@ static int wpas_dbus_get_scan_type(DBusMessage *message, DBusMessageIter *var, char **type, DBusMessage **reply) { if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_STRING) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "Type must be a string"); + wpa_printf(MSG_DEBUG, "%s[dbus]: Type must be a string", + __func__); *reply = wpas_dbus_error_invalid_args( message, "Wrong Type value type. String required"); return -1; @@ -988,36 +997,36 @@ static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var, int len; if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ssids " - "must be an array of arrays of bytes"); + wpa_printf(MSG_DEBUG, + "%s[dbus]: ssids must be an array of arrays of bytes", + __func__); *reply = wpas_dbus_error_invalid_args( - message, "Wrong SSIDs value type. Array of arrays of " - "bytes required"); + message, + "Wrong SSIDs value type. Array of arrays of bytes required"); return -1; } dbus_message_iter_recurse(var, &array_iter); if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY || - dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE) - { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ssids " - "must be an array of arrays of bytes"); + dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE) { + wpa_printf(MSG_DEBUG, + "%s[dbus]: ssids must be an array of arrays of bytes", + __func__); *reply = wpas_dbus_error_invalid_args( - message, "Wrong SSIDs value type. Array of arrays of " - "bytes required"); + message, + "Wrong SSIDs value type. Array of arrays of bytes required"); return -1; } - while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY) - { + while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY) { if (ssids_num >= WPAS_MAX_SCAN_SSIDS) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "Too many ssids specified on scan dbus " - "call"); + wpa_printf(MSG_DEBUG, + "%s[dbus]: Too many ssids specified on scan dbus call", + __func__); *reply = wpas_dbus_error_invalid_args( - message, "Too many ssids specified. Specify " - "at most four"); + message, + "Too many ssids specified. Specify at most four"); return -1; } @@ -1027,9 +1036,8 @@ static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var, 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); + "%s[dbus]: SSID too long (len=%d max_len=%d)", + __func__, len, MAX_SSID_LEN); *reply = wpas_dbus_error_invalid_args( message, "Invalid SSID: too long"); return -1; @@ -1038,12 +1046,7 @@ static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var, 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); + *reply = wpas_dbus_error_no_memory(message); return -1; } os_memcpy(ssid, val, len); @@ -1075,28 +1078,28 @@ static int wpas_dbus_get_scan_ies(DBusMessage *message, DBusMessageIter *var, int len; if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ies must " - "be an array of arrays of bytes"); + wpa_printf(MSG_DEBUG, + "%s[dbus]: ies must be an array of arrays of bytes", + __func__); *reply = wpas_dbus_error_invalid_args( - message, "Wrong IEs value type. Array of arrays of " - "bytes required"); + message, + "Wrong IEs value type. Array of arrays of bytes required"); return -1; } dbus_message_iter_recurse(var, &array_iter); if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY || - dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE) - { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ies must " - "be an array of arrays of bytes"); + dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE) { + wpa_printf(MSG_DEBUG, + "%s[dbus]: ies must be an array of arrays of bytes", + __func__); *reply = wpas_dbus_error_invalid_args( message, "Wrong IEs value type. Array required"); return -1; } - while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY) - { + while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY) { dbus_message_iter_recurse(&array_iter, &sub_array_iter); dbus_message_iter_get_fixed_array(&sub_array_iter, &val, &len); @@ -1107,12 +1110,8 @@ static int wpas_dbus_get_scan_ies(DBusMessage *message, DBusMessageIter *var, nies = os_realloc(ies, ies_len + len); if (nies == NULL) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "out of memory. Cannot allocate memory for " - "IE"); os_free(ies); - *reply = dbus_message_new_error( - message, DBUS_ERROR_NO_MEMORY, NULL); + *reply = wpas_dbus_error_no_memory(message); return -1; } ies = nies; @@ -1138,11 +1137,12 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message, int freqs_num = 0; if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "Channels must be an array of structs"); + wpa_printf(MSG_DEBUG, + "%s[dbus]: Channels must be an array of structs", + __func__); *reply = wpas_dbus_error_invalid_args( - message, "Wrong Channels value type. Array of structs " - "required"); + message, + "Wrong Channels value type. Array of structs required"); return -1; } @@ -1150,11 +1150,11 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message, if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_STRUCT) { wpa_printf(MSG_DEBUG, - "wpas_dbus_handler_scan[dbus]: Channels must be an " - "array of structs"); + "%s[dbus]: Channels must be an array of structs", + __func__); *reply = wpas_dbus_error_invalid_args( - message, "Wrong Channels value type. Array of structs " - "required"); + message, + "Wrong Channels value type. Array of structs required"); return -1; } @@ -1166,14 +1166,14 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message, if (dbus_message_iter_get_arg_type(&sub_array_iter) != DBUS_TYPE_UINT32) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "Channel must by specified by struct of " - "two UINT32s %c", + wpa_printf(MSG_DEBUG, + "%s[dbus]: Channel must by specified by struct of two UINT32s %c", + __func__, dbus_message_iter_get_arg_type( &sub_array_iter)); *reply = wpas_dbus_error_invalid_args( - message, "Wrong Channel struct. Two UINT32s " - "required"); + message, + "Wrong Channel struct. Two UINT32s required"); os_free(freqs); return -1; } @@ -1182,9 +1182,9 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message, if (!dbus_message_iter_next(&sub_array_iter) || dbus_message_iter_get_arg_type(&sub_array_iter) != DBUS_TYPE_UINT32) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "Channel must by specified by struct of " - "two UINT32s"); + wpa_printf(MSG_DEBUG, + "%s[dbus]: Channel must by specified by struct of two UINT32s", + __func__); *reply = wpas_dbus_error_invalid_args( message, "Wrong Channel struct. Two UINT32s required"); @@ -1204,11 +1204,7 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message, freqs = nfreqs; } if (freqs == NULL) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "out of memory. can't allocate memory for " - "freqs"); - *reply = dbus_message_new_error( - message, DBUS_ERROR_NO_MEMORY, NULL); + *reply = wpas_dbus_error_no_memory(message); return -1; } @@ -1223,10 +1219,7 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message, os_free(freqs); freqs = nfreqs; if (freqs == NULL) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "out of memory. Can't allocate memory for freqs"); - *reply = dbus_message_new_error( - message, DBUS_ERROR_NO_MEMORY, NULL); + *reply = wpas_dbus_error_no_memory(message); return -1; } freqs[freqs_num] = 0; @@ -1236,6 +1229,23 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message, } +static int wpas_dbus_get_scan_allow_roam(DBusMessage *message, + DBusMessageIter *var, + dbus_bool_t *allow, + DBusMessage **reply) +{ + if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_BOOLEAN) { + wpa_printf(MSG_DEBUG, "%s[dbus]: Type must be a boolean", + __func__); + *reply = wpas_dbus_error_invalid_args( + message, "Wrong Type value type. Boolean required"); + return -1; + } + dbus_message_iter_get_basic(var, allow); + return 0; +} + + /** * wpas_dbus_handler_scan - Request a wireless scan on an interface * @message: Pointer to incoming dbus message @@ -1254,6 +1264,7 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, char *key = NULL, *type = NULL; struct wpa_driver_scan_params params; size_t i; + dbus_bool_t allow_roam = 1; os_memset(¶ms, 0, sizeof(params)); @@ -1262,7 +1273,7 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, dbus_message_iter_recurse(&iter, &dict_iter); while (dbus_message_iter_get_arg_type(&dict_iter) == - DBUS_TYPE_DICT_ENTRY) { + DBUS_TYPE_DICT_ENTRY) { dbus_message_iter_recurse(&dict_iter, &entry_iter); dbus_message_iter_get_basic(&entry_iter, &key); dbus_message_iter_next(&entry_iter); @@ -1284,9 +1295,15 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, if (wpas_dbus_get_scan_channels(message, &variant_iter, ¶ms, &reply) < 0) goto out; + } else if (os_strcmp(key, "AllowRoam") == 0) { + if (wpas_dbus_get_scan_allow_roam(message, + &variant_iter, + &allow_roam, + &reply) < 0) + goto out; } else { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "Unknown argument %s", key); + wpa_printf(MSG_DEBUG, "%s[dbus]: Unknown argument %s", + __func__, key); reply = wpas_dbus_error_invalid_args(message, key); goto out; } @@ -1295,27 +1312,31 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, } if (!type) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "Scan type not specified"); + wpa_printf(MSG_DEBUG, "%s[dbus]: Scan type not specified", + __func__); reply = wpas_dbus_error_invalid_args(message, key); goto out; } - if (!os_strcmp(type, "passive")) { + if (os_strcmp(type, "passive") == 0) { if (params.num_ssids || params.extra_ies_len) { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "SSIDs or IEs specified for passive scan."); + wpa_printf(MSG_DEBUG, + "%s[dbus]: SSIDs or IEs specified for passive scan.", + __func__); reply = wpas_dbus_error_invalid_args( - message, "You can specify only Channels in " - "passive scan"); + message, + "You can specify only Channels in passive scan"); goto out; } else if (params.freqs && params.freqs[0]) { - wpa_supplicant_trigger_scan(wpa_s, ¶ms); + if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) { + reply = wpas_dbus_error_scan_error( + message, "Scan request rejected"); + } } else { wpa_s->scan_req = MANUAL_SCAN_REQ; wpa_supplicant_req_scan(wpa_s, 0, 0); } - } else if (!os_strcmp(type, "active")) { + } else if (os_strcmp(type, "active") == 0) { if (!params.num_ssids) { /* Add wildcard ssid */ params.num_ssids++; @@ -1323,15 +1344,21 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, #ifdef CONFIG_AUTOSCAN autoscan_deinit(wpa_s); #endif /* CONFIG_AUTOSCAN */ - wpa_supplicant_trigger_scan(wpa_s, ¶ms); + if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) { + reply = wpas_dbus_error_scan_error( + message, "Scan request rejected"); + } } else { - wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: " - "Unknown scan type: %s", type); + wpa_printf(MSG_DEBUG, "%s[dbus]: Unknown scan type: %s", + __func__, type); reply = wpas_dbus_error_invalid_args(message, "Wrong scan type"); goto out; } + if (!allow_roam) + wpa_s->scan_res_handler = scan_only_handler; + out: for (i = 0; i < WPAS_MAX_SCAN_SSIDS; i++) os_free((u8 *) params.ssids[i].ssid); @@ -1341,6 +1368,72 @@ out: } +/** + * wpas_dbus_handler_signal_poll - Request immediate signal properties + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL indicating success or DBus error message on failure + * + * Handler function for "SignalPoll" method call of a network device. Requests + * that wpa_supplicant read signal properties like RSSI, noise, and link + * speed and return them. + */ +DBusMessage * wpas_dbus_handler_signal_poll(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + struct wpa_signal_info si; + DBusMessage *reply = NULL; + DBusMessageIter iter, iter_dict, variant_iter; + int ret; + + ret = wpa_drv_signal_poll(wpa_s, &si); + if (ret) { + return dbus_message_new_error(message, DBUS_ERROR_FAILED, + "Failed to read signal"); + } + + reply = dbus_message_new_method_return(message); + if (reply == NULL) + goto nomem; + + dbus_message_iter_init_append(reply, &iter); + + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, + "a{sv}", &variant_iter) || + !wpa_dbus_dict_open_write(&variant_iter, &iter_dict) || + !wpa_dbus_dict_append_int32(&iter_dict, "rssi", + si.current_signal) || + !wpa_dbus_dict_append_int32(&iter_dict, "linkspeed", + si.current_txrate / 1000) || + !wpa_dbus_dict_append_int32(&iter_dict, "noise", + si.current_noise) || + !wpa_dbus_dict_append_uint32(&iter_dict, "frequency", + si.frequency) || + (si.chanwidth != CHAN_WIDTH_UNKNOWN && + !wpa_dbus_dict_append_string( + &iter_dict, "width", + channel_width_to_string(si.chanwidth))) || + (si.center_frq1 > 0 && si.center_frq2 > 0 && + (!wpa_dbus_dict_append_int32(&iter_dict, "center-frq1", + si.center_frq1) || + !wpa_dbus_dict_append_int32(&iter_dict, "center-frq2", + si.center_frq2))) || + (si.avg_signal && + !wpa_dbus_dict_append_int32(&iter_dict, "avg-rssi", + si.avg_signal)) || + !wpa_dbus_dict_close_write(&variant_iter, &iter_dict) || + !dbus_message_iter_close_container(&iter, &variant_iter)) + goto nomem; + + return reply; + +nomem: + if (reply) + dbus_message_unref(reply); + return wpas_dbus_error_no_memory(message); +} + + /* * wpas_dbus_handler_disconnect - Terminate the current connection * @message: Pointer to incoming dbus message @@ -1387,12 +1480,11 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) { - wpa_printf(MSG_ERROR, "wpas_dbus_handler_add_network[dbus]: " - "can't add new interface."); + wpa_printf(MSG_ERROR, "%s[dbus]: can't add new interface.", + __func__); reply = wpas_dbus_error_unknown_error( message, - "wpa_supplicant could not add " - "a network on this interface."); + "wpa_supplicant could not add a network on this interface."); goto err; } wpas_notify_network_added(wpa_s, ssid); @@ -1401,9 +1493,9 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, 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"); + wpa_printf(MSG_DEBUG, + "%s[dbus]: control interface couldn't set network properties", + __func__); reply = wpas_dbus_reply_new_from_error(message, &error, DBUS_ERROR_INVALID_ARGS, "Failed to add network"); @@ -1418,15 +1510,13 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, reply = dbus_message_new_method_return(message); if (reply == NULL) { - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + reply = wpas_dbus_error_no_memory(message); 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); + reply = wpas_dbus_error_no_memory(message); goto err; } @@ -1442,10 +1532,10 @@ err: /** - * wpas_dbus_handler_reassociate - Reassociate to current AP + * wpas_dbus_handler_reassociate - Reassociate * @message: Pointer to incoming dbus message * @wpa_s: wpa_supplicant structure for a network interface - * Returns: NotConnected DBus error message if not connected + * Returns: InterfaceDisabled DBus error message if disabled * or NULL otherwise. * * Handler function for "Reassociate" method call of network interface. @@ -1453,7 +1543,30 @@ err: DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message, struct wpa_supplicant *wpa_s) { + if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED) { + wpas_request_connection(wpa_s); + return NULL; + } + + return dbus_message_new_error(message, WPAS_DBUS_ERROR_IFACE_DISABLED, + "This interface is disabled"); +} + + +/** + * wpas_dbus_handler_reattach - Reattach to current AP + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NotConnected DBus error message if not connected + * or NULL otherwise. + * + * Handler function for "Reattach" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_reattach(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ if (wpa_s->current_ssid != NULL) { + wpa_s->reattach = 1; wpas_request_connection(wpa_s); return NULL; } @@ -1476,16 +1589,19 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, { DBusMessage *reply = NULL; const char *op; - char *iface = NULL, *net_id = NULL; + char *iface, *net_id; int id; struct wpa_ssid *ssid; + int was_disabled; 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, 0, &net_id, NULL); + iface = wpas_dbus_new_decompose_object_path(op, + WPAS_DBUS_NEW_NETWORKS_PART, + &net_id); if (iface == NULL || net_id == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); @@ -1505,25 +1621,32 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, goto out; } + was_disabled = ssid->disabled; + wpas_notify_network_removed(wpa_s, ssid); + if (ssid == wpa_s->current_ssid) + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + else if (!was_disabled && wpa_s->sched_scanning) { + wpa_printf(MSG_DEBUG, + "Stop ongoing sched_scan to remove network from filters"); + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } + if (wpa_config_remove_network(wpa_s->conf, id) < 0) { wpa_printf(MSG_ERROR, - "wpas_dbus_handler_remove_network[dbus]: " - "error occurred when removing network %d", id); + "%s[dbus]: error occurred when removing network %d", + __func__, id); reply = wpas_dbus_error_unknown_error( - message, "error removing the specified network on " - "this interface."); + message, + "error removing the specified network on is interface."); goto out; } - if (ssid == wpa_s->current_ssid) - wpa_supplicant_deauthenticate(wpa_s, - WLAN_REASON_DEAUTH_LEAVING); - out: os_free(iface); - os_free(net_id); return reply; } @@ -1536,9 +1659,8 @@ static void remove_network(void *arg, struct wpa_ssid *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); + "%s[dbus]: error occurred when removing network %d", + __func__, ssid->id); return; } @@ -1559,6 +1681,9 @@ static void remove_network(void *arg, struct wpa_ssid *ssid) DBusMessage * wpas_dbus_handler_remove_all_networks( DBusMessage *message, struct wpa_supplicant *wpa_s) { + if (wpa_s->sched_scanning) + wpa_supplicant_cancel_sched_scan(wpa_s); + /* NB: could check for failure and return an error */ wpa_config_foreach_network(wpa_s->conf, remove_network, wpa_s); return NULL; @@ -1578,7 +1703,7 @@ DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message, { DBusMessage *reply = NULL; const char *op; - char *iface = NULL, *net_id = NULL; + char *iface, *net_id; int id; struct wpa_ssid *ssid; @@ -1587,7 +1712,9 @@ 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, 0, &net_id, NULL); + iface = wpas_dbus_new_decompose_object_path(op, + WPAS_DBUS_NEW_NETWORKS_PART, + &net_id); if (iface == NULL || net_id == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); @@ -1612,7 +1739,6 @@ DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message, out: os_free(iface); - os_free(net_id); return reply; } @@ -1631,20 +1757,22 @@ DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message, #ifdef IEEE8021X_EAPOL DBusMessage *reply = NULL; const char *op, *field, *value; - char *iface = NULL, *net_id = NULL; + char *iface, *net_id; 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)) + 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); + iface = wpas_dbus_new_decompose_object_path(op, + WPAS_DBUS_NEW_NETWORKS_PART, + &net_id); if (iface == NULL || net_id == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); @@ -1674,7 +1802,6 @@ DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message, out: os_free(iface); - os_free(net_id); return reply; #else /* IEEE8021X_EAPOL */ wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included"); @@ -1683,6 +1810,8 @@ out: } +#ifndef CONFIG_NO_CONFIG_BLOBS + /** * wpas_dbus_handler_add_blob - Store named binary blob (ie, for certificates) * @message: Pointer to incoming dbus message @@ -1718,26 +1847,18 @@ DBusMessage * wpas_dbus_handler_add_blob(DBusMessage *message, blob = os_zalloc(sizeof(*blob)); if (!blob) { - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + reply = wpas_dbus_error_no_memory(message); goto err; } blob->data = os_malloc(blob_len); - if (!blob->data) { - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + blob->name = os_strdup(blob_name); + if (!blob->data || !blob->name) { + reply = wpas_dbus_error_no_memory(message); goto err; } os_memcpy(blob->data, blob_data, blob_len); - blob->len = blob_len; - blob->name = os_strdup(blob_name); - if (!blob->name) { - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); - goto err; - } wpa_config_set_blob(wpa_s->conf, blob); wpas_notify_blob_added(wpa_s, blob->name); @@ -1782,39 +1903,21 @@ DBusMessage * wpas_dbus_handler_get_blob(DBusMessage *message, } reply = dbus_message_new_method_return(message); - if (!reply) { - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); - goto out; - } + if (!reply) + return wpas_dbus_error_no_memory(message); dbus_message_iter_init_append(reply, &iter); if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, - &array_iter)) { + &array_iter) || + !dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, + &(blob->data), blob->len) || + !dbus_message_iter_close_container(&iter, &array_iter)) { dbus_message_unref(reply); - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); - goto out; - } - - if (!dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, - &(blob->data), blob->len)) { - dbus_message_unref(reply); - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); - goto out; - } - - if (!dbus_message_iter_close_container(&iter, &array_iter)) { - dbus_message_unref(reply); - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); - goto out; + reply = wpas_dbus_error_no_memory(message); } -out: return reply; } @@ -1847,6 +1950,9 @@ DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message, } +#endif /* CONFIG_NO_CONFIG_BLOBS */ + + /* * wpas_dbus_handler_flush_bss - Flush the BSS cache * @message: Pointer to incoming dbus message @@ -1893,11 +1999,10 @@ DBusMessage * wpas_dbus_handler_autoscan(DBusMessage *message, if (arg != NULL && os_strlen(arg) > 0) { char *tmp; + tmp = os_strdup(arg); if (tmp == NULL) { - reply = dbus_message_new_error(message, - DBUS_ERROR_NO_MEMORY, - NULL); + reply = wpas_dbus_error_no_memory(message); } else { os_free(wpa_s->conf->autoscan); wpa_s->conf->autoscan = tmp; @@ -1920,6 +2025,258 @@ DBusMessage * wpas_dbus_handler_autoscan(DBusMessage *message, #endif /* CONFIG_AUTOSCAN */ +/* + * wpas_dbus_handler_eap_logoff - IEEE 802.1X EAPOL state machine logoff + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL + * + * Handler function for "EAPLogoff" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_eap_logoff(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + eapol_sm_notify_logoff(wpa_s->eapol, TRUE); + return NULL; +} + + +/* + * wpas_dbus_handler_eap_logon - IEEE 802.1X EAPOL state machine logon + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL + * + * Handler function for "EAPLogin" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_eap_logon(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + eapol_sm_notify_logoff(wpa_s->eapol, FALSE); + return NULL; +} + + +#ifdef CONFIG_TDLS + +static int get_peer_hwaddr_helper(DBusMessage *message, const char *func_name, + u8 *peer_address, DBusMessage **error) +{ + const char *peer_string; + + *error = NULL; + + if (!dbus_message_get_args(message, NULL, + DBUS_TYPE_STRING, &peer_string, + DBUS_TYPE_INVALID)) { + *error = wpas_dbus_error_invalid_args(message, NULL); + return -1; + } + + if (hwaddr_aton(peer_string, peer_address)) { + wpa_printf(MSG_DEBUG, "%s: invalid address '%s'", + func_name, peer_string); + *error = wpas_dbus_error_invalid_args( + message, "Invalid hardware address format"); + return -1; + } + + return 0; +} + + +/* + * wpas_dbus_handler_tdls_discover - Discover TDLS peer + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL indicating success or DBus error message on failure + * + * Handler function for "TDLSDiscover" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + u8 peer[ETH_ALEN]; + DBusMessage *error_reply; + int ret; + + if (get_peer_hwaddr_helper(message, __func__, peer, &error_reply) < 0) + return error_reply; + + wpa_printf(MSG_DEBUG, "DBUS 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); + + if (ret) { + return wpas_dbus_error_unknown_error( + message, "error performing TDLS discovery"); + } + + return NULL; +} + + +/* + * wpas_dbus_handler_tdls_setup - Setup TDLS session + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL indicating success or DBus error message on failure + * + * Handler function for "TDLSSetup" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_tdls_setup(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + u8 peer[ETH_ALEN]; + DBusMessage *error_reply; + int ret; + + if (get_peer_hwaddr_helper(message, __func__, peer, &error_reply) < 0) + return error_reply; + + wpa_printf(MSG_DEBUG, "DBUS TDLS_SETUP " MACSTR, MAC2STR(peer)); + + wpa_tdls_remove(wpa_s->wpa, peer); + 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); + + if (ret) { + return wpas_dbus_error_unknown_error( + message, "error performing TDLS setup"); + } + + return NULL; +} + + +/* + * wpas_dbus_handler_tdls_status - Return TDLS session status + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: A string representing the state of the link to this TDLS peer + * + * Handler function for "TDLSStatus" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_tdls_status(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + u8 peer[ETH_ALEN]; + DBusMessage *reply; + const char *tdls_status; + + if (get_peer_hwaddr_helper(message, __func__, peer, &reply) < 0) + return reply; + + wpa_printf(MSG_DEBUG, "DBUS TDLS_STATUS " MACSTR, MAC2STR(peer)); + + tdls_status = wpa_tdls_get_link_status(wpa_s->wpa, peer); + + reply = dbus_message_new_method_return(message); + dbus_message_append_args(reply, DBUS_TYPE_STRING, + &tdls_status, DBUS_TYPE_INVALID); + return reply; +} + + +/* + * wpas_dbus_handler_tdls_teardown - Teardown TDLS session + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: NULL indicating success or DBus error message on failure + * + * Handler function for "TDLSTeardown" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + u8 peer[ETH_ALEN]; + DBusMessage *error_reply; + int ret; + + if (get_peer_hwaddr_helper(message, __func__, peer, &error_reply) < 0) + return error_reply; + + wpa_printf(MSG_DEBUG, "DBUS TDLS_TEARDOWN " MACSTR, MAC2STR(peer)); + + if (wpa_tdls_is_external_setup(wpa_s->wpa)) + ret = wpa_tdls_teardown_link( + wpa_s->wpa, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); + else + ret = wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer); + + if (ret) { + return wpas_dbus_error_unknown_error( + message, "error performing TDLS teardown"); + } + + return NULL; +} + +#endif /* CONFIG_TDLS */ + + +/** + * wpas_dbus_handler_set_pkcs11_engine_and_module_path - Set PKCS #11 engine and module path + * @message: Pointer to incoming dbus message + * @wpa_s: %wpa_supplicant data structure + * Returns: A dbus message containing an error on failure or NULL on success + * + * Sets the PKCS #11 engine and module path. + */ +DBusMessage * wpas_dbus_handler_set_pkcs11_engine_and_module_path( + DBusMessage *message, struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter; + char *value = NULL; + char *pkcs11_engine_path = NULL; + char *pkcs11_module_path = NULL; + + dbus_message_iter_init(message, &iter); + dbus_message_iter_get_basic(&iter, &value); + if (value == NULL) { + return dbus_message_new_error( + message, DBUS_ERROR_INVALID_ARGS, + "Invalid pkcs11_engine_path argument"); + } + /* Empty path defaults to NULL */ + if (os_strlen(value)) + pkcs11_engine_path = value; + + dbus_message_iter_next(&iter); + dbus_message_iter_get_basic(&iter, &value); + if (value == NULL) { + os_free(pkcs11_engine_path); + return dbus_message_new_error( + message, DBUS_ERROR_INVALID_ARGS, + "Invalid pkcs11_module_path argument"); + } + /* Empty path defaults to NULL */ + if (os_strlen(value)) + pkcs11_module_path = value; + + if (wpas_set_pkcs11_engine_and_module_path(wpa_s, pkcs11_engine_path, + pkcs11_module_path)) + return dbus_message_new_error( + message, DBUS_ERROR_FAILED, + "Reinit of the EAPOL state machine with the new PKCS #11 engine and module path failed."); + + wpa_dbus_mark_property_changed( + wpa_s->global->dbus, wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11EnginePath"); + wpa_dbus_mark_property_changed( + wpa_s->global->dbus, wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11ModulePath"); + + return NULL; +} + + /** * wpas_dbus_getter_capabilities - Return interface capabilities * @iter: Pointer to incoming dbus message iter @@ -1940,10 +2297,8 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *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)) + "a{sv}", &variant_iter) || + !wpa_dbus_dict_open_write(&variant_iter, &iter_dict)) goto nomem; res = wpa_drv_get_capa(wpa_s, &capa); @@ -1951,42 +2306,35 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, /***** pairwise cipher */ if (res < 0) { const char *args[] = {"ccmp", "tkip", "none"}; + if (!wpa_dbus_dict_append_string_array( &iter_dict, "Pairwise", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto nomem; } else { if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Pairwise", &iter_dict_entry, &iter_dict_val, - &iter_array)) - goto nomem; - - if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "ccmp")) - goto nomem; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "gcmp")) - goto nomem; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "tkip")) - goto nomem; - } - - if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "none")) - goto nomem; - } - - if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_array) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "ccmp-256")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "gcmp-256")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "ccmp")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "gcmp")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "tkip")) || + ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "none")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -1998,48 +2346,38 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, const char *args[] = { "ccmp", "tkip", "wep104", "wep40" }; + if (!wpa_dbus_dict_append_string_array( &iter_dict, "Group", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto nomem; } else { if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Group", &iter_dict_entry, &iter_dict_val, - &iter_array)) - goto nomem; - - if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "ccmp")) - goto nomem; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "gcmp")) - goto nomem; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "tkip")) - goto nomem; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "wep104")) - goto nomem; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "wep40")) - goto nomem; - } - - if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_array) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "ccmp-256")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "gcmp-256")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "ccmp")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "gcmp")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "tkip")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "wep104")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "wep40")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -2057,34 +2395,28 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, }; if (!wpa_dbus_dict_append_string_array( &iter_dict, "KeyMgmt", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto nomem; } else { if (!wpa_dbus_dict_begin_string_array(&iter_dict, "KeyMgmt", &iter_dict_entry, &iter_dict_val, - &iter_array)) - goto nomem; - - if (!wpa_dbus_dict_string_array_add_element(&iter_array, - "none")) - goto nomem; - - if (!wpa_dbus_dict_string_array_add_element(&iter_array, + &iter_array) || + !wpa_dbus_dict_string_array_add_element(&iter_array, + "none") || + !wpa_dbus_dict_string_array_add_element(&iter_array, "ieee8021x")) goto nomem; if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) { if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "wpa-eap")) + &iter_array, "wpa-eap") || + ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "wpa-ft-eap"))) goto nomem; - if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "wpa-ft-eap")) - goto nomem; - /* TODO: Ensure that driver actually supports sha256 encryption. */ #ifdef CONFIG_IEEE80211W if (!wpa_dbus_dict_string_array_add_element( @@ -2096,14 +2428,13 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "wpa-psk")) + &iter_array, "wpa-psk") || + ((capa.key_mgmt & + WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "wpa-ft-psk"))) goto nomem; - if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "wpa-ft-psk")) - goto nomem; - /* TODO: Ensure that driver actually supports sha256 encryption. */ #ifdef CONFIG_IEEE80211W if (!wpa_dbus_dict_string_array_add_element( @@ -2112,11 +2443,10 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, #endif /* CONFIG_IEEE80211W */ } - if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "wpa-none")) - goto nomem; - } + if ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) && + !wpa_dbus_dict_string_array_add_element(&iter_array, + "wpa-none")) + goto nomem; #ifdef CONFIG_WPS @@ -2135,32 +2465,25 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, /***** WPA protocol */ if (res < 0) { const char *args[] = { "rsn", "wpa" }; + if (!wpa_dbus_dict_append_string_array( &iter_dict, "Protocol", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto nomem; } else { if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Protocol", &iter_dict_entry, &iter_dict_val, - &iter_array)) - goto nomem; - - if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "rsn")) - goto nomem; - } - - if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | - WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "wpa")) - goto nomem; - } - - if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_array) || + ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "rsn")) || + ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "wpa")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -2170,9 +2493,10 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, /***** auth alg */ if (res < 0) { const char *args[] = { "open", "shared", "leap" }; + if (!wpa_dbus_dict_append_string_array( &iter_dict, "AuthAlg", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto nomem; } else { if (!wpa_dbus_dict_begin_string_array(&iter_dict, "AuthAlg", @@ -2181,25 +2505,16 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, &iter_array)) goto nomem; - if (capa.auth & (WPA_DRIVER_AUTH_OPEN)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "open")) - goto nomem; - } - - if (capa.auth & (WPA_DRIVER_AUTH_SHARED)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "shared")) - goto nomem; - } - - if (capa.auth & (WPA_DRIVER_AUTH_LEAP)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "leap")) - goto nomem; - } - - if (!wpa_dbus_dict_end_string_array(&iter_dict, + if (((capa.auth & WPA_DRIVER_AUTH_OPEN) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "open")) || + ((capa.auth & WPA_DRIVER_AUTH_SHARED) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "shared")) || + ((capa.auth & WPA_DRIVER_AUTH_LEAP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "leap")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -2208,39 +2523,25 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, /***** Scan */ if (!wpa_dbus_dict_append_string_array(&iter_dict, "Scan", scans, - sizeof(scans) / sizeof(char *))) + ARRAY_SIZE(scans))) goto nomem; /***** Modes */ 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_array) || + !wpa_dbus_dict_string_array_add_element( + &iter_array, "infrastructure") || + !wpa_dbus_dict_string_array_add_element( + &iter_array, "ad-hoc") || + (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_AP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "ap")) || + (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "p2p")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -2255,9 +2556,8 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, goto nomem; } - if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict)) - goto nomem; - if (!dbus_message_iter_close_container(iter, &variant_iter)) + if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict) || + !dbus_message_iter_close_container(iter, &variant_iter)) goto nomem; return TRUE; @@ -2318,7 +2618,7 @@ dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error, * Getter for "scanning" property. */ dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error, - void *user_data) + void *user_data) { struct wpa_supplicant *wpa_s = user_data; dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE; @@ -2440,6 +2740,7 @@ dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter, { struct wpa_supplicant *wpa_s = user_data; dbus_int32_t reason = wpa_s->disconnect_reason; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32, &reason, error); } @@ -2694,8 +2995,8 @@ dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error, 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"); + wpa_printf(MSG_DEBUG, "%s[dbus]: wpa_s has no driver set", + __func__); dbus_set_error(error, DBUS_ERROR_FAILED, "%s: no driver set", __func__); return FALSE; @@ -2815,6 +3116,7 @@ dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter, { struct wpa_supplicant *wpa_s = user_data; const char *bridge_ifname = wpa_s->bridge_ifname; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &bridge_ifname, error); } @@ -2889,14 +3191,6 @@ dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error, unsigned int i = 0, num = 0; dbus_bool_t success = FALSE; - if (wpa_s->conf == 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) if (!network_is_persistent_group(ssid)) num++; @@ -2913,7 +3207,8 @@ dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error, continue; paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX); if (paths[i] == NULL) { - dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory"); + dbus_set_error(error, DBUS_ERROR_NO_MEMORY, + "no memory"); goto out; } @@ -2936,6 +3231,56 @@ out: /** + * wpas_dbus_getter_pkcs11_engine_path - Get PKCS #11 engine path + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: A dbus message containing the PKCS #11 engine path + * + * Getter for "PKCS11EnginePath" property. + */ +dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + const char *pkcs11_engine_path; + + if (wpa_s->conf->pkcs11_engine_path == NULL) + pkcs11_engine_path = ""; + else + pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &pkcs11_engine_path, error); +} + + +/** + * wpas_dbus_getter_pkcs11_module_path - Get PKCS #11 module path + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: A dbus message containing the PKCS #11 module path + * + * Getter for "PKCS11ModulePath" property. + */ +dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + const char *pkcs11_module_path; + + if (wpa_s->conf->pkcs11_module_path == NULL) + pkcs11_module_path = ""; + else + pkcs11_module_path = wpa_s->conf->pkcs11_module_path; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &pkcs11_module_path, error); +} + + +/** * wpas_dbus_getter_blobs - Get all blobs defined for this interface * @iter: Pointer to incoming dbus message iter * @error: Location to store error on failure @@ -3004,7 +3349,7 @@ static struct wpa_bss * get_bss_helper(struct bss_handler_args *args, if (!res) { wpa_printf(MSG_ERROR, "%s[dbus]: no bss with id %d found", - func_name, args->id); + func_name, args->id); dbus_set_error(error, DBUS_ERROR_FAILED, "%s: BSS %d not found", func_name, args->id); @@ -3109,11 +3454,22 @@ dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error, res = get_bss_helper(args, error, __func__); if (!res) return FALSE; - - if (res->caps & IEEE80211_CAP_IBSS) - mode = "ad-hoc"; - else - mode = "infrastructure"; + if (bss_is_dmg(res)) { + switch (res->caps & IEEE80211_CAP_DMG_MASK) { + case IEEE80211_CAP_DMG_PBSS: + case IEEE80211_CAP_DMG_IBSS: + mode = "ad-hoc"; + break; + case IEEE80211_CAP_DMG_AP: + mode = "infrastructure"; + break; + } + } else { + if (res->caps & IEEE80211_CAP_IBSS) + mode = "ad-hoc"; + else + mode = "infrastructure"; + } return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &mode, error); @@ -3233,8 +3589,8 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter, { DBusMessageIter iter_dict, variant_iter; const char *group; - const char *pairwise[3]; /* max 3 pairwise ciphers is supported */ - const char *key_mgmt[7]; /* max 7 key managements may be supported */ + const char *pairwise[5]; /* max 5 pairwise ciphers is supported */ + const char *key_mgmt[9]; /* max 9 key managements may be supported */ int n; if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, @@ -3258,6 +3614,14 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter, key_mgmt[n++] = "wpa-ft-eap"; if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) key_mgmt[n++] = "wpa-eap-sha256"; +#ifdef CONFIG_SUITEB + if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) + key_mgmt[n++] = "wpa-eap-suite-b"; +#endif /* CONFIG_SUITEB */ +#ifdef CONFIG_SUITEB192 + if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + key_mgmt[n++] = "wpa-eap-suite-b-192"; +#endif /* CONFIG_SUITEB192 */ if (ie_data->key_mgmt & WPA_KEY_MGMT_NONE) key_mgmt[n++] = "wpa-none"; @@ -3282,6 +3646,12 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter, case WPA_CIPHER_WEP104: group = "wep104"; break; + case WPA_CIPHER_CCMP_256: + group = "ccmp-256"; + break; + case WPA_CIPHER_GCMP_256: + group = "gcmp-256"; + break; default: group = ""; break; @@ -3298,6 +3668,10 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter, pairwise[n++] = "ccmp"; if (ie_data->pairwise_cipher & WPA_CIPHER_GCMP) pairwise[n++] = "gcmp"; + if (ie_data->pairwise_cipher & WPA_CIPHER_CCMP_256) + pairwise[n++] = "ccmp-256"; + if (ie_data->pairwise_cipher & WPA_CIPHER_GCMP_256) + pairwise[n++] = "gcmp-256"; if (!wpa_dbus_dict_append_string_array(&iter_dict, "Pairwise", pairwise, n)) @@ -3321,9 +3695,8 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter, goto nomem; } - if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict)) - goto nomem; - if (!dbus_message_iter_close_container(iter, &variant_iter)) + if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict) || + !dbus_message_iter_close_container(iter, &variant_iter)) goto nomem; return TRUE; @@ -3357,12 +3730,10 @@ dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error, 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) { - dbus_set_error_const(error, DBUS_ERROR_FAILED, - "failed to parse WPA IE"); - return FALSE; - } + if (ie && 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(iter, &wpa_data, error); @@ -3392,12 +3763,10 @@ dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error, 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) { - dbus_set_error_const(error, DBUS_ERROR_FAILED, - "failed to parse RSN IE"); - return FALSE; - } + if (ie && 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(iter, &wpa_data, error); @@ -3429,10 +3798,8 @@ dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, return FALSE; 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)) + "a{sv}", &variant_iter) || + !wpa_dbus_dict_open_write(&variant_iter, &iter_dict)) goto nomem; #ifdef CONFIG_WPS @@ -3442,15 +3809,14 @@ dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, type = "pbc"; else if (wps_is_selected_pin_registrar(wps_ie)) type = "pin"; + + wpabuf_free(wps_ie); } #endif /* CONFIG_WPS */ - if (!wpa_dbus_dict_append_string(&iter_dict, "Type", type)) - goto nomem; - - if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict)) - goto nomem; - if (!dbus_message_iter_close_container(iter, &variant_iter)) + if (!wpa_dbus_dict_append_string(&iter_dict, "Type", type) || + !wpa_dbus_dict_close_write(&variant_iter, &iter_dict) || + !dbus_message_iter_close_container(iter, &variant_iter)) goto nomem; return TRUE; @@ -3487,6 +3853,35 @@ dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error, /** + * wpas_dbus_getter_bss_age - Return time in seconds since BSS was last seen + * @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 BSS age + */ +dbus_bool_t wpas_dbus_getter_bss_age(DBusMessageIter *iter, DBusError *error, + void *user_data) +{ + struct bss_handler_args *args = user_data; + struct wpa_bss *res; + struct os_reltime now, diff = { 0, 0 }; + u32 age; + + res = get_bss_helper(args, error, __func__); + if (!res) + return FALSE; + + os_get_reltime(&now); + os_reltime_sub(&now, &res->last_update, &diff); + age = diff.sec > 0 ? diff.sec : 0; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32, &age, + error); +} + + +/** * wpas_dbus_getter_enabled - Check whether network is enabled or disabled * @iter: Pointer to incoming dbus message iter * @error: Location to store error on failure @@ -3643,8 +4038,7 @@ DBusMessage * wpas_dbus_handler_subscribe_preq( name = os_strdup(dbus_message_get_sender(message)); if (!name) - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - "out of memory"); + return wpas_dbus_error_no_memory(message); wpa_s->preq_notify_peer = name; @@ -3724,28 +4118,22 @@ void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s, dbus_message_iter_init_append(msg, &iter); - if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) - goto fail; - if (addr && !wpa_dbus_dict_append_byte_array(&dict_iter, "addr", - (const char *) addr, - ETH_ALEN)) - goto fail; - if (dst && !wpa_dbus_dict_append_byte_array(&dict_iter, "dst", - (const char *) dst, - ETH_ALEN)) - goto fail; - if (bssid && !wpa_dbus_dict_append_byte_array(&dict_iter, "bssid", - (const char *) bssid, - ETH_ALEN)) - goto fail; - if (ie && ie_len && !wpa_dbus_dict_append_byte_array(&dict_iter, "ies", - (const char *) ie, - ie_len)) - goto fail; - if (ssi_signal && !wpa_dbus_dict_append_int32(&dict_iter, "signal", - ssi_signal)) - goto fail; - if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + (addr && !wpa_dbus_dict_append_byte_array(&dict_iter, "addr", + (const char *) addr, + ETH_ALEN)) || + (dst && !wpa_dbus_dict_append_byte_array(&dict_iter, "dst", + (const char *) dst, + ETH_ALEN)) || + (bssid && !wpa_dbus_dict_append_byte_array(&dict_iter, "bssid", + (const char *) bssid, + ETH_ALEN)) || + (ie && ie_len && !wpa_dbus_dict_append_byte_array(&dict_iter, "ies", + (const char *) ie, + ie_len)) || + (ssi_signal && !wpa_dbus_dict_append_int32(&dict_iter, "signal", + ssi_signal)) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) goto fail; dbus_connection_send(priv->con, msg, NULL); diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h index aa565508f8e95..6113db500390e 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.h +++ b/wpa_supplicant/dbus/dbus_new_handlers.h @@ -55,8 +55,8 @@ dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter, void *user_data); dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter, - DBusError *error, - void *user_data); + DBusError *error, + void *user_data); dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter, DBusError *error, @@ -87,6 +87,9 @@ dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter, DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_signal_poll(DBusMessage *message, + struct wpa_supplicant *wpa_s); + DBusMessage * wpas_dbus_handler_disconnect(DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -101,6 +104,9 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message, DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_reattach(DBusMessage *message, + struct wpa_supplicant *wpa_s); + DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -122,12 +128,21 @@ DBusMessage * wpas_dbus_handler_get_blob(DBusMessage *message, DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_set_pkcs11_engine_and_module_path( + DBusMessage *message, struct wpa_supplicant *wpa_s); + DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message, struct wpa_supplicant *wpa_s); DBusMessage * wpas_dbus_handler_autoscan(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_eap_logoff(DBusMessage *message, + struct wpa_supplicant *wpa_s); + +DBusMessage * wpas_dbus_handler_eap_logon(DBusMessage *message, + struct wpa_supplicant *wpa_s); + dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter, DBusError *error, void *user_data); @@ -212,6 +227,14 @@ dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error, dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error, void *user_data); +dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter, + DBusError *error, + void *user_data); + dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error, void *user_data); @@ -248,6 +271,9 @@ dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error, dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error, void *user_data); +dbus_bool_t wpas_dbus_getter_bss_age(DBusMessageIter *iter, DBusError *error, + void *user_data); + dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error, void *user_data); @@ -272,10 +298,28 @@ dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter, DBusError *error, void *user_data); +dbus_bool_t wpas_dbus_getter_config_methods(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_setter_config_methods(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message, + struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_tdls_setup(DBusMessage *message, + struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_tdls_status(DBusMessage *message, + struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message, + struct wpa_supplicant *wpa_s); + DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message, const char *arg); DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message, const char *arg); +DBusMessage * wpas_dbus_error_no_memory(DBusMessage *message); DBusMessage * wpas_dbus_handler_subscribe_preq( DBusMessage *message, struct wpa_supplicant *wpa_s); diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c index 30e0eb3e5fb8c..0eff76386fa4c 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c +++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c @@ -26,6 +26,7 @@ #include "ap/wps_hostapd.h" #include "../p2p_supplicant.h" +#include "../wifi_display.h" /** * Parses out the mac address from the peer object path. @@ -34,9 +35,9 @@ * @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]) +static int parse_peer_object_path(const char *peer_path, u8 addr[ETH_ALEN]) { - char *p; + const char *p; if (!peer_path) return -1; @@ -56,12 +57,12 @@ static int parse_peer_object_path(char *peer_path, u8 addr[ETH_ALEN]) * * Convenience function to create and return an invalid persistent group error. */ -static DBusMessage * wpas_dbus_error_persistent_group_unknown( - DBusMessage *message) +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."); + return dbus_message_new_error( + message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN, + "There is no such persistent group in this P2P device."); } @@ -73,7 +74,7 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message, DBusMessageIter iter; DBusMessageIter iter_dict; unsigned int timeout = 0; - enum p2p_discovery_type type = P2P_FIND_ONLY_SOCIAL; + enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL; int num_req_dev_types = 0; unsigned int i; u8 *req_dev_types = NULL; @@ -88,12 +89,12 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message, if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; - if (!os_strcmp(entry.key, "Timeout") && - (entry.type == DBUS_TYPE_INT32)) { + if (os_strcmp(entry.key, "Timeout") == 0 && + 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)) + if (entry.type != DBUS_TYPE_ARRAY || + entry.array_type != WPAS_DBUS_TYPE_BINARRAY) goto error_clear; os_free(req_dev_types); @@ -104,20 +105,20 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message, for (i = 0; i < entry.array_len; i++) { if (wpabuf_len(entry.binarray_value[i]) != - WPS_DEV_TYPE_LEN) + 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")) + } else if (os_strcmp(entry.key, "DiscoveryType") == 0 && + entry.type == DBUS_TYPE_STRING) { + if (os_strcmp(entry.str_value, "start_with_full") == 0) type = P2P_FIND_START_WITH_FULL; - else if (!os_strcmp(entry.str_value, "social")) + else if (os_strcmp(entry.str_value, "social") == 0) type = P2P_FIND_ONLY_SOCIAL; - else if (!os_strcmp(entry.str_value, "progressive")) + else if (os_strcmp(entry.str_value, "progressive") == 0) type = P2P_FIND_PROGRESSIVE; else goto error_clear; @@ -126,8 +127,11 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message, wpa_dbus_dict_entry_clear(&entry); } + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types, - NULL, 0); + NULL, 0, 0, NULL, 0); os_free(req_dev_types); return reply; @@ -143,6 +147,9 @@ error: DBusMessage * wpas_dbus_handler_p2p_stop_find(DBusMessage *message, struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + wpas_p2p_stop_find(wpa_s); return NULL; } @@ -161,6 +168,9 @@ DBusMessage * wpas_dbus_handler_p2p_rejectpeer(DBusMessage *message, if (parse_peer_object_path(peer_object_path, peer_addr) < 0) return wpas_dbus_error_invalid_args(message, NULL); + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + if (wpas_p2p_reject(wpa_s, peer_addr) < 0) return wpas_dbus_error_unknown_error(message, "Failed to call wpas_p2p_reject method."); @@ -176,12 +186,16 @@ DBusMessage * wpas_dbus_handler_p2p_listen(DBusMessage *message, 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); + return wpas_dbus_error_no_memory(message); + + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; - if (wpas_p2p_listen(wpa_s, (unsigned int)timeout)) - 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, + WPAS_DBUS_ERROR_UNKNOWN_ERROR, + "Could not start P2P listen"); + } return NULL; } @@ -205,17 +219,20 @@ DBusMessage * wpas_dbus_handler_p2p_extendedlisten( if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; - if (!os_strcmp(entry.key, "period") && - (entry.type == DBUS_TYPE_INT32)) + if (os_strcmp(entry.key, "period") == 0 && + entry.type == DBUS_TYPE_INT32) period = entry.uint32_value; - else if (!os_strcmp(entry.key, "interval") && - (entry.type == DBUS_TYPE_INT32)) + else if (os_strcmp(entry.key, "interval") == 0 && + entry.type == DBUS_TYPE_INT32) interval = entry.uint32_value; else goto error_clear; wpa_dbus_dict_entry_clear(&entry); } + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + if (wpas_p2p_ext_listen(wpa_s, period, interval)) return wpas_dbus_error_unknown_error( message, "failed to initiate a p2p_ext_listen."); @@ -247,16 +264,16 @@ DBusMessage * wpas_dbus_handler_p2p_presence_request( if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; - if (!os_strcmp(entry.key, "duration1") && - (entry.type == DBUS_TYPE_INT32)) + if (os_strcmp(entry.key, "duration1") == 0 && + entry.type == DBUS_TYPE_INT32) dur1 = entry.uint32_value; - else if (!os_strcmp(entry.key, "interval1") && + else if (os_strcmp(entry.key, "interval1") == 0 && entry.type == DBUS_TYPE_INT32) int1 = entry.uint32_value; - else if (!os_strcmp(entry.key, "duration2") && + else if (os_strcmp(entry.key, "duration2") == 0 && entry.type == DBUS_TYPE_INT32) dur2 = entry.uint32_value; - else if (!os_strcmp(entry.key, "interval2") && + else if (os_strcmp(entry.key, "interval2") == 0 && entry.type == DBUS_TYPE_INT32) int2 = entry.uint32_value; else @@ -264,6 +281,10 @@ DBusMessage * wpas_dbus_handler_p2p_presence_request( wpa_dbus_dict_entry_clear(&entry); } + + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + if (wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2) < 0) return wpas_dbus_error_unknown_error(message, "Failed to invoke presence request."); @@ -288,7 +309,6 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, int persistent_group = 0; int freq = 0; char *iface = NULL; - char *net_id_str = NULL; unsigned int group_id = 0; struct wpa_ssid *ssid; @@ -301,15 +321,16 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, 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)) { + if (os_strcmp(entry.key, "persistent") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + persistent_group = entry.bool_value; + } else if (os_strcmp(entry.key, "frequency") == 0 && + 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") && + } else if (os_strcmp(entry.key, "persistent_group_object") == + 0 && entry.type == DBUS_TYPE_OBJECT_PATH) pg_object_path = os_strdup(entry.str_value); else @@ -318,15 +339,21 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, wpa_dbus_dict_entry_clear(&entry); } + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + if (pg_object_path != NULL) { + char *net_id_str; + /* * 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 || + iface = wpas_dbus_new_decompose_object_path( + pg_object_path, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART, + &net_id_str); + if (iface == NULL || net_id_str == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, @@ -346,18 +373,18 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message, if (ssid == NULL || ssid->disabled != 2) goto inv_args; - if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0)) { + if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, 0, 0, + NULL, 0)) { 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, 0)) + } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0)) goto inv_args; out: os_free(pg_object_path); - os_free(net_id_str); os_free(iface); return reply; inv_args_clear: @@ -392,8 +419,7 @@ static dbus_bool_t wpa_dbus_p2p_check_enabled(struct wpa_supplicant *wpa_s, "P2P is not available for this interface"); } dbus_set_error_const(error, DBUS_ERROR_FAILED, - "P2P is not available for this " - "interface"); + "P2P is not available for this interface"); return FALSE; } return TRUE; @@ -408,6 +434,9 @@ DBusMessage * wpas_dbus_handler_p2p_flush(DBusMessage *message, if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL)) return reply; + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); wpa_s->force_long_sd = 0; p2p_flush(wpa_s->global->p2p); @@ -448,42 +477,42 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message, if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto inv_args; - if (!os_strcmp(entry.key, "peer") && - (entry.type == DBUS_TYPE_OBJECT_PATH)) { + if (os_strcmp(entry.key, "peer") == 0 && + 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)) { + } else if (os_strcmp(entry.key, "persistent") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + persistent_group = entry.bool_value; + } else if (os_strcmp(entry.key, "join") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + join = entry.bool_value; + } else if (os_strcmp(entry.key, "authorize_only") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + authorize_only = entry.bool_value; + } else if (os_strcmp(entry.key, "frequency") == 0 && + 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)) { + } else if (os_strcmp(entry.key, "go_intent") == 0 && + 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")) + } else if (os_strcmp(entry.key, "wps_method") == 0 && + entry.type == DBUS_TYPE_STRING) { + if (os_strcmp(entry.str_value, "pbc") == 0) wps_method = WPS_PBC; - else if (!os_strcmp(entry.str_value, "pin")) + else if (os_strcmp(entry.str_value, "pin") == 0) wps_method = WPS_PIN_DISPLAY; - else if (!os_strcmp(entry.str_value, "display")) + else if (os_strcmp(entry.str_value, "display") == 0) wps_method = WPS_PIN_DISPLAY; - else if (!os_strcmp(entry.str_value, "keypad")) + else if (os_strcmp(entry.str_value, "keypad") == 0) wps_method = WPS_PIN_KEYPAD; else goto inv_args_clear; - } else if (!os_strcmp(entry.key, "pin") && - (entry.type == DBUS_TYPE_STRING)) { + } else if (os_strcmp(entry.key, "pin") == 0 && + entry.type == DBUS_TYPE_STRING) { pin = os_strdup(entry.str_value); } else goto inv_args_clear; @@ -491,24 +520,28 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message, wpa_dbus_dict_entry_clear(&entry); } - if (!peer_object_path || (wps_method == WPS_NOT_READY) || - (parse_peer_object_path(peer_object_path, addr) < 0) || + if (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)) + if ((!pin || !pin[0]) && wps_method == WPS_PIN_KEYPAD) goto inv_args; + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method, persistent_group, 0, join, authorize_only, - go_intent, freq, -1, 0, 0); + go_intent, freq, -1, 0, 0, 0); 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); @@ -517,8 +550,8 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message, } else { switch (new_pin) { case -2: - err_msg = "connect failed due to channel " - "unavailability."; + err_msg = + "connect failed due to channel unavailability."; iface = WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNAVAILABLE; break; @@ -564,7 +597,6 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, 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; @@ -582,12 +614,13 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto err; - if (!os_strcmp(entry.key, "peer") && - (entry.type == DBUS_TYPE_OBJECT_PATH)) { + if (os_strcmp(entry.key, "peer") == 0 && + 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)) { + } else if (os_strcmp(entry.key, "persistent_group_object") == + 0 && + entry.type == DBUS_TYPE_OBJECT_PATH) { pg_object_path = os_strdup(entry.str_value); persistent = 1; wpa_dbus_dict_entry_clear(&entry); @@ -597,21 +630,25 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, } } - if (!peer_object_path || - (parse_peer_object_path(peer_object_path, peer_addr) < 0) || - !p2p_peer_known(wpa_s->global->p2p, peer_addr)) { + if (parse_peer_object_path(peer_object_path, peer_addr) < 0 || + !p2p_peer_known(wpa_s->global->p2p, peer_addr)) goto err; - } + + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; if (persistent) { + char *net_id_str; /* * 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 || + iface = wpas_dbus_new_decompose_object_path( + pg_object_path, + WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART, + &net_id_str); + if (iface == NULL || net_id_str == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, pg_object_path); @@ -630,7 +667,8 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, if (ssid == NULL || ssid->disabled != 2) goto err; - if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0) < 0) { + if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0) < + 0) { reply = wpas_dbus_error_unknown_error( message, "Failed to reinvoke a persistent group"); @@ -649,6 +687,7 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message, } out: + os_free(iface); os_free(pg_object_path); os_free(peer_object_path); return reply; @@ -687,8 +726,11 @@ DBusMessage * wpas_dbus_handler_p2p_prov_disc_req(DBusMessage *message, os_strcmp(config_method, "pushbutton")) return wpas_dbus_error_invalid_args(message, NULL); + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method, - WPAS_P2P_PD_FOR_GO_NEG) < 0) + WPAS_P2P_PD_FOR_GO_NEG, NULL) < 0) return wpas_dbus_error_unknown_error(message, "Failed to send provision discovery request"); @@ -716,6 +758,9 @@ dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter, if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error)) return FALSE; + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{sv}", &variant_iter) || !wpa_dbus_dict_open_write(&variant_iter, &dict_iter)) @@ -729,8 +774,8 @@ dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter, /* Primary device type */ if (!wpa_dbus_dict_append_byte_array(&dict_iter, "PrimaryDeviceType", - (char *)wpa_s->conf->device_type, - WPS_DEV_TYPE_LEN)) + (char *) wpa_s->conf->device_type, + WPS_DEV_TYPE_LEN)) goto err_no_mem; /* Secondary device types */ @@ -765,65 +810,37 @@ dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter, 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) || + if ((num_vendor_extensions && + !wpa_dbus_dict_append_wpabuf_array(&dict_iter, + "VendorExtension", + vendor_ext, + num_vendor_extensions)) || + !wpa_dbus_dict_append_uint32(&dict_iter, "GOIntent", + wpa_s->conf->p2p_go_intent) || + !wpa_dbus_dict_append_bool(&dict_iter, "PersistentReconnect", + wpa_s->conf->persistent_reconnect) || + !wpa_dbus_dict_append_uint32(&dict_iter, "ListenRegClass", + wpa_s->conf->p2p_listen_reg_class) || + !wpa_dbus_dict_append_uint32(&dict_iter, "ListenChannel", + wpa_s->conf->p2p_listen_channel) || + !wpa_dbus_dict_append_uint32(&dict_iter, "OperRegClass", + wpa_s->conf->p2p_oper_reg_class) || + !wpa_dbus_dict_append_uint32(&dict_iter, "OperChannel", + wpa_s->conf->p2p_oper_channel) || + (wpa_s->conf->p2p_ssid_postfix && + !wpa_dbus_dict_append_string(&dict_iter, "SsidPostfix", + wpa_s->conf->p2p_ssid_postfix)) || + !wpa_dbus_dict_append_bool(&dict_iter, "IntraBss", + wpa_s->conf->p2p_intra_bss) || + !wpa_dbus_dict_append_uint32(&dict_iter, "GroupIdle", + wpa_s->conf->p2p_group_idle) || + !wpa_dbus_dict_append_uint32(&dict_iter, "disassoc_low_ack", + wpa_s->conf->disassoc_low_ack) || + !wpa_dbus_dict_append_bool(&dict_iter, "NoGroupIface", + wpa_s->conf->p2p_no_group_iface) || + !wpa_dbus_dict_append_uint32(&dict_iter, "p2p_search_delay", + wpa_s->conf->p2p_search_delay) || + !wpa_dbus_dict_close_write(&variant_iter, &dict_iter) || !dbus_message_iter_close_container(iter, &variant_iter)) goto err_no_mem; @@ -847,6 +864,9 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter, if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error)) return FALSE; + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + dbus_message_iter_recurse(iter, &variant_iter); if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error)) return FALSE; @@ -902,8 +922,8 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter, 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) || + if (entry.type != DBUS_TYPE_ARRAY || + entry.array_type != WPAS_DBUS_TYPE_BINARRAY || (entry.array_len > P2P_MAX_WPS_VENDOR_EXT)) goto error; @@ -919,30 +939,30 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter, } else wpa_s->conf->wps_vendor_ext[i] = NULL; } - } else if ((os_strcmp(entry.key, "GOIntent") == 0) && - (entry.type == DBUS_TYPE_UINT32) && + } 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)) + 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)) { + 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)) { + } 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)) { + } 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)) { + } 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; @@ -961,17 +981,23 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter, wpa_s->conf->changed_parameters |= CFG_CHANGED_P2P_SSID_POSTFIX; - } else if ((os_strcmp(entry.key, "IntraBss") == 0) && - (entry.type == DBUS_TYPE_BOOLEAN)) { + } 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)) + } 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 if (os_strcmp(entry.key, "NoGroupIface") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) + wpa_s->conf->p2p_no_group_iface = entry.bool_value; + else if (os_strcmp(entry.key, "p2p_search_delay") == 0 && + entry.type == DBUS_TYPE_UINT32) + wpa_s->conf->p2p_search_delay = entry.uint32_value; else goto error; @@ -1125,6 +1151,7 @@ dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error, break; default: str = "device"; + break; } return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &str, @@ -1240,8 +1267,8 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type( dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter, - DBusError *error, - void *user_data) + DBusError *error, + void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1265,8 +1292,8 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter, dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter, - DBusError *error, - void *user_data) + DBusError *error, + void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1290,8 +1317,8 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter, dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter, - DBusError *error, - void *user_data) + DBusError *error, + void *user_data) { struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1349,8 +1376,7 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types( 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"); + dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer"); return FALSE; } @@ -1358,18 +1384,13 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types( 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, + &variant_iter) || + !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__); + "%s: failed to construct message 1", __func__); return FALSE; } @@ -1384,29 +1405,14 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types( 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_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( + &sec_dev_type_list, WPS_DEV_TYPE_LEN) || + !dbus_message_iter_close_container( &array_iter, &inner_array_iter)) { dbus_set_error(error, DBUS_ERROR_FAILED, - "%s: failed to construct " - "message 5 (%d)", + "%s: failed to construct message 2 (%d)", __func__, i); return FALSE; } @@ -1415,15 +1421,10 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types( } } - if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) { + if (!dbus_message_iter_close_container(&variant_iter, &array_iter) || + !dbus_message_iter_close_container(iter, &variant_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__); + "%s: failed to construct message 3", __func__); return FALSE; } @@ -1436,7 +1437,7 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter, void *user_data) { struct wpabuf *vendor_extension[P2P_MAX_WPS_VENDOR_EXT]; - int i, num; + unsigned int i, num = 0; struct peer_handler_args *peer_args = user_data; const struct p2p_peer_info *info; @@ -1449,7 +1450,8 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter, } /* Add WPS vendor extensions attribute */ - for (i = 0, num = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { + os_memset(vendor_extension, 0, sizeof(vendor_extension)); + for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { if (info->wps_vendor_ext[i] == NULL) continue; vendor_extension[num] = info->wps_vendor_ext[i]; @@ -1468,11 +1470,149 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter, 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; */ + 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 (info->wfd_subelems == NULL) + return wpas_dbus_simple_array_property_getter(iter, + DBUS_TYPE_BYTE, + NULL, 0, error); + + return wpas_dbus_simple_array_property_getter( + iter, DBUS_TYPE_BYTE, (char *) info->wfd_subelems->buf, + info->wfd_subelems->used, error); +} + + +dbus_bool_t wpas_dbus_getter_p2p_peer_device_address(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; + } + + return wpas_dbus_simple_array_property_getter( + iter, DBUS_TYPE_BYTE, (char *) info->p2p_device_addr, + ETH_ALEN, error); +} + + +struct peer_group_data { + struct wpa_supplicant *wpa_s; + const struct p2p_peer_info *info; + char **paths; + unsigned int nb_paths; + int error; +}; + + +static int match_group_where_peer_is_client(struct p2p_group *group, + void *user_data) +{ + struct peer_group_data *data = user_data; + const struct p2p_group_config *cfg; + struct wpa_supplicant *wpa_s_go; + char **paths; + + if (!p2p_group_is_client_connected(group, data->info->p2p_device_addr)) + return 1; + + cfg = p2p_group_get_config(group); + + wpa_s_go = wpas_get_p2p_go_iface(data->wpa_s, cfg->ssid, + cfg->ssid_len); + if (wpa_s_go == NULL) + return 1; + + paths = os_realloc_array(data->paths, data->nb_paths + 1, + sizeof(char *)); + if (paths == NULL) + goto out_of_memory; + + data->paths = paths; + data->paths[data->nb_paths] = wpa_s_go->dbus_groupobj_path; + data->nb_paths++; + + return 1; + +out_of_memory: + data->error = ENOMEM; + return 0; +} + + +dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct peer_handler_args *peer_args = user_data; + const struct p2p_peer_info *info; + struct peer_group_data data; + struct wpa_supplicant *wpa_s, *wpa_s_go; + dbus_bool_t success = FALSE; + + 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; + } + + os_memset(&data, 0, sizeof(data)); + + wpa_s = peer_args->wpa_s; + if (wpa_s->p2p_dev) + wpa_s = wpa_s->p2p_dev; + + wpa_s_go = wpas_get_p2p_client_iface(wpa_s, info->p2p_device_addr); + if (wpa_s_go) { + data.paths = os_calloc(1, sizeof(char *)); + if (data.paths == NULL) + goto out_of_memory; + data.paths[0] = wpa_s_go->dbus_groupobj_path; + data.nb_paths = 1; + } - success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, - NULL, 0, error); + data.wpa_s = peer_args->wpa_s; + data.info = info; + + p2p_loop_on_all_groups(peer_args->wpa_s->global->p2p, + match_group_where_peer_is_client, &data); + if (data.error) + goto out_of_memory; + + if (data.paths == NULL) { + return wpas_dbus_simple_array_property_getter( + iter, DBUS_TYPE_OBJECT_PATH, NULL, 0, error); + } + + success = wpas_dbus_simple_array_property_getter(iter, + DBUS_TYPE_OBJECT_PATH, + data.paths, + data.nb_paths, error); + goto out; + +out_of_memory: + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); +out: + os_free(data.paths); return success; } @@ -1496,15 +1636,6 @@ dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter, 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++; @@ -1617,12 +1748,12 @@ DBusMessage * wpas_dbus_handler_add_persistent_group( ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) { - wpa_printf(MSG_ERROR, "dbus: %s: " - "Cannot add new persistent group", __func__); + 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."); + "wpa_supplicant could not add a persistent group on this interface."); goto err; } @@ -1635,13 +1766,12 @@ DBusMessage * wpas_dbus_handler_add_persistent_group( 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"); + 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; } @@ -1653,15 +1783,13 @@ DBusMessage * wpas_dbus_handler_add_persistent_group( reply = dbus_message_new_method_return(message); if (reply == NULL) { - reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + reply = wpas_dbus_error_no_memory(message); 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); + reply = wpas_dbus_error_no_memory(message); goto err; } @@ -1691,7 +1819,7 @@ DBusMessage * wpas_dbus_handler_remove_persistent_group( { DBusMessage *reply = NULL; const char *op; - char *iface = NULL, *persistent_group_id = NULL; + char *iface = NULL, *persistent_group_id; int id; struct wpa_ssid *ssid; @@ -1702,10 +1830,11 @@ DBusMessage * wpas_dbus_handler_remove_persistent_group( * 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) { + iface = wpas_dbus_new_decompose_object_path( + op, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART, + &persistent_group_id); + if (iface == NULL || persistent_group_id == NULL || + os_strcmp(iface, wpa_s->dbus_new_path) != 0) { reply = wpas_dbus_error_invalid_args(message, op); goto out; } @@ -1725,19 +1854,17 @@ DBusMessage * wpas_dbus_handler_remove_persistent_group( 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", + 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."); + "error removing the specified persistent group on this interface."); goto out; } out: os_free(iface); - os_free(persistent_group_id); return reply; } @@ -1748,8 +1875,8 @@ static void remove_persistent_group(struct wpa_supplicant *wpa_s, 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", + wpa_printf(MSG_ERROR, + "dbus: %s: error occurred when removing persistent group %d", __func__, ssid->id); return; } @@ -1826,9 +1953,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter, if (!paths[i]) goto out_of_memory; os_snprintf(paths[i], WPAS_DBUS_OBJECT_PATH_MAX, - "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART + "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR, - wpa_s->dbus_groupobj_path, MAC2STR(addr)); + wpa_s->parent->dbus_new_path, MAC2STR(addr)); i++; } @@ -1857,6 +1984,7 @@ 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( @@ -1917,15 +2045,14 @@ dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter, void *user_data) { struct wpa_supplicant *wpa_s = user_data; - u8 role = wpas_get_p2p_role(wpa_s); - char *p_pass = NULL; + char *p_pass; + struct wpa_ssid *ssid = wpa_s->current_ssid; - /* 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 + if (ssid == NULL) + return FALSE; + + p_pass = ssid->passphrase; + if (!p_pass) p_pass = ""; return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, @@ -1938,20 +2065,20 @@ 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; + struct wpa_ssid *ssid = wpa_s->current_ssid; - /* 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; + if (ssid == NULL) + return FALSE; + + if (ssid->psk_set) { + p_psk = ssid->psk; + psk_len = sizeof(ssid->psk); } return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, - &p_psk, psk_len, error); + p_psk, psk_len, error); } @@ -1962,8 +2089,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter, 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; + unsigned int i, num_vendor_ext = 0; + + os_memset(vendor_ext, 0, sizeof(vendor_ext)); /* Verify correct role for this property */ if (wpas_get_p2p_role(wpa_s) == WPAS_P2P_ROLE_GO) { @@ -1974,11 +2102,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter, /* 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]; - } + continue; + vendor_ext[num_vendor_ext++] = + hapd->conf->wps_vendor_ext[i]; } } @@ -1987,7 +2113,7 @@ dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter, DBUS_TYPE_BYTE, vendor_ext, num_vendor_ext, - error); + error); } @@ -1996,7 +2122,7 @@ dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter, void *user_data) { struct wpa_supplicant *wpa_s = user_data; - DBusMessageIter variant_iter, iter_dict; + DBusMessageIter variant_iter, iter_dict, array_iter, sub; struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING }; unsigned int i; struct hostapd_data *hapd = NULL; @@ -2008,6 +2134,82 @@ dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter, return FALSE; dbus_message_iter_recurse(iter, &variant_iter); + if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY) + return FALSE; + + /* + * This is supposed to be array of bytearrays (aay), but the earlier + * implementation used a dict with "WPSVendorExtensions" as the key in + * this setter function which does not match the format used by the + * getter function. For backwards compatibility, allow both formats to + * be used in the setter. + */ + if (dbus_message_iter_get_element_type(&variant_iter) == + DBUS_TYPE_ARRAY) { + /* This is the proper format matching the getter */ + struct wpabuf *vals[MAX_WPS_VENDOR_EXTENSIONS]; + + dbus_message_iter_recurse(&variant_iter, &array_iter); + + if (dbus_message_iter_get_arg_type(&array_iter) != + DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&array_iter) != + DBUS_TYPE_BYTE) { + wpa_printf(MSG_DEBUG, + "dbus: Not an array of array of bytes"); + return FALSE; + } + + i = 0; + os_memset(vals, 0, sizeof(vals)); + + while (dbus_message_iter_get_arg_type(&array_iter) == + DBUS_TYPE_ARRAY) { + char *val; + int len; + + if (i == MAX_WPS_VENDOR_EXTENSIONS) { + wpa_printf(MSG_DEBUG, + "dbus: Too many WPSVendorExtensions values"); + i = MAX_WPS_VENDOR_EXTENSIONS + 1; + break; + } + + dbus_message_iter_recurse(&array_iter, &sub); + dbus_message_iter_get_fixed_array(&sub, &val, &len); + wpa_hexdump(MSG_DEBUG, "dbus: WPSVendorExtentions[]", + val, len); + vals[i] = wpabuf_alloc_copy(val, len); + if (vals[i] == NULL) { + i = MAX_WPS_VENDOR_EXTENSIONS + 1; + break; + } + i++; + dbus_message_iter_next(&array_iter); + } + + if (i > MAX_WPS_VENDOR_EXTENSIONS) { + for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) + wpabuf_free(vals[i]); + return FALSE; + } + + for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { + wpabuf_free(hapd->conf->wps_vendor_ext[i]); + hapd->conf->wps_vendor_ext[i] = vals[i]; + } + + hostapd_update_wps(hapd); + + return TRUE; + } + + if (dbus_message_iter_get_element_type(&variant_iter) != + DBUS_TYPE_DICT_ENTRY) + return FALSE; + + wpa_printf(MSG_DEBUG, + "dbus: Try to use backwards compatibility version of WPSVendorExtensions setter"); if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error)) return FALSE; @@ -2025,6 +2227,7 @@ dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter, goto error; for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) { + wpabuf_free(hapd->conf->wps_vendor_ext[i]); if (i < entry.array_len) { hapd->conf->wps_vendor_ext[i] = entry.binarray_value[i]; @@ -2073,30 +2276,31 @@ DBusMessage * wpas_dbus_handler_p2p_add_service(DBusMessage *message, 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")) + if (os_strcmp(entry.key, "service_type") == 0 && + entry.type == DBUS_TYPE_STRING) { + if (os_strcmp(entry.str_value, "upnp") == 0) upnp = 1; - else if (!os_strcmp(entry.str_value, "bonjour")) + else if (os_strcmp(entry.str_value, "bonjour") == 0) bonjour = 1; else goto error_clear; - } else if (!os_strcmp(entry.key, "version") && - entry.type == DBUS_TYPE_INT32) { + } else if (os_strcmp(entry.key, "version") == 0 && + entry.type == DBUS_TYPE_INT32) { version = entry.uint32_value; - } else if (!os_strcmp(entry.key, "service") && - (entry.type == DBUS_TYPE_STRING)) { + } else if (os_strcmp(entry.key, "service") == 0 && + entry.type == DBUS_TYPE_STRING) { + os_free(service); service = os_strdup(entry.str_value); - } else if (!os_strcmp(entry.key, "query")) { - if ((entry.type != DBUS_TYPE_ARRAY) || - (entry.array_type != DBUS_TYPE_BYTE)) + } else if (os_strcmp(entry.key, "query") == 0) { + 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)) + } else if (os_strcmp(entry.key, "response") == 0) { + 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); @@ -2111,8 +2315,6 @@ DBusMessage * wpas_dbus_handler_p2p_add_service(DBusMessage *message, if (wpas_p2p_service_add_upnp(wpa_s, version, service) != 0) goto error; - os_free(service); - service = NULL; } else if (bonjour == 1) { if (query == NULL || resp == NULL) goto error; @@ -2124,6 +2326,7 @@ DBusMessage * wpas_dbus_handler_p2p_add_service(DBusMessage *message, } else goto error; + os_free(service); return reply; error_clear: wpa_dbus_dict_entry_clear(&entry); @@ -2158,11 +2361,11 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service( 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")) + if (os_strcmp(entry.key, "service_type") == 0 && + entry.type == DBUS_TYPE_STRING) { + if (os_strcmp(entry.str_value, "upnp") == 0) upnp = 1; - else if (!os_strcmp(entry.str_value, "bonjour")) + else if (os_strcmp(entry.str_value, "bonjour") == 0) bonjour = 1; else goto error_clear; @@ -2173,13 +2376,14 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service( 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") && + if (os_strcmp(entry.key, "version") == 0 && entry.type == DBUS_TYPE_INT32) version = entry.uint32_value; - else if (!os_strcmp(entry.key, "service") && - entry.type == DBUS_TYPE_STRING) + else if (os_strcmp(entry.key, "service") == 0 && + entry.type == DBUS_TYPE_STRING) { + os_free(service); service = os_strdup(entry.str_value); - else + } else goto error_clear; wpa_dbus_dict_entry_clear(&entry); @@ -2189,7 +2393,6 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service( goto error; ret = wpas_p2p_service_del_upnp(wpa_s, version, service); - os_free(service); if (ret != 0) goto error; } else if (bonjour == 1) { @@ -2197,10 +2400,11 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service( 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)) + if (os_strcmp(entry.key, "query") == 0) { + if (entry.type != DBUS_TYPE_ARRAY || + entry.array_type != DBUS_TYPE_BYTE) goto error_clear; + wpabuf_free(query); query = wpabuf_alloc_copy( entry.bytearray_value, entry.array_len); @@ -2216,14 +2420,17 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service( ret = wpas_p2p_service_del_bonjour(wpa_s, query); if (ret != 0) goto error; - wpabuf_free(query); } else goto error; + wpabuf_free(query); + os_free(service); return reply; error_clear: wpa_dbus_dict_entry_clear(&entry); error: + wpabuf_free(query); + os_free(service); return wpas_dbus_error_invalid_args(message, NULL); } @@ -2259,22 +2466,22 @@ DBusMessage * wpas_dbus_handler_p2p_service_sd_req( 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") && + if (os_strcmp(entry.key, "peer_object") == 0 && entry.type == DBUS_TYPE_OBJECT_PATH) { peer_object_path = os_strdup(entry.str_value); - } else if (!os_strcmp(entry.key, "service_type") && + } else if (os_strcmp(entry.key, "service_type") == 0 && entry.type == DBUS_TYPE_STRING) { - if (!os_strcmp(entry.str_value, "upnp")) + if (os_strcmp(entry.str_value, "upnp") == 0) upnp = 1; else goto error_clear; - } else if (!os_strcmp(entry.key, "version") && + } else if (os_strcmp(entry.key, "version") == 0 && entry.type == DBUS_TYPE_INT32) { version = entry.uint32_value; - } else if (!os_strcmp(entry.key, "service") && + } else if (os_strcmp(entry.key, "service") == 0 && entry.type == DBUS_TYPE_STRING) { service = os_strdup(entry.str_value); - } else if (!os_strcmp(entry.key, "tlv")) { + } else if (os_strcmp(entry.key, "tlv") == 0) { if (entry.type != DBUS_TYPE_ARRAY || entry.array_type != DBUS_TYPE_BYTE) goto error_clear; @@ -2352,16 +2559,17 @@ DBusMessage * wpas_dbus_handler_p2p_service_sd_res( if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; - if (!os_strcmp(entry.key, "peer_object") && + if (os_strcmp(entry.key, "peer_object") == 0 && entry.type == DBUS_TYPE_OBJECT_PATH) { peer_object_path = os_strdup(entry.str_value); - } else if (!os_strcmp(entry.key, "frequency") && + } else if (os_strcmp(entry.key, "frequency") == 0 && entry.type == DBUS_TYPE_INT32) { freq = entry.uint32_value; - } else if (!os_strcmp(entry.key, "dialog_token") && - entry.type == DBUS_TYPE_UINT32) { + } else if (os_strcmp(entry.key, "dialog_token") == 0 && + (entry.type == DBUS_TYPE_UINT32 || + entry.type == DBUS_TYPE_INT32)) { dlg_tok = entry.uint32_value; - } else if (!os_strcmp(entry.key, "tlvs")) { + } else if (os_strcmp(entry.key, "tlvs") == 0) { if (entry.type != DBUS_TYPE_ARRAY || entry.array_type != DBUS_TYPE_BYTE) goto error_clear; @@ -2372,12 +2580,9 @@ DBusMessage * wpas_dbus_handler_p2p_service_sd_res( 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) + if (parse_peer_object_path(peer_object_path, addr) < 0 || + !p2p_peer_known(wpa_s->global->p2p, addr) || + tlv == NULL) goto error; wpas_p2p_sd_response(wpa_s, freq, addr, (u8) dlg_tok, tlv); @@ -2405,7 +2610,7 @@ DBusMessage * wpas_dbus_handler_p2p_service_sd_cancel_req( if (req == 0) goto error; - if (!wpas_p2p_sd_cancel_request(wpa_s, req)) + if (wpas_p2p_sd_cancel_request(wpa_s, req) < 0) goto error; return NULL; @@ -2436,3 +2641,77 @@ DBusMessage * wpas_dbus_handler_p2p_serv_disc_external( return NULL; } + + +#ifdef CONFIG_WIFI_DISPLAY + +dbus_bool_t wpas_dbus_getter_global_wfd_ies(DBusMessageIter *iter, + DBusError *error, void *user_data) +{ + struct wpa_global *global = user_data; + struct wpabuf *ie; + dbus_bool_t ret; + + ie = wifi_display_get_wfd_ie(global); + if (ie == NULL) + return wpas_dbus_simple_array_property_getter(iter, + DBUS_TYPE_BYTE, + NULL, 0, error); + + ret = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, + wpabuf_head(ie), + wpabuf_len(ie), error); + wpabuf_free(ie); + + return ret; +} + + +dbus_bool_t wpas_dbus_setter_global_wfd_ies(DBusMessageIter *iter, + DBusError *error, void *user_data) +{ + struct wpa_global *global = user_data; + DBusMessageIter variant, array; + struct wpabuf *ie = NULL; + const u8 *data; + int len; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) + goto err; + + dbus_message_iter_recurse(iter, &variant); + if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY) + goto err; + + dbus_message_iter_recurse(&variant, &array); + dbus_message_iter_get_fixed_array(&array, &data, &len); + if (len == 0) { + wifi_display_enable(global, 0); + wifi_display_deinit(global); + + return TRUE; + } + + ie = wpabuf_alloc(len); + if (ie == NULL) + goto err; + + wpabuf_put_data(ie, data, len); + if (wifi_display_subelem_set_from_ies(global, ie) != 0) + goto err; + + if (global->wifi_display == 0) + wifi_display_enable(global, 1); + + wpabuf_free(ie); + + return TRUE; +err: + wpabuf_free(ie); + + dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS, + "invalid message format"); + return FALSE; +} + +#endif /* CONFIG_WIFI_DISPLAY */ diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h index a11b3c8d47773..fdaccbafb1437 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.h +++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.h @@ -14,11 +14,6 @@ struct peer_handler_args { u8 p2p_device_addr[ETH_ALEN]; }; -struct groupmember_handler_args { - struct wpa_supplicant *wpa_s; - u8 member_addr[ETH_ALEN]; -}; - /* * P2P Device methods */ @@ -114,39 +109,47 @@ dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter, */ dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter, - DBusError *error, - void *user_data); + 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); + DBusError *error, + void *user_data); dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter, - DBusError *error, - void *user_data); + DBusError *error, + void *user_data); dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter, - DBusError *error, - void *user_data); + DBusError *error, + void *user_data); dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter, - DBusError *error, - void *user_data); + 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); + DBusError *error, + void *user_data); dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter, DBusError *error, void *user_data); +dbus_bool_t wpas_dbus_getter_p2p_peer_device_address(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter, + DBusError *error, + void *user_data); + /* * P2P Group properties */ @@ -207,5 +210,16 @@ DBusMessage * wpas_dbus_handler_remove_persistent_group( DBusMessage * wpas_dbus_handler_remove_all_persistent_groups( DBusMessage *message, struct wpa_supplicant *wpa_s); +#ifdef CONFIG_WIFI_DISPLAY + +dbus_bool_t wpas_dbus_getter_global_wfd_ies(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +dbus_bool_t wpas_dbus_setter_global_wfd_ies(DBusMessageIter *iter, + DBusError *error, + void *user_data); + +#endif /* CONFIG_WIFI_DISPLAY */ #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 4ad5e7e0bd407..a94a0e51fc294 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_wps.c +++ b/wpa_supplicant/dbus/dbus_new_handlers_wps.c @@ -41,8 +41,8 @@ static int wpas_dbus_handler_wps_role(DBusMessage *message, dbus_message_iter_recurse(entry_iter, &variant_iter); if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_STRING) { - wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Role type, " - "string required"); + wpa_printf(MSG_DEBUG, + "dbus: WPS.Start - Wrong Role type, string required"); *reply = wpas_dbus_error_invalid_args(message, "Role must be a string"); return -1; @@ -70,10 +70,9 @@ static int wpas_dbus_handler_wps_type(DBusMessage *message, char *val; dbus_message_iter_recurse(entry_iter, &variant_iter); - if (dbus_message_iter_get_arg_type(&variant_iter) != - DBUS_TYPE_STRING) { - wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Type type, " - "string required"); + if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_STRING) { + wpa_printf(MSG_DEBUG, + "dbus: WPS.Start - Wrong Type type, string required"); *reply = wpas_dbus_error_invalid_args(message, "Type must be a string"); return -1; @@ -105,8 +104,8 @@ static int wpas_dbus_handler_wps_bssid(DBusMessage *message, 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 Bssid type, " - "byte array required"); + wpa_printf(MSG_DEBUG, + "dbus: WPS.Start - Wrong Bssid type, byte array required"); *reply = wpas_dbus_error_invalid_args( message, "Bssid must be a byte array"); return -1; @@ -114,8 +113,8 @@ static int wpas_dbus_handler_wps_bssid(DBusMessage *message, dbus_message_iter_recurse(&variant_iter, &array_iter); dbus_message_iter_get_fixed_array(&array_iter, ¶ms->bssid, &len); if (len != ETH_ALEN) { - wpa_printf(MSG_DEBUG, "dbus: WPS.Stsrt - Wrong Bssid length " - "%d", len); + wpa_printf(MSG_DEBUG, "dbus: WPS.Stsrt - Wrong Bssid length %d", + len); *reply = wpas_dbus_error_invalid_args(message, "Bssid is wrong length"); return -1; @@ -132,10 +131,9 @@ static int wpas_dbus_handler_wps_pin(DBusMessage *message, DBusMessageIter variant_iter; dbus_message_iter_recurse(entry_iter, &variant_iter); - if (dbus_message_iter_get_arg_type(&variant_iter) != - DBUS_TYPE_STRING) { - wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Pin type, " - "string required"); + if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_STRING) { + wpa_printf(MSG_DEBUG, + "dbus: WPS.Start - Wrong Pin type, string required"); *reply = wpas_dbus_error_invalid_args(message, "Pin must be a string"); return -1; @@ -158,8 +156,8 @@ static int wpas_dbus_handler_wps_p2p_dev_addr(DBusMessage *message, 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"); + 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; @@ -168,11 +166,11 @@ static int wpas_dbus_handler_wps_p2p_dev_addr(DBusMessage *message, 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"); + 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; @@ -249,54 +247,54 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, dbus_message_iter_next(&dict_iter); } +#ifdef CONFIG_AP + if (wpa_s->ap_iface && params.type == 1) { + if (params.pin == NULL) { + wpa_printf(MSG_DEBUG, + "dbus: WPS.Start - Pin required for registrar role"); + return wpas_dbus_error_invalid_args( + message, "Pin required for registrar role."); + } + ret = wpa_supplicant_ap_wps_pin(wpa_s, + params.bssid, + params.pin, + npin, sizeof(npin), 0); + } else if (wpa_s->ap_iface) { + ret = wpa_supplicant_ap_wps_pbc(wpa_s, + params.bssid, + params.p2p_dev_addr); + } else +#endif /* CONFIG_AP */ if (params.role == 0) { wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Role not specified"); return wpas_dbus_error_invalid_args(message, "Role not specified"); - } else if (params.role == 1 && params.type == 0) { + } else if (params.role == 2) { + if (params.pin == NULL) { + wpa_printf(MSG_DEBUG, + "dbus: WPS.Start - Pin required for registrar role"); + return wpas_dbus_error_invalid_args( + message, "Pin required for registrar role."); + } + ret = wpas_wps_start_reg(wpa_s, params.bssid, params.pin, + NULL); + } else if (params.type == 0) { wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Type not specified"); return wpas_dbus_error_invalid_args(message, "Type not specified"); - } else if (params.role == 2 && params.pin == NULL) { - wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Pin required for " - "registrar role"); - return wpas_dbus_error_invalid_args( - message, "Pin required for registrar role."); - } - - if (params.role == 2) - ret = wpas_wps_start_reg(wpa_s, params.bssid, params.pin, - NULL); - else if (params.type == 1) { -#ifdef CONFIG_AP - if (wpa_s->ap_iface) - ret = wpa_supplicant_ap_wps_pin(wpa_s, - params.bssid, - params.pin, - npin, sizeof(npin), 0); - 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 if (params.type == 1) { + 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 " - "role %s and key %s", + wpa_printf(MSG_DEBUG, + "dbus: WPS.Start wpas_wps_failed in role %s and key %s", (params.role == 1 ? "enrollee" : "registrar"), (params.type == 0 ? "" : (params.type == 1 ? "pin" : "pbc"))); @@ -305,31 +303,16 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message, } reply = dbus_message_new_method_return(message); - if (!reply) { - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); - } + if (!reply) + return wpas_dbus_error_no_memory(message); dbus_message_iter_init_append(reply, &iter); - if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) { + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + (os_strlen(npin) > 0 && + !wpa_dbus_dict_append_string(&dict_iter, "Pin", npin)) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) { dbus_message_unref(reply); - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); - } - - if (os_strlen(npin) > 0) { - if (!wpa_dbus_dict_append_string(&dict_iter, "Pin", npin)) { - dbus_message_unref(reply); - return dbus_message_new_error(message, - DBUS_ERROR_NO_MEMORY, - NULL); - } - } - - if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) { - dbus_message_unref(reply); - return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, - NULL); + return wpas_dbus_error_no_memory(message); } return reply; @@ -351,7 +334,8 @@ dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter, void *user_data) { struct wpa_supplicant *wpa_s = user_data; - dbus_bool_t process = (wpa_s->conf->wps_cred_processing != 1); + dbus_bool_t process = wpa_s->conf->wps_cred_processing != 1; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN, &process, error); } @@ -378,7 +362,7 @@ dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter, &process_credentials)) return FALSE; - old_pc = (wpa_s->conf->wps_cred_processing != 1); + old_pc = wpa_s->conf->wps_cred_processing != 1; wpa_s->conf->wps_cred_processing = (process_credentials ? 2 : 1); if ((wpa_s->conf->wps_cred_processing != 1) != old_pc) @@ -389,3 +373,62 @@ dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter, return TRUE; } + + +/** + * wpas_dbus_getter_config_methods - Get current WPS configuration methods + * @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 "ConfigMethods" property. Returned boolean will be true if + * providing the relevant string worked, or false otherwise. + */ +dbus_bool_t wpas_dbus_getter_config_methods(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + char *methods = wpa_s->conf->config_methods; + + if (methods == NULL) + methods = ""; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &methods, error); +} + + +/** + * wpas_dbus_setter_config_methods - Set WPS configuration methods + * @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 "ConfigMethods" property. Sets the methods string, apply such + * change and returns true on success. Returns false otherwise. + */ +dbus_bool_t wpas_dbus_setter_config_methods(DBusMessageIter *iter, + DBusError *error, + void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + char *methods, *new_methods; + + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING, + &methods)) + return FALSE; + + new_methods = os_strdup(methods); + if (!new_methods) + return FALSE; + + os_free(wpa_s->conf->config_methods); + wpa_s->conf->config_methods = new_methods; + + wpa_s->conf->changed_parameters |= CFG_CHANGED_CONFIG_METHODS; + wpa_supplicant_update_config(wpa_s); + + return TRUE; +} diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c index cfa6a15162a37..15b090141c978 100644 --- a/wpa_supplicant/dbus/dbus_new_helpers.c +++ b/wpa_supplicant/dbus/dbus_new_helpers.c @@ -15,6 +15,7 @@ #include "dbus_common_i.h" #include "dbus_new.h" #include "dbus_new_helpers.h" +#include "dbus_new_handlers.h" #include "dbus_dict_helpers.h" @@ -38,27 +39,25 @@ static dbus_bool_t fill_dict_with_properties( 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, + NULL, &entry_iter) || + !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; - } + &dsc->dbus_property)) + goto error; /* 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); + if (!dbus_message_iter_close_container(dict_iter, &entry_iter)) + goto error; } return TRUE; + +error: + dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory"); + return FALSE; } @@ -75,43 +74,38 @@ static dbus_bool_t fill_dict_with_properties( * 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) + struct wpa_dbus_object_desc *obj_dsc) { DBusMessage *reply; DBusMessageIter iter, dict_iter; DBusError error; reply = dbus_message_new_method_return(message); - if (reply == NULL) { - wpa_printf(MSG_ERROR, "%s: out of memory creating dbus reply", - __func__); - return NULL; - } + if (reply == NULL) + return wpas_dbus_error_no_memory(message); 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; + return wpas_dbus_error_no_memory(message); } dbus_error_init(&error); if (!fill_dict_with_properties(&dict_iter, obj_dsc->properties, - interface, obj_dsc->user_data, &error)) - { + interface, obj_dsc->user_data, &error)) { dbus_message_unref(reply); - reply = wpas_dbus_reply_new_from_error(message, &error, - 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); + if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) { + dbus_message_unref(reply); + return wpas_dbus_error_no_memory(message); + } + return reply; } @@ -132,8 +126,9 @@ static int is_signature_correct(DBusMessage *message, for (arg = method_dsc->args; arg && arg->name; arg++) { if (arg->dir == ARG_IN) { size_t blen = registered_sig + MAX_SIG_LEN - pos; + ret = os_snprintf(pos, blen, "%s", arg->type); - if (ret < 0 || (size_t) ret >= blen) + if (os_snprintf_error(blen, ret)) return 0; pos += ret; } @@ -267,10 +262,13 @@ properties_get_or_set(DBusMessage *message, DBusMessageIter *iter, } if (os_strncmp(WPA_DBUS_PROPERTIES_GET, method, - WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) == 0) + WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) == 0) { + wpa_printf(MSG_MSGDUMP, "%s: Get(%s)", __func__, property); return properties_get(message, property_dsc, obj_dsc->user_data); + } + wpa_printf(MSG_MSGDUMP, "%s: Set(%s)", __func__, property); return properties_set(message, property_dsc, obj_dsc->user_data); } @@ -292,8 +290,7 @@ static DBusMessage * properties_handler(DBusMessage *message, !os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method, WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) { /* First argument: interface name (DBUS_TYPE_STRING) */ - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) - { + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) { return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, NULL); @@ -349,8 +346,7 @@ static DBusMessage * msg_method_handler(DBusMessage *message, NULL); } - return method_dsc->method_handler(message, - obj_dsc->user_data); + return method_dsc->method_handler(message, obj_dsc->user_data); } @@ -385,8 +381,9 @@ static DBusHandlerResult message_handler(DBusConnection *connection, if (!method || !path || !msg_interface) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - wpa_printf(MSG_MSGDUMP, "dbus: %s.%s (%s)", - msg_interface, method, path); + wpa_printf(MSG_MSGDUMP, "dbus: %s.%s (%s) [%s]", + msg_interface, method, path, + dbus_message_get_signature(message)); /* if message is introspection method call */ if (!os_strncmp(WPA_DBUS_INTROSPECTION_METHOD, method, @@ -398,8 +395,7 @@ static DBusHandlerResult message_handler(DBusConnection *connection, #else /* CONFIG_CTRL_IFACE_DBUS_INTRO */ reply = dbus_message_new_error( message, DBUS_ERROR_UNKNOWN_METHOD, - "wpa_supplicant was compiled without " - "introspection support."); + "wpa_supplicant was compiled without introspection support."); #endif /* CONFIG_CTRL_IFACE_DBUS_INTRO */ } else if (!os_strncmp(WPA_DBUS_PROPERTIES_INTERFACE, msg_interface, WPAS_DBUS_INTERFACE_MAX)) { @@ -452,6 +448,7 @@ static void free_dbus_object_desc_cb(DBusConnection *connection, void *obj_dsc) free_dbus_object_desc(obj_dsc); } + /** * wpa_dbus_ctrl_iface_init - Initialize dbus control interface * @application_data: Pointer to application specific data structure @@ -479,30 +476,28 @@ int wpa_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface, obj_desc->path = os_strdup(dbus_path); /* Register the message handler for the global dbus interface */ - if (!dbus_connection_register_object_path(iface->con, - dbus_path, &wpa_vtable, - obj_desc)) { - wpa_printf(MSG_ERROR, "dbus: Could not set up message " - "handler"); + if (!dbus_connection_register_object_path(iface->con, dbus_path, + &wpa_vtable, obj_desc)) { + wpa_printf(MSG_ERROR, "dbus: Could not set up message handler"); return -1; } /* Register our service with the message bus */ dbus_error_init(&error); - switch (dbus_bus_request_name(iface->con, dbus_service, - 0, &error)) { + switch (dbus_bus_request_name(iface->con, dbus_service, 0, &error)) { case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: ret = 0; break; case DBUS_REQUEST_NAME_REPLY_EXISTS: case DBUS_REQUEST_NAME_REPLY_IN_QUEUE: case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: - wpa_printf(MSG_ERROR, "dbus: Could not request service name: " - "already registered"); + wpa_printf(MSG_ERROR, + "dbus: Could not request service name: already registered"); break; default: - wpa_printf(MSG_ERROR, "dbus: Could not request service name: " - "%s %s", error.name, error.message); + wpa_printf(MSG_ERROR, + "dbus: Could not request service name: %s %s", + error.name, error.message); break; } dbus_error_free(&error); @@ -526,14 +521,12 @@ int wpa_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface, * * Registers a new interface with dbus and assigns it a dbus object path. */ -int wpa_dbus_register_object_per_iface( - struct wpas_dbus_priv *ctrl_iface, - const char *path, const char *ifname, - struct wpa_dbus_object_desc *obj_desc) +int wpa_dbus_register_object_per_iface(struct wpas_dbus_priv *ctrl_iface, + const char *path, const char *ifname, + struct wpa_dbus_object_desc *obj_desc) { DBusConnection *con; DBusError error; - DBusObjectPathVTable vtable = { &free_dbus_object_desc_cb, &message_handler, NULL, NULL, NULL, NULL @@ -551,14 +544,12 @@ int wpa_dbus_register_object_per_iface( /* Register the message handler for the interface functions */ if (!dbus_connection_try_register_object_path(con, path, &vtable, obj_desc, &error)) { - if (!os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE)) { + if (os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE) == 0) { 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); + wpa_printf(MSG_ERROR, + "dbus: Could not set up message handler for interface %s object %s (error: %s message: %s)", + ifname, path, error.name, error.message); } dbus_error_free(&error); return -1; @@ -588,13 +579,14 @@ int wpa_dbus_unregister_object_per_iface( dbus_connection_get_object_path_data(con, path, (void **) &obj_desc); if (!obj_desc) { - wpa_printf(MSG_ERROR, "dbus: %s: Could not obtain object's " - "private data: %s", __func__, path); - } else { - eloop_cancel_timeout(flush_object_timeout_handler, con, - obj_desc); + wpa_printf(MSG_ERROR, + "dbus: %s: Could not obtain object's private data: %s", + __func__, path); + return 0; } + eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc); + if (!dbus_connection_unregister_object_path(con, path)) return -1; @@ -623,24 +615,22 @@ static dbus_bool_t put_changed_properties( if (!dbus_message_iter_open_container(dict_iter, DBUS_TYPE_DICT_ENTRY, - NULL, &entry_iter)) - return FALSE; - - if (!dbus_message_iter_append_basic(&entry_iter, + NULL, &entry_iter) || + !dbus_message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &dsc->dbus_property)) 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); + 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", + wpa_printf(MSG_ERROR, + "dbus: %s: Cannot get new value of property %s", __func__, dsc->dbus_property); } dbus_error_free(&error); @@ -670,38 +660,23 @@ static void do_send_prop_changed_signal( 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); + &interface) || + /* Changed properties dict */ + !dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, + "{sv}", &dict_iter) || + !put_changed_properties(obj_dsc, interface, &dict_iter, 0) || + !dbus_message_iter_close_container(&signal_iter, &dict_iter) || + /* Invalidated properties array (empty) */ + !dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY, + "s", &dict_iter) || + !dbus_message_iter_close_container(&signal_iter, &dict_iter)) { + wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", + __func__); + } else { + dbus_connection_send(con, msg, NULL); + } -out: dbus_message_unref(msg); - return; - -err: - wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", - __func__); - goto out; } @@ -719,25 +694,16 @@ static void do_send_deprecated_prop_changed_signal( dbus_message_iter_init_append(msg, &signal_iter); 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, 1)) - goto err; - - if (!dbus_message_iter_close_container(&signal_iter, &dict_iter)) - goto err; - - dbus_connection_send(con, msg, NULL); + "{sv}", &dict_iter) || + !put_changed_properties(obj_dsc, interface, &dict_iter, 1) || + !dbus_message_iter_close_container(&signal_iter, &dict_iter)) { + wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", + __func__); + } else { + dbus_connection_send(con, msg, NULL); + } -out: dbus_message_unref(msg); - return; - -err: - wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal", - __func__); - goto out; } @@ -769,8 +735,9 @@ static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx) DBusConnection *con = eloop_ctx; struct wpa_dbus_object_desc *obj_desc = timeout_ctx; - wpa_printf(MSG_DEBUG, "dbus: %s: Timeout - sending changed properties " - "of object %s", __func__, obj_desc->path); + wpa_printf(MSG_DEBUG, + "dbus: %s: Timeout - sending changed properties of object %s", + __func__, obj_desc->path); wpa_dbus_flush_object_changed_properties(con, obj_desc->path); } @@ -840,7 +807,6 @@ void wpa_dbus_flush_object_changed_properties(DBusConnection *con, return; eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc); - dsc = obj_desc->properties; for (dsc = obj_desc->properties, i = 0; dsc && dsc->dbus_property; dsc++, i++) { if (obj_desc->prop_changed_flags == NULL || @@ -882,8 +848,9 @@ void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface, dbus_connection_get_object_path_data(iface->con, path, (void **) &obj_desc); if (!obj_desc) { - wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: " - "could not obtain object's private data: %s", path); + wpa_printf(MSG_ERROR, + "dbus: wpa_dbus_property_changed: could not obtain object's private data: %s", + path); return; } @@ -896,13 +863,14 @@ void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface, } if (!dsc || !dsc->dbus_property) { - wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: " - "no property %s in object %s", property, path); + wpa_printf(MSG_ERROR, + "dbus: wpa_dbus_property_changed: no property %s in object %s", + property, path); return; } if (!eloop_is_timeout_registered(flush_object_timeout_handler, - iface->con, obj_desc->path)) { + iface->con, obj_desc)) { eloop_register_timeout(0, WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT, flush_object_timeout_handler, iface->con, obj_desc); @@ -934,8 +902,9 @@ dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface, dbus_connection_get_object_path_data(iface->con, path, (void **) &obj_desc); if (!obj_desc) { - wpa_printf(MSG_ERROR, "dbus: %s: could not obtain object's " - "private data: %s", __func__, path); + wpa_printf(MSG_ERROR, + "dbus: %s: could not obtain object's private data: %s", + __func__, path); return FALSE; } @@ -949,10 +918,11 @@ dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface, 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"); + 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; } @@ -963,29 +933,34 @@ dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface, /** * 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 + * @sep: Separating part (e.g., "Networks" or "PersistentGroups") + * @item: (out) The part following the specified separator, if any + * Returns: The object path of the 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. + * For a given object path, decomposes the object path into object id and + * requested part, if those parts exist. The caller is responsible for freeing + * the returned value. The *item pointer points to that allocated value and must + * not be freed separately. + * + * As an example, path = "/fi/w1/wpa_supplicant1/Interfaces/1/Networks/0" and + * sep = "Networks" would result in "/fi/w1/wpa_supplicant1/Interfaces/1" + * getting returned and *items set to point to "0". */ -char *wpas_dbus_new_decompose_object_path(const char *path, - int p2p_persistent_group, - char **network, - char **bssid) +char * wpas_dbus_new_decompose_object_path(const char *path, const char *sep, + char **item) { const unsigned int dev_path_prefix_len = os_strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/"); char *obj_path_only; - char *next_sep; + char *pos; + size_t sep_len; - /* Be a bit paranoid about path */ - if (!path || os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/", - dev_path_prefix_len)) - return NULL; + *item = NULL; + + /* Verify that this starts with our interface prefix */ + if (os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/", + dev_path_prefix_len) != 0) + return NULL; /* not our path */ /* Ensure there's something at the end of the path */ if ((path + dev_path_prefix_len)[0] == '\0') @@ -995,39 +970,20 @@ char *wpas_dbus_new_decompose_object_path(const char *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; - } + pos = obj_path_only + dev_path_prefix_len; + pos = os_strchr(pos, '/'); + if (pos == NULL) + return obj_path_only; /* no next item on the path */ - /* Cut off interface object path before "/" */ - *next_sep = '\0'; - } + /* Separate network interface prefix from the path */ + *pos++ = '\0'; + + sep_len = os_strlen(sep); + if (os_strncmp(pos, sep, sep_len) != 0 || pos[sep_len] != '/') + return obj_path_only; /* no match */ + /* return a pointer to the requested item */ + *item = pos + sep_len + 1; return obj_path_only; } diff --git a/wpa_supplicant/dbus/dbus_new_helpers.h b/wpa_supplicant/dbus/dbus_new_helpers.h index 6d31ad53ceb71..6e2c1f1933f12 100644 --- a/wpa_supplicant/dbus/dbus_new_helpers.h +++ b/wpa_supplicant/dbus/dbus_new_helpers.h @@ -12,13 +12,13 @@ #include <dbus/dbus.h> -typedef DBusMessage * (* WPADBusMethodHandler)(DBusMessage *message, - void *user_data); -typedef void (* WPADBusArgumentFreeFunction)(void *handler_arg); +typedef DBusMessage * (*WPADBusMethodHandler)(DBusMessage *message, + void *user_data); +typedef void (*WPADBusArgumentFreeFunction)(void *handler_arg); -typedef dbus_bool_t (* WPADBusPropertyAccessor)(DBusMessageIter *iter, - DBusError *error, - void *user_data); +typedef dbus_bool_t (*WPADBusPropertyAccessor)(DBusMessageIter *iter, + DBusError *error, + void *user_data); struct wpa_dbus_object_desc { DBusConnection *connection; @@ -137,10 +137,8 @@ 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); +char * wpas_dbus_new_decompose_object_path(const char *path, const char *sep, + char **item); DBusMessage *wpas_dbus_reply_new_from_error(DBusMessage *message, DBusError *error, diff --git a/wpa_supplicant/dbus/dbus_new_introspect.c b/wpa_supplicant/dbus/dbus_new_introspect.c index 3b090c028acb5..6209c67856bbe 100644 --- a/wpa_supplicant/dbus/dbus_new_introspect.c +++ b/wpa_supplicant/dbus/dbus_new_introspect.c @@ -37,14 +37,16 @@ static struct interfaces * add_interface(struct dl_list *list, iface = os_zalloc(sizeof(struct interfaces)); if (!iface) return NULL; + iface->dbus_interface = os_strdup(dbus_interface); iface->xml = wpabuf_alloc(6000); - if (iface->xml == NULL) { + if (iface->dbus_interface == NULL || iface->xml == NULL) { + os_free(iface->dbus_interface); + wpabuf_free(iface->xml); os_free(iface); return NULL; } wpabuf_printf(iface->xml, "<interface name=\"%s\">", dbus_interface); dl_list_add_tail(list, &iface->list); - iface->dbus_interface = os_strdup(dbus_interface); return iface; } @@ -96,6 +98,7 @@ static void extract_interfaces_methods( { const struct wpa_dbus_method_desc *dsc; struct interfaces *iface; + for (dsc = methods; dsc && dsc->dbus_method; dsc++) { iface = add_interface(list, dsc->dbus_interface); if (iface) @@ -110,6 +113,7 @@ static void extract_interfaces_signals( { const struct wpa_dbus_signal_desc *dsc; struct interfaces *iface; + for (dsc = signals; dsc && dsc->dbus_signal; dsc++) { iface = add_interface(list, dsc->dbus_interface); if (iface) @@ -124,6 +128,7 @@ static void extract_interfaces_properties( { const struct wpa_dbus_property_desc *dsc; struct interfaces *iface; + for (dsc = properties; dsc && dsc->dbus_property; dsc++) { iface = add_interface(list, dsc->dbus_interface); if (iface) @@ -154,14 +159,14 @@ static void extract_interfaces(struct dl_list *list, static void add_interfaces(struct dl_list *list, struct wpabuf *xml) { struct interfaces *iface, *n; + dl_list_for_each_safe(iface, n, list, struct interfaces, list) { if (wpabuf_len(iface->xml) + 20 < wpabuf_tailroom(xml)) { wpabuf_put_buf(xml, iface->xml); wpabuf_put_str(xml, "</interface>"); } else { - wpa_printf(MSG_DEBUG, "dbus: Not enough room for " - "add_interfaces inspect data: tailroom %u, " - "add %u", + 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)); } @@ -229,6 +234,7 @@ static void add_wpas_interfaces(struct wpabuf *xml, struct wpa_dbus_object_desc *obj_dsc) { struct dl_list ifaces; + dl_list_init(&ifaces); extract_interfaces(&ifaces, obj_dsc); add_interfaces(&ifaces, xml); @@ -270,6 +276,7 @@ DBusMessage * wpa_dbus_introspect(DBusMessage *message, reply = dbus_message_new_method_return(message); if (reply) { const char *intro_str = wpabuf_head(xml); + dbus_message_append_args(reply, DBUS_TYPE_STRING, &intro_str, DBUS_TYPE_INVALID); } diff --git a/wpa_supplicant/dbus/dbus_old.c b/wpa_supplicant/dbus/dbus_old.c index 5f298e76ac05f..45bb4022702fe 100644 --- a/wpa_supplicant/dbus/dbus_old.c +++ b/wpa_supplicant/dbus/dbus_old.c @@ -92,9 +92,9 @@ char * wpas_dbus_decompose_object_path(const char *path, char **network, */ DBusMessage * wpas_dbus_new_invalid_iface_error(DBusMessage *message) { - return dbus_message_new_error(message, WPAS_ERROR_INVALID_IFACE, - "wpa_supplicant knows nothing about " - "this interface."); + return dbus_message_new_error( + message, WPAS_ERROR_INVALID_IFACE, + "wpa_supplicant knows nothing about this interface."); } @@ -216,8 +216,12 @@ static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection, if (!msg_interface) goto out; + wpa_printf(MSG_MSGDUMP, "dbus[old/iface]: %s.%s (%s) [%s]", + msg_interface, method, path, + dbus_message_get_signature(message)); + iface_obj_path = wpas_dbus_decompose_object_path(path, &network, - &bssid); + &bssid); if (iface_obj_path == NULL) { reply = wpas_dbus_new_invalid_iface_error(message); goto out; @@ -227,7 +231,7 @@ static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection, * wpa_supplicant structure it's supposed to (which is wpa_s) */ if (wpa_supplicant_get_iface_by_dbus_path(wpa_s->global, - iface_obj_path) != wpa_s) { + iface_obj_path) != wpa_s) { reply = wpas_dbus_new_invalid_iface_error(message); goto out; } @@ -235,6 +239,7 @@ static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection, if (network && !strcmp(msg_interface, WPAS_DBUS_IFACE_NETWORK)) { /* A method for one of this interface's configured networks */ int nid = strtoul(network, NULL, 10); + if (errno != EINVAL) reply = wpas_dispatch_network_method(message, wpa_s, nid); @@ -268,30 +273,32 @@ static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection, reply = wpas_dbus_iface_get_state(message, wpa_s); else if (!strcmp(method, "scanning")) reply = wpas_dbus_iface_get_scanning(message, wpa_s); +#ifndef CONFIG_NO_CONFIG_BLOBS else if (!strcmp(method, "setBlobs")) reply = wpas_dbus_iface_set_blobs(message, wpa_s); else if (!strcmp(method, "removeBlobs")) reply = wpas_dbus_iface_remove_blobs(message, wpa_s); +#endif /* CONFIG_NO_CONFIG_BLOBS */ #ifdef CONFIG_WPS - else if (!os_strcmp(method, "wpsPbc")) + else if (os_strcmp(method, "wpsPbc") == 0) reply = wpas_dbus_iface_wps_pbc(message, wpa_s); - else if (!os_strcmp(method, "wpsPin")) + else if (os_strcmp(method, "wpsPin") == 0) reply = wpas_dbus_iface_wps_pin(message, wpa_s); - else if (!os_strcmp(method, "wpsReg")) + else if (os_strcmp(method, "wpsReg") == 0) reply = wpas_dbus_iface_wps_reg(message, wpa_s); #endif /* CONFIG_WPS */ - else if (!os_strcmp(method, "flush")) + else if (os_strcmp(method, "flush") == 0) reply = wpas_dbus_iface_flush(message, wpa_s); } /* If the message was handled, send back the reply */ +out: if (reply) { if (!dbus_message_get_no_reply(message)) dbus_connection_send(connection, reply, NULL); dbus_message_unref(reply); } -out: os_free(iface_obj_path); os_free(network); os_free(bssid); @@ -326,6 +333,10 @@ static DBusHandlerResult wpas_message_handler(DBusConnection *connection, if (!method || !path || !ctrl_iface || !msg_interface) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + wpa_printf(MSG_MSGDUMP, "dbus[old]: %s.%s (%s) [%s]", + msg_interface, method, path, + dbus_message_get_signature(message)); + /* Validate the method interface */ if (strcmp(msg_interface, WPAS_DBUS_INTERFACE) != 0) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -379,8 +390,8 @@ void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s) WPAS_DBUS_IFACE_INTERFACE, "ScanResultsAvailable"); if (_signal == NULL) { - wpa_printf(MSG_ERROR, "dbus: Not enough memory to send scan " - "results signal"); + wpa_printf(MSG_ERROR, + "dbus: Not enough memory to send scan results signal"); return; } dbus_connection_send(iface->con, _signal, NULL); @@ -424,29 +435,21 @@ void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s, "StateChange"); if (_signal == NULL) { wpa_printf(MSG_ERROR, - "dbus: wpa_supplicant_dbus_notify_state_change: " - "could not create dbus signal; likely out of " - "memory"); + "dbus: %s: could not create dbus signal; likely out of memory", + __func__); return; } new_state_str = wpa_supplicant_state_txt(new_state); old_state_str = wpa_supplicant_state_txt(old_state); - if (new_state_str == NULL || old_state_str == NULL) { - wpa_printf(MSG_ERROR, - "dbus: wpa_supplicant_dbus_notify_state_change: " - "Could not convert state strings"); - goto out; - } if (!dbus_message_append_args(_signal, - DBUS_TYPE_STRING, &new_state_str, - DBUS_TYPE_STRING, &old_state_str, - DBUS_TYPE_INVALID)) { + DBUS_TYPE_STRING, &new_state_str, + DBUS_TYPE_STRING, &old_state_str, + DBUS_TYPE_INVALID)) { wpa_printf(MSG_ERROR, - "dbus: wpa_supplicant_dbus_notify_state_change: " - "Not enough memory to construct state change " - "signal"); + "dbus: %s: Not enough memory to construct state change signal", + __func__); goto out; } @@ -478,18 +481,18 @@ void wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s) WPAS_DBUS_IFACE_INTERFACE, "Scanning"); if (_signal == NULL) { - wpa_printf(MSG_ERROR, "dbus: Not enough memory to send scan " - "results signal"); + wpa_printf(MSG_ERROR, + "dbus: Not enough memory to send scan results signal"); return; } if (dbus_message_append_args(_signal, - DBUS_TYPE_BOOLEAN, &scanning, - DBUS_TYPE_INVALID)) { + DBUS_TYPE_BOOLEAN, &scanning, + DBUS_TYPE_INVALID)) { dbus_connection_send(iface->con, _signal, NULL); } else { - wpa_printf(MSG_ERROR, "dbus: Not enough memory to construct " - "signal"); + wpa_printf(MSG_ERROR, + "dbus: Not enough memory to construct signal"); } dbus_message_unref(_signal); } @@ -514,19 +517,18 @@ void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, "WpsCred"); if (_signal == NULL) { wpa_printf(MSG_ERROR, - "dbus: wpa_supplicant_dbus_notify_wps_cred: " - "Could not create dbus signal; likely out of " - "memory"); + "dbus: %s: Could not create dbus signal; likely out of memory", + __func__); return; } if (!dbus_message_append_args(_signal, - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cred->cred_attr, cred->cred_attr_len, - DBUS_TYPE_INVALID)) { + DBUS_TYPE_INVALID)) { wpa_printf(MSG_ERROR, - "dbus: wpa_supplicant_dbus_notify_wps_cred: " - "Not enough memory to construct signal"); + "dbus: %s: Not enough memory to construct signal", + __func__); goto out; } @@ -565,9 +567,8 @@ void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s, "Certification"); if (_signal == NULL) { wpa_printf(MSG_ERROR, - "dbus: wpa_supplicant_dbus_notify_certification: " - "Could not create dbus signal; likely out of " - "memory"); + "dbus: %s: Could not create dbus signal; likely out of memory", + __func__); return; } @@ -576,15 +577,15 @@ void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s, cert_hex_len = cert ? wpabuf_len(cert) : 0; if (!dbus_message_append_args(_signal, - DBUS_TYPE_INT32,&depth, + DBUS_TYPE_INT32, &depth, DBUS_TYPE_STRING, &subject, - DBUS_TYPE_STRING, &hash, - DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + DBUS_TYPE_STRING, &hash, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cert_hex, cert_hex_len, - DBUS_TYPE_INVALID)) { + DBUS_TYPE_INVALID)) { wpa_printf(MSG_ERROR, - "dbus: wpa_supplicant_dbus_notify_certification: " - "Not enough memory to construct signal"); + "dbus: %s: Not enough memory to construct signal", + __func__); goto out; } @@ -616,8 +617,7 @@ int wpa_supplicant_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface) if (!dbus_connection_register_object_path(iface->con, WPAS_DBUS_PATH, &wpas_vtable, iface)) { - wpa_printf(MSG_ERROR, "dbus: Could not set up message " - "handler"); + wpa_printf(MSG_ERROR, "dbus: Could not set up message handler"); return -1; } @@ -631,12 +631,13 @@ int wpa_supplicant_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface) case DBUS_REQUEST_NAME_REPLY_EXISTS: case DBUS_REQUEST_NAME_REPLY_IN_QUEUE: case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: - wpa_printf(MSG_ERROR, "dbus: Could not request service name: " - "already registered"); + wpa_printf(MSG_ERROR, + "dbus: Could not request service name: already registered"); break; default: - wpa_printf(MSG_ERROR, "dbus: Could not request service name: " - "%s %s", error.name, error.message); + wpa_printf(MSG_ERROR, + "dbus: Could not request service name: %s %s", + error.name, error.message); break; } dbus_error_free(&error); @@ -685,8 +686,9 @@ int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s) /* Register the message handler for the interface functions */ if (!dbus_connection_register_fallback(con, wpa_s->dbus_path, &vtable, wpa_s)) { - wpa_printf(MSG_ERROR, "dbus: Could not set up message " - "handler for interface %s", wpa_s->ifname); + wpa_printf(MSG_ERROR, + "dbus: Could not set up message handler for interface %s", + wpa_s->ifname); return -1; } @@ -710,7 +712,7 @@ int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s) if (wpa_s == NULL || wpa_s->global == NULL) return 0; ctrl_iface = wpa_s->global->dbus; - if (ctrl_iface == NULL) + if (ctrl_iface == NULL || wpa_s->dbus_path == NULL) return 0; con = ctrl_iface->con; diff --git a/wpa_supplicant/dbus/dbus_old.h b/wpa_supplicant/dbus/dbus_old.h index e6682310e254a..451a9f827aa93 100644 --- a/wpa_supplicant/dbus/dbus_old.h +++ b/wpa_supplicant/dbus/dbus_old.h @@ -82,7 +82,7 @@ void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s, const struct wpabuf *cert); char * wpas_dbus_decompose_object_path(const char *path, char **network, - char **bssid); + char **bssid); int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s); int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s); @@ -104,7 +104,12 @@ wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s) { } -#define wpa_supplicant_dbus_notify_state_change(w,n,o) do { } while (0) +static inline void +wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s, + enum wpa_states new_state, + enum wpa_states old_state) +{ +} static inline void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s, diff --git a/wpa_supplicant/dbus/dbus_old_handlers.c b/wpa_supplicant/dbus/dbus_old_handlers.c index 68e551524b85e..773ee8b49a2d3 100644 --- a/wpa_supplicant/dbus/dbus_old_handlers.c +++ b/wpa_supplicant/dbus/dbus_old_handlers.c @@ -25,10 +25,6 @@ #include "dbus_old_handlers.h" #include "dbus_dict_helpers.h" -extern int wpa_debug_level; -extern int wpa_debug_show_keys; -extern int wpa_debug_timestamp; - /** * wpas_dbus_new_invalid_opts_error - Return a new invalid options error message * @message: Pointer to incoming dbus message this error refers to @@ -41,9 +37,9 @@ DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message, { DBusMessage *reply; - reply = dbus_message_new_error(message, WPAS_ERROR_INVALID_OPTS, - "Did not receive correct message " - "arguments."); + reply = dbus_message_new_error( + message, WPAS_ERROR_INVALID_OPTS, + "Did not receive correct message arguments."); if (arg != NULL) dbus_message_append_args(reply, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID); @@ -116,25 +112,29 @@ DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message, if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; if (!strcmp(entry.key, "driver") && - (entry.type == DBUS_TYPE_STRING)) { + entry.type == DBUS_TYPE_STRING) { + os_free(driver); driver = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (driver == NULL) goto error; } else if (!strcmp(entry.key, "driver-params") && - (entry.type == DBUS_TYPE_STRING)) { + entry.type == DBUS_TYPE_STRING) { + os_free(driver_param); driver_param = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (driver_param == NULL) goto error; } else if (!strcmp(entry.key, "config-file") && - (entry.type == DBUS_TYPE_STRING)) { + entry.type == DBUS_TYPE_STRING) { + os_free(confname); confname = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (confname == NULL) goto error; } else if (!strcmp(entry.key, "bridge-ifname") && - (entry.type == DBUS_TYPE_STRING)) { + entry.type == DBUS_TYPE_STRING) { + os_free(bridge_ifname); bridge_ifname = os_strdup(entry.str_value); wpa_dbus_dict_entry_clear(&entry); if (bridge_ifname == NULL) @@ -151,13 +151,13 @@ DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message, * an error if we already control it. */ if (wpa_supplicant_get_iface(global, ifname) != NULL) { - reply = dbus_message_new_error(message, - WPAS_ERROR_EXISTS_ERROR, - "wpa_supplicant already " - "controls this interface."); + reply = dbus_message_new_error( + message, WPAS_ERROR_EXISTS_ERROR, + "wpa_supplicant already controls this interface."); } else { struct wpa_supplicant *wpa_s; struct wpa_interface iface; + os_memset(&iface, 0, sizeof(iface)); iface.ifname = ifname; iface.driver = driver; @@ -165,17 +165,17 @@ DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message, iface.confname = confname; iface.bridge_ifname = bridge_ifname; /* Otherwise, have wpa_supplicant attach to it. */ - if ((wpa_s = wpa_supplicant_add_iface(global, &iface))) { + wpa_s = wpa_supplicant_add_iface(global, &iface, NULL); + if (wpa_s) { const char *path = wpa_s->dbus_path; + reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, - &path, DBUS_TYPE_INVALID); + &path, DBUS_TYPE_INVALID); } else { - reply = dbus_message_new_error(message, - WPAS_ERROR_ADD_ERROR, - "wpa_supplicant " - "couldn't grab this " - "interface."); + reply = dbus_message_new_error( + message, WPAS_ERROR_ADD_ERROR, + "wpa_supplicant couldn't grab this interface."); } } @@ -226,10 +226,9 @@ DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message, if (!wpa_supplicant_remove_iface(global, wpa_s, 0)) { reply = wpas_dbus_new_success_reply(message); } else { - reply = dbus_message_new_error(message, - WPAS_ERROR_REMOVE_ERROR, - "wpa_supplicant couldn't " - "remove this interface."); + reply = dbus_message_new_error( + message, WPAS_ERROR_REMOVE_ERROR, + "wpa_supplicant couldn't remove this interface."); } out: @@ -256,8 +255,8 @@ DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message, struct wpa_supplicant *wpa_s; if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_STRING, &ifname, - DBUS_TYPE_INVALID)) { + DBUS_TYPE_STRING, &ifname, + DBUS_TYPE_INVALID)) { reply = wpas_dbus_new_invalid_opts_error(message, NULL); goto out; } @@ -271,8 +270,8 @@ DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message, path = wpa_s->dbus_path; reply = dbus_message_new_method_return(message); dbus_message_append_args(reply, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID); + DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); out: return reply; @@ -298,10 +297,10 @@ DBusMessage * wpas_dbus_global_set_debugparams(DBusMessage *message, dbus_bool_t debug_show_keys; if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_INT32, &debug_level, - DBUS_TYPE_BOOLEAN, &debug_timestamp, - DBUS_TYPE_BOOLEAN, &debug_show_keys, - DBUS_TYPE_INVALID)) { + DBUS_TYPE_INT32, &debug_level, + DBUS_TYPE_BOOLEAN, &debug_timestamp, + DBUS_TYPE_BOOLEAN, &debug_show_keys, + DBUS_TYPE_INVALID)) { return wpas_dbus_new_invalid_opts_error(message, NULL); } @@ -350,7 +349,7 @@ DBusMessage * wpas_dbus_iface_scan(DBusMessage *message, DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message, struct wpa_supplicant *wpa_s) { - DBusMessage *reply = NULL; + DBusMessage *reply; DBusMessageIter iter; DBusMessageIter sub_iter; struct wpa_bss *bss; @@ -358,9 +357,10 @@ DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message, /* Create and initialize the return message */ reply = dbus_message_new_method_return(message); dbus_message_iter_init_append(reply, &iter); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, - DBUS_TYPE_OBJECT_PATH_AS_STRING, - &sub_iter); + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_OBJECT_PATH_AS_STRING, + &sub_iter)) + goto error; /* 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) { @@ -374,13 +374,21 @@ DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message, "%s/" WPAS_DBUS_BSSIDS_PART "/" WPAS_DBUS_BSSID_FORMAT, wpa_s->dbus_path, MAC2STR(bss->bssid)); - dbus_message_iter_append_basic(&sub_iter, - DBUS_TYPE_OBJECT_PATH, &path); + if (!dbus_message_iter_append_basic(&sub_iter, + DBUS_TYPE_OBJECT_PATH, + &path)) + goto error; } - dbus_message_iter_close_container(&iter, &sub_iter); + if (!dbus_message_iter_close_container(&iter, &sub_iter)) + goto error; return reply; + +error: + dbus_message_unref(reply); + return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR, + "an internal error occurred returning scan results"); } @@ -400,84 +408,56 @@ DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message, { DBusMessage *reply; DBusMessageIter iter, iter_dict; - const u8 *ie; + const u8 *wpa_ie, *rsn_ie, *wps_ie; /* Dump the properties into a dbus message */ reply = dbus_message_new_method_return(message); - dbus_message_iter_init_append(reply, &iter); - if (!wpa_dbus_dict_open_write(&iter, &iter_dict)) - goto error; + wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); + rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); + wps_ie = wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE); - if (!wpa_dbus_dict_append_byte_array(&iter_dict, "bssid", + dbus_message_iter_init_append(reply, &iter); + if (!wpa_dbus_dict_open_write(&iter, &iter_dict) || + !wpa_dbus_dict_append_byte_array(&iter_dict, "bssid", (const char *) bss->bssid, - ETH_ALEN)) - goto error; - - ie = wpa_bss_get_ie(bss, WLAN_EID_SSID); - if (ie) { - if (!wpa_dbus_dict_append_byte_array(&iter_dict, "ssid", - (const char *) (ie + 2), - ie[1])) - goto error; - } - - ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); - if (ie) { - if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpaie", - (const char *) ie, - ie[1] + 2)) - goto error; - } - - ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); - if (ie) { - if (!wpa_dbus_dict_append_byte_array(&iter_dict, "rsnie", - (const char *) ie, - ie[1] + 2)) - goto error; - } - - ie = wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE); - if (ie) { - if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpsie", - (const char *) ie, - ie[1] + 2)) - goto error; - } - - if (bss->freq) { - if (!wpa_dbus_dict_append_int32(&iter_dict, "frequency", - bss->freq)) - goto error; + ETH_ALEN) || + !wpa_dbus_dict_append_byte_array(&iter_dict, "ssid", + (const char *) bss->ssid, + bss->ssid_len) || + (wpa_ie && + !wpa_dbus_dict_append_byte_array(&iter_dict, "wpaie", + (const char *) wpa_ie, + wpa_ie[1] + 2)) || + (rsn_ie && + !wpa_dbus_dict_append_byte_array(&iter_dict, "rsnie", + (const char *) rsn_ie, + rsn_ie[1] + 2)) || + (wps_ie && + !wpa_dbus_dict_append_byte_array(&iter_dict, "wpsie", + (const char *) wps_ie, + wps_ie[1] + 2)) || + (bss->freq && + !wpa_dbus_dict_append_int32(&iter_dict, "frequency", bss->freq)) || + !wpa_dbus_dict_append_uint16(&iter_dict, "capabilities", + bss->caps) || + (!(bss->flags & WPA_BSS_QUAL_INVALID) && + !wpa_dbus_dict_append_int32(&iter_dict, "quality", bss->qual)) || + (!(bss->flags & WPA_BSS_NOISE_INVALID) && + !wpa_dbus_dict_append_int32(&iter_dict, "noise", bss->noise)) || + (!(bss->flags & WPA_BSS_LEVEL_INVALID) && + !wpa_dbus_dict_append_int32(&iter_dict, "level", bss->level)) || + !wpa_dbus_dict_append_int32(&iter_dict, "maxrate", + wpa_bss_get_max_rate(bss) * 500000) || + !wpa_dbus_dict_close_write(&iter, &iter_dict)) { + if (reply) + dbus_message_unref(reply); + reply = dbus_message_new_error( + message, WPAS_ERROR_INTERNAL_ERROR, + "an internal error occurred returning BSSID properties."); } - if (!wpa_dbus_dict_append_uint16(&iter_dict, "capabilities", - bss->caps)) - goto error; - if (!(bss->flags & WPA_BSS_QUAL_INVALID) && - !wpa_dbus_dict_append_int32(&iter_dict, "quality", bss->qual)) - goto error; - if (!(bss->flags & WPA_BSS_NOISE_INVALID) && - !wpa_dbus_dict_append_int32(&iter_dict, "noise", bss->noise)) - goto error; - if (!(bss->flags & WPA_BSS_LEVEL_INVALID) && - !wpa_dbus_dict_append_int32(&iter_dict, "level", bss->level)) - goto error; - if (!wpa_dbus_dict_append_int32(&iter_dict, "maxrate", - wpa_bss_get_max_rate(bss) * 500000)) - goto error; - - if (!wpa_dbus_dict_close_write(&iter, &iter_dict)) - goto error; return reply; - -error: - if (reply) - dbus_message_unref(reply); - return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR, - "an internal error occurred returning " - "BSSID properties."); } @@ -537,37 +517,27 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, if (res < 0) { if (!strict) { const char *args[] = {"CCMP", "TKIP", "NONE"}; + if (!wpa_dbus_dict_append_string_array( &iter_dict, "pairwise", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto error; } } else { if (!wpa_dbus_dict_begin_string_array(&iter_dict, "pairwise", &iter_dict_entry, &iter_dict_val, - &iter_array)) - goto error; - - if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "CCMP")) - goto error; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "TKIP")) - goto error; - } - - if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "NONE")) - goto error; - } - - if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_array) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "CCMP")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "TKIP")) || + ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "NONE")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -580,9 +550,10 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, const char *args[] = { "CCMP", "TKIP", "WEP104", "WEP40" }; + if (!wpa_dbus_dict_append_string_array( &iter_dict, "group", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto error; } } else { @@ -592,31 +563,19 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, &iter_array)) goto error; - if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "CCMP")) - goto error; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "TKIP")) - goto error; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "WEP104")) - goto error; - } - - if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "WEP40")) - goto error; - } - - if (!wpa_dbus_dict_end_string_array(&iter_dict, + if (((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "CCMP")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "TKIP")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "WEP104")) || + ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "WEP40")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -632,45 +591,30 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, }; if (!wpa_dbus_dict_append_string_array( &iter_dict, "key_mgmt", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto error; } } else { if (!wpa_dbus_dict_begin_string_array(&iter_dict, "key_mgmt", &iter_dict_entry, &iter_dict_val, - &iter_array)) - goto error; - - if (!wpa_dbus_dict_string_array_add_element(&iter_array, - "NONE")) - goto error; - - if (!wpa_dbus_dict_string_array_add_element(&iter_array, - "IEEE8021X")) - goto error; - - if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "WPA-EAP")) - goto error; - } - - if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "WPA-PSK")) - goto error; - } - - if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "WPA-NONE")) - goto error; - } - - if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_array) || + !wpa_dbus_dict_string_array_add_element(&iter_array, + "NONE") || + !wpa_dbus_dict_string_array_add_element(&iter_array, + "IEEE8021X") || + ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "WPA-EAP")) || + ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "WPA-PSK")) || + ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "WPA-NONE")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -681,33 +625,26 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, if (res < 0) { if (!strict) { const char *args[] = { "RSN", "WPA" }; + if (!wpa_dbus_dict_append_string_array( &iter_dict, "proto", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto error; } } else { if (!wpa_dbus_dict_begin_string_array(&iter_dict, "proto", &iter_dict_entry, &iter_dict_val, - &iter_array)) - goto error; - - if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | - WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "RSN")) - goto error; - } - - if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | - WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "WPA")) - goto error; - } - - if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_array) || + ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "RSN")) || + ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "WPA")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -718,37 +655,27 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, if (res < 0) { if (!strict) { const char *args[] = { "OPEN", "SHARED", "LEAP" }; + if (!wpa_dbus_dict_append_string_array( &iter_dict, "auth_alg", args, - sizeof(args) / sizeof(char*))) + ARRAY_SIZE(args))) goto error; } } else { if (!wpa_dbus_dict_begin_string_array(&iter_dict, "auth_alg", &iter_dict_entry, &iter_dict_val, - &iter_array)) - goto error; - - if (capa.auth & (WPA_DRIVER_AUTH_OPEN)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "OPEN")) - goto error; - } - - if (capa.auth & (WPA_DRIVER_AUTH_SHARED)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "SHARED")) - goto error; - } - - if (capa.auth & (WPA_DRIVER_AUTH_LEAP)) { - if (!wpa_dbus_dict_string_array_add_element( - &iter_array, "LEAP")) - goto error; - } - - if (!wpa_dbus_dict_end_string_array(&iter_dict, + &iter_array) || + ((capa.auth & WPA_DRIVER_AUTH_OPEN) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "OPEN")) || + ((capa.auth & WPA_DRIVER_AUTH_SHARED) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "SHARED")) || + ((capa.auth & WPA_DRIVER_AUTH_LEAP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "LEAP")) || + !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, &iter_array)) @@ -763,9 +690,9 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message, error: if (reply) dbus_message_unref(reply); - return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR, - "an internal error occurred returning " - "interface capabilities."); + return dbus_message_new_error( + message, WPAS_ERROR_INTERNAL_ERROR, + "an internal error occurred returning interface capabilities."); } @@ -786,10 +713,9 @@ DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message, ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) { - reply = dbus_message_new_error(message, - WPAS_ERROR_ADD_NETWORK_ERROR, - "wpa_supplicant could not add " - "a network on this interface."); + reply = dbus_message_new_error( + message, WPAS_ERROR_ADD_NETWORK_ERROR, + "wpa_supplicant could not add a network on this interface."); goto out; } wpas_notify_network_added(wpa_s, ssid); @@ -829,15 +755,15 @@ DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, struct wpa_ssid *ssid; if (!dbus_message_get_args(message, NULL, - DBUS_TYPE_OBJECT_PATH, &op, - DBUS_TYPE_INVALID)) { + DBUS_TYPE_OBJECT_PATH, &op, + DBUS_TYPE_INVALID)) { reply = wpas_dbus_new_invalid_opts_error(message, NULL); goto out; } /* Extract the network ID */ iface = wpas_dbus_decompose_object_path(op, &net_id, NULL); - if (iface == NULL) { + if (iface == NULL || net_id == NULL) { reply = wpas_dbus_new_invalid_network_error(message); goto out; } @@ -857,17 +783,17 @@ DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message, wpas_notify_network_removed(wpa_s, ssid); + if (ssid == wpa_s->current_ssid) + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + if (wpa_config_remove_network(wpa_s->conf, id) < 0) { - reply = dbus_message_new_error(message, - WPAS_ERROR_REMOVE_NETWORK_ERROR, - "error removing the specified " - "on this interface."); + reply = dbus_message_new_error( + message, WPAS_ERROR_REMOVE_NETWORK_ERROR, + "error removing the specified on this interface."); goto out; } - if (ssid == wpa_s->current_ssid) - wpa_supplicant_deauthenticate(wpa_s, - WLAN_REASON_DEAUTH_LEAVING); reply = wpas_dbus_new_success_reply(message); out: @@ -877,7 +803,7 @@ out: } -static const char *dont_quote[] = { +static const char const *dont_quote[] = { "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap", "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path", "bssid", NULL @@ -887,8 +813,9 @@ static const char *dont_quote[] = { static dbus_bool_t should_quote_opt(const char *key) { int i = 0; + while (dont_quote[i] != NULL) { - if (strcmp(key, dont_quote[i]) == 0) + if (os_strcmp(key, dont_quote[i]) == 0) return FALSE; i++; } @@ -959,7 +886,7 @@ DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message, goto error; ret = os_snprintf(value, size, "\"%s\"", entry.str_value); - if (ret < 0 || (size_t) ret != (size - 1)) + if (os_snprintf_error(size, ret)) goto error; } else { value = os_strdup(entry.str_value); @@ -972,7 +899,7 @@ DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message, goto error; ret = os_snprintf(value, size, "%u", entry.uint32_value); - if (ret <= 0) + if (os_snprintf_error(size, ret)) goto error; } else if (entry.type == DBUS_TYPE_INT32) { value = os_zalloc(size); @@ -980,7 +907,7 @@ DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message, goto error; ret = os_snprintf(value, size, "%d", entry.int32_value); - if (ret <= 0) + if (os_snprintf_error(size, ret)) goto error; } else goto error; @@ -1093,7 +1020,8 @@ DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message, goto out; } /* Ensure the object path really points to this interface */ - if (os_strcmp(iface_obj_path, wpa_s->dbus_path) != 0) { + if (network == NULL || + os_strcmp(iface_obj_path, wpa_s->dbus_path) != 0) { reply = wpas_dbus_new_invalid_network_error(message); goto out; } @@ -1203,25 +1131,30 @@ DBusMessage * wpas_dbus_iface_set_smartcard_modules( if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) goto error; if (!strcmp(entry.key, "opensc_engine_path") && - (entry.type == DBUS_TYPE_STRING)) { + entry.type == DBUS_TYPE_STRING) { + os_free(opensc_engine_path); opensc_engine_path = os_strdup(entry.str_value); + wpa_dbus_dict_entry_clear(&entry); if (opensc_engine_path == NULL) goto error; } else if (!strcmp(entry.key, "pkcs11_engine_path") && - (entry.type == DBUS_TYPE_STRING)) { + entry.type == DBUS_TYPE_STRING) { + os_free(pkcs11_engine_path); pkcs11_engine_path = os_strdup(entry.str_value); + wpa_dbus_dict_entry_clear(&entry); if (pkcs11_engine_path == NULL) goto error; } else if (!strcmp(entry.key, "pkcs11_module_path") && - (entry.type == DBUS_TYPE_STRING)) { + entry.type == DBUS_TYPE_STRING) { + os_free(pkcs11_module_path); pkcs11_module_path = os_strdup(entry.str_value); + wpa_dbus_dict_entry_clear(&entry); if (pkcs11_module_path == NULL) goto error; } else { wpa_dbus_dict_entry_clear(&entry); goto error; } - wpa_dbus_dict_entry_clear(&entry); } os_free(wpa_s->conf->opensc_engine_path); @@ -1292,14 +1225,16 @@ DBusMessage * wpas_dbus_iface_get_scanning(DBusMessage *message, dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &scanning, DBUS_TYPE_INVALID); } else { - wpa_printf(MSG_ERROR, "dbus: Not enough memory to return " - "scanning state"); + wpa_printf(MSG_ERROR, + "dbus: Not enough memory to return scanning state"); } return reply; } +#ifndef CONFIG_NO_CONFIG_BLOBS + /** * wpas_dbus_iface_set_blobs - Store named binary blobs (ie, for certificates) * @message: Pointer to incoming dbus message @@ -1364,7 +1299,7 @@ DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message, blob->len = entry.array_len; os_memcpy(blob->data, (u8 *) entry.bytearray_value, entry.array_len); - if (blob->name == NULL || blob->data == NULL) { + if (blob->name == NULL) { wpa_config_free_blob(blob); reply = dbus_message_new_error( message, WPAS_ERROR_ADD_ERROR, @@ -1403,8 +1338,8 @@ DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message, dbus_message_iter_init(message, &iter); - if ((dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) || - (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_STRING)) + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) return wpas_dbus_new_invalid_opts_error(message, NULL); dbus_message_iter_recurse(&iter, &array); @@ -1414,8 +1349,7 @@ DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message, dbus_message_iter_get_basic(&array, &name); if (!os_strlen(name)) err_msg = "Invalid blob name."; - - if (wpa_config_remove_blob(wpa_s->conf, name) != 0) + else if (wpa_config_remove_blob(wpa_s->conf, name) != 0) err_msg = "Error removing blob."; else wpas_notify_blob_removed(wpa_s, name); @@ -1429,6 +1363,8 @@ DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message, return wpas_dbus_new_success_reply(message); } +#endif /* CONFIG_NO_CONFIG_BLOBS */ + /** * wpas_dbus_iface_flush - Clear BSS of old or all inactive entries diff --git a/wpa_supplicant/dbus/dbus_old_handlers.h b/wpa_supplicant/dbus/dbus_old_handlers.h index 825bc6d2c1b96..e60ad06a032e1 100644 --- a/wpa_supplicant/dbus/dbus_old_handlers.h +++ b/wpa_supplicant/dbus/dbus_old_handlers.h @@ -58,13 +58,13 @@ DBusMessage * wpas_dbus_iface_disable_network(DBusMessage *message, struct wpa_ssid *ssid); DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message, - struct wpa_supplicant *wpa_s); + struct wpa_supplicant *wpa_s); DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message, struct wpa_supplicant *wpa_s); DBusMessage * wpas_dbus_iface_set_ap_scan(DBusMessage *message, - struct wpa_supplicant *wpa_s); + struct wpa_supplicant *wpa_s); DBusMessage * wpas_dbus_iface_set_smartcard_modules( DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -76,7 +76,7 @@ DBusMessage * wpas_dbus_iface_get_scanning(DBusMessage *message, struct wpa_supplicant *wpa_s); DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message, - struct wpa_supplicant *wpa_s); + struct wpa_supplicant *wpa_s); DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message, struct wpa_supplicant *wpa_s); diff --git a/wpa_supplicant/dbus/dbus_old_handlers_wps.c b/wpa_supplicant/dbus/dbus_old_handlers_wps.c index bb793824653c9..5309a5301fc24 100644 --- a/wpa_supplicant/dbus/dbus_old_handlers_wps.c +++ b/wpa_supplicant/dbus/dbus_old_handlers_wps.c @@ -36,7 +36,7 @@ DBusMessage * wpas_dbus_iface_wps_pbc(DBusMessage *message, DBUS_TYPE_INVALID)) return wpas_dbus_new_invalid_opts_error(message, NULL); - if (!os_strcmp(arg_bssid, "any")) + if (os_strcmp(arg_bssid, "any") == 0) ret = wpas_wps_start_pbc(wpa_s, NULL, 0); else if (!hwaddr_aton(arg_bssid, bssid)) ret = wpas_wps_start_pbc(wpa_s, bssid, 0); @@ -46,10 +46,9 @@ DBusMessage * wpas_dbus_iface_wps_pbc(DBusMessage *message, } if (ret < 0) { - return dbus_message_new_error(message, - WPAS_ERROR_WPS_PBC_ERROR, - "Could not start PBC " - "negotiation"); + return dbus_message_new_error( + message, WPAS_ERROR_WPS_PBC_ERROR, + "Could not start PBC negotiation"); } return wpas_dbus_new_success_reply(message); @@ -73,12 +72,13 @@ DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message, char *pin = NULL; u8 bssid[ETH_ALEN], *_bssid = NULL; int ret = 0; + char npin[9]; if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg_bssid, DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID)) return wpas_dbus_new_invalid_opts_error(message, NULL); - if (!os_strcmp(arg_bssid, "any")) + if (os_strcmp(arg_bssid, "any") == 0) _bssid = NULL; else if (!hwaddr_aton(arg_bssid, bssid)) _bssid = bssid; @@ -104,15 +104,12 @@ DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message, if (reply == NULL) return NULL; - if (ret == 0) { - dbus_message_append_args(reply, DBUS_TYPE_STRING, &pin, - DBUS_TYPE_INVALID); - } else { - char npin[9]; + if (ret > 0) { os_snprintf(npin, sizeof(npin), "%08d", ret); - dbus_message_append_args(reply, DBUS_TYPE_STRING, &npin, - DBUS_TYPE_INVALID); + pin = npin; } + dbus_message_append_args(reply, DBUS_TYPE_STRING, &pin, + DBUS_TYPE_INVALID); return reply; } @@ -138,9 +135,7 @@ DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message, DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID)) return wpas_dbus_new_invalid_opts_error(message, NULL); - if (!os_strcmp(arg_bssid, "any")) - ret = wpas_wps_start_reg(wpa_s, NULL, pin, NULL); - else if (!hwaddr_aton(arg_bssid, bssid)) + if (!hwaddr_aton(arg_bssid, bssid)) ret = wpas_wps_start_reg(wpa_s, bssid, pin, NULL); else { return wpas_dbus_new_invalid_opts_error(message, @@ -149,7 +144,7 @@ DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message, if (ret < 0) { return dbus_message_new_error(message, - WPAS_ERROR_WPS_PBC_ERROR, + WPAS_ERROR_WPS_REG_ERROR, "Could not request credentials"); } diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index 711b4073a7256..7f627fdd6146f 100644 --- a/wpa_supplicant/defconfig +++ b/wpa_supplicant/defconfig @@ -20,63 +20,6 @@ # used to fix build issues on such systems (krb5.h not found). #CFLAGS += -I/usr/include/kerberos -# Example configuration for various cross-compilation platforms - -#### sveasoft (e.g., for Linksys WRT54G) ###################################### -#CC=mipsel-uclibc-gcc -#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc -#CFLAGS += -Os -#CPPFLAGS += -I../src/include -I../../src/router/openssl/include -#LIBS += -L/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib -lssl -############################################################################### - -#### openwrt (e.g., for Linksys WRT54G) ####################################### -#CC=mipsel-uclibc-gcc -#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc -#CFLAGS += -Os -#CPPFLAGS=-I../src/include -I../openssl-0.9.7d/include \ -# -I../WRT54GS/release/src/include -#LIBS = -lssl -############################################################################### - - -# Driver interface for Host AP driver -CONFIG_DRIVER_HOSTAP=y - -# Driver interface for Agere driver -#CONFIG_DRIVER_HERMES=y -# Change include directories to match with the local setup -#CFLAGS += -I../../hcf -I../../include -I../../include/hcf -#CFLAGS += -I../../include/wireless - -# Driver interface for madwifi driver -# Deprecated; use CONFIG_DRIVER_WEXT=y instead. -#CONFIG_DRIVER_MADWIFI=y -# Set include directory to the madwifi source tree -#CFLAGS += -I../../madwifi - -# Driver interface for ndiswrapper -# Deprecated; use CONFIG_DRIVER_WEXT=y instead. -#CONFIG_DRIVER_NDISWRAPPER=y - -# Driver interface for Atmel driver -CONFIG_DRIVER_ATMEL=y - -# Driver interface for old Broadcom driver -# Please note that the newer Broadcom driver ("hybrid Linux driver") supports -# Linux wireless extensions and does not need (or even work) with the old -# driver wrapper. Use CONFIG_DRIVER_WEXT=y with that driver. -#CONFIG_DRIVER_BROADCOM=y -# Example path for wlioctl.h; change to match your configuration -#CFLAGS += -I/opt/WRT54GS/release/src/include - -# Driver interface for Intel ipw2100/2200 driver -# Deprecated; use CONFIG_DRIVER_WEXT=y instead. -#CONFIG_DRIVER_IPW=y - -# Driver interface for Ralink driver -#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 @@ -88,6 +31,19 @@ CONFIG_DRIVER_WEXT=y # Driver interface for Linux drivers using the nl80211 kernel interface CONFIG_DRIVER_NL80211=y +# driver_nl80211.c requires libnl. If you are compiling it yourself +# you may need to point hostapd to your version of libnl. +# +#CFLAGS += -I$<path to libnl include files> +#LIBS += -L$<path to libnl library files> + +# Use libnl v2.0 (or 3.0) libraries. +#CONFIG_LIBNL20=y + +# Use libnl 3.2 libraries (if this is selected, CONFIG_LIBNL20 is ignored) +#CONFIG_LIBNL32=y + + # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) #CONFIG_DRIVER_BSD=y #CFLAGS += -I/usr/local/include @@ -111,9 +67,6 @@ CONFIG_DRIVER_NL80211=y # wpa_supplicant. # CONFIG_USE_NDISUIO=y -# Driver interface for development testing -#CONFIG_DRIVER_TEST=y - # Driver interface for wired Ethernet drivers CONFIG_DRIVER_WIRED=y @@ -147,10 +100,9 @@ CONFIG_EAP_PEAP=y CONFIG_EAP_TTLS=y # EAP-FAST -# Note: Default OpenSSL package does not include support for all the -# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL, -# the OpenSSL library must be patched (openssl-0.9.8d-tls-extensions.patch) -# to add the needed functions. +# Note: If OpenSSL is used as the TLS library, OpenSSL 1.0 or newer is needed +# for EAP-FAST support. Older OpenSSL releases would need to be patched, e.g., +# with openssl-0.9.8x-tls-extensions.patch, to add the needed functions. #CONFIG_EAP_FAST=y # EAP-GTC @@ -197,8 +149,6 @@ 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 @@ -210,6 +160,9 @@ CONFIG_EAP_LEAP=y # EAP-IKEv2 #CONFIG_EAP_IKEV2=y +# EAP-EKE +#CONFIG_EAP_EKE=y + # PKCS#12 (PFX) support (used to read private key and certificate file from # a file that usually has extension .p12 or .pfx) CONFIG_PKCS12=y @@ -225,14 +178,19 @@ CONFIG_SMARTCARD=y # Support HT overrides (disable HT/HT40, mask MCS rates, etc.) #CONFIG_HT_OVERRIDES=y +# Support VHT overrides (disable VHT, mask MCS rates, etc.) +#CONFIG_VHT_OVERRIDES=y + # Development testing #CONFIG_EAPOL_TEST=y # Select control interface backend for external programs, e.g, wpa_cli: # unix = UNIX domain sockets (default for Linux/*BSD) # udp = UDP sockets using localhost (127.0.0.1) +# udp6 = UDP IPv6 sockets using localhost (::1) # named_pipe = Windows Named Pipe (default for Windows) # udp-remote = UDP sockets with remote access (only for tests systems/purpose) +# udp6-remote = UDP IPv6 sockets with remote access (only for tests purpose) # y = use default (backwards compatibility) # If this option is commented out, control interface is not included in the # build. @@ -258,11 +216,6 @@ CONFIG_CTRL_IFACE=y # 35-50 kB in code size. #CONFIG_NO_WPA=y -# Remove WPA2 support. This allows WPA to be used, but removes WPA2 code to -# save about 1 kB in code size when building only WPA-Personal (no EAP support) -# or 6 kB if building for WPA-Enterprise. -#CONFIG_NO_WPA2=y - # Remove IEEE 802.11i/WPA-Personal ASCII passphrase support # This option can be used to reduce code size by removing support for # converting ASCII passphrases into PSK. If this functionality is removed, the @@ -297,7 +250,7 @@ CONFIG_BACKEND=file # main_none = Very basic example (development use only) #CONFIG_MAIN=main -# Select wrapper for operatins system and C library specific functions +# Select wrapper for operating system and C library specific functions # unix = UNIX/POSIX like systems (default) # win32 = Windows systems # none = Empty template @@ -306,12 +259,14 @@ CONFIG_BACKEND=file # Select event loop implementation # eloop = select() loop (default) # eloop_win = Windows events and WaitForMultipleObject() loop -# eloop_none = Empty template #CONFIG_ELOOP=eloop # Should we use poll instead of select? Select is used by default. #CONFIG_ELOOP_POLL=y +# Should we use epoll instead of select? Select is used by default. +#CONFIG_ELOOP_EPOLL=y + # Select layer 2 packet implementation # linux = Linux packet socket (default) # pcap = libpcap/libdnet/WinPcap @@ -420,6 +375,10 @@ CONFIG_PEERKEY=y # same file, e.g., using trace-cmd. #CONFIG_DEBUG_LINUX_TRACING=y +# Add support for writing debug log to Android logcat instead of standard +# output +#CONFIG_ANDROID_LOG=y + # Enable privilege separation (see README 'Privilege separation' for details) #CONFIG_PRIVSEP=y @@ -479,6 +438,10 @@ CONFIG_PEERKEY=y # IEEE 802.11n (High Throughput) support (mainly for AP mode) #CONFIG_IEEE80211N=y +# IEEE 802.11ac (Very High Throughput) support (mainly for AP mode) +# (depends on CONFIG_IEEE80211N) +#CONFIG_IEEE80211AC=y + # Wireless Network Management (IEEE Std 802.11v-2011) # Note: This is experimental and not complete implementation. #CONFIG_WNM=y @@ -492,6 +455,9 @@ CONFIG_PEERKEY=y # Hotspot 2.0 #CONFIG_HS20=y +# Disable roaming in wpa_supplicant +#CONFIG_NO_ROAMING=y + # AP mode operations with wpa_supplicant # This can be used for controlling AP mode operations with wpa_supplicant. It # should be noted that this is mainly aimed at simple cases like @@ -504,9 +470,17 @@ CONFIG_PEERKEY=y # more information on P2P operations. #CONFIG_P2P=y +# Enable TDLS support +#CONFIG_TDLS=y + +# Wi-Fi Direct +# This can be used to enable Wi-Fi Direct extensions for P2P using an external +# program to control the additional information exchanges in the messages. +#CONFIG_WIFI_DISPLAY=y + # Autoscan # This can be used to enable automatic scan support in wpa_supplicant. -# See wpa_supplicant.conf for more information on autoscan usage. +# See wpa_supplicant.conf for more information on autoscan usage. # # Enabling directly a module will enable autoscan support. # For exponential module: diff --git a/wpa_supplicant/doc/docbook/Makefile b/wpa_supplicant/doc/docbook/Makefile index aaeee2eee4533..82f9de315dc55 100644 --- a/wpa_supplicant/doc/docbook/Makefile +++ b/wpa_supplicant/doc/docbook/Makefile @@ -7,6 +7,7 @@ FILES += wpa_passphrase FILES += wpa_priv FILES += wpa_supplicant.conf FILES += wpa_supplicant +FILES += eapol_test man: for i in $(FILES); do docbook2man $$i.sgml; done @@ -20,7 +21,7 @@ pdf: clean: - rm -f wpa_background.8 wpa_cli.8 wpa_gui.8 wpa_passphrase.8 wpa_priv.8 wpa_supplicant.8 + rm -f wpa_background.8 wpa_cli.8 wpa_gui.8 wpa_passphrase.8 wpa_priv.8 wpa_supplicant.8 eapol_test.8 rm -f wpa_supplicant.conf.5 rm -f manpage.links manpage.refs rm -f $(FILES:%=%.pdf) diff --git a/wpa_supplicant/doc/docbook/eapol_test.8 b/wpa_supplicant/doc/docbook/eapol_test.8 new file mode 100644 index 0000000000000..d06727c0b406f --- /dev/null +++ b/wpa_supplicant/doc/docbook/eapol_test.8 @@ -0,0 +1,124 @@ +.\" This manpage has been automatically generated by docbook2man +.\" from a DocBook document. This tool can be found at: +.\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> +.\" Please send any bug reports, improvements, comments, patches, +.\" etc. to Steve Cheng <steve@ggi-project.org>. +.TH "EAPOL_TEST" "8" "15 March 2015" "" "" + +.SH NAME +eapol_test \- EAP peer and RADIUS client testing +.SH SYNOPSIS + +\fBeapol_test\fR [ \fB-nWS\fR ] [ \fB-c\fIconfig file\fB\fR ] [ \fB-a\fIserver IP address\fB\fR ] [ \fB-A\fIclient IP address\fB\fR ] [ \fB-p\fIUDP port\fB\fR ] [ \fB-s\fIshared secret\fB\fR ] [ \fB-r\fIre-authentications\fB\fR ] [ \fB-t\fItimeout\fB\fR ] [ \fB-C\fIConnect-Info\fB\fR ] [ \fB-M\fIMAC address\fB\fR ] [ \fB-o\fIfile\fB\fR ] [ \fB-N\fIattr spec\fB\fR ] + + +\fBeapol_test scard\fR + + +\fBeapol_test sim\fR [ \fBPIN\fR ] [ \fBnum triplets\fR ] + +.SH "OVERVIEW" +.PP +eapol_test is a program that links together the same EAP +peer implementation that wpa_supplicant is using and the RADIUS +authentication client code from hostapd. In addition, it has +minimal glue code to combine these two components in similar +ways to IEEE 802.1X/EAPOL Authenticator state machines. In other +words, it integrates IEEE 802.1X Authenticator (normally, an +access point) and IEEE 802.1X Supplicant (normally, a wireless +client) together to generate a single program that can be used to +test EAP methods without having to setup an access point and a +wireless client. +.PP +The main uses for eapol_test are in interoperability testing +of EAP methods against RADIUS servers and in development testing +for new EAP methods. It can be easily used to automate EAP testing +for interoperability and regression since the program can be run +from shell scripts without require additional test components apart +from a RADIUS server. For example, the automated EAP tests described +in eap_testing.txt are implemented with eapol_test. Similarly, +eapol_test could be used to implement an automated regression +test suite for a RADIUS authentication server. +.PP +As an example: +.sp +.RS + +.nf +eapol_test -ctest.conf -a127.0.0.1 -p1812 -ssecret -r1 +.fi +.RE +.PP +tries to complete EAP authentication based on the network +configuration from test.conf against the RADIUS server running +on the local host. A re-authentication is triggered to test fast +re-authentication. The configuration file uses the same format for +network blocks as wpa_supplicant. +.SH "COMMAND ARGUMENTS" +.TP +\fB-c configuration file path\fR +A configuration to use. The configuration should +use the same format for network blocks as wpa_supplicant. +.TP +\fB-a AS address\fR +IP address of the authentication server. The +default is '127.0.0.1'. +.TP +\fB-A client address\fR +IP address of the client. The default is to +select an address automatically. +.TP +\fB-p AS port\fR +UDP port of the authentication server. The +default is '1812'. +.TP +\fB-s AS secret\fR +Shared secret with the authentication server. +The default is 'radius'. +.TP +\fB-r count\fR +Number of reauthentications. +.TP +\fB-t timeout\fR +Timeout in seconds. The default is 30. +.TP +\fB-C info\fR +RADIUS Connect-Info. The default is +\&'CONNECT 11Mbps 802.11b'. +.TP +\fB-M mac address\fR +Client MAC address (Calling-Station-Id). The +default is '02:00:00:00:00:01'. +.TP +\fB-o file\fR +Location to write out server certificate. +.TP +\fB-N attr spec\fR +Send arbitrary attribute specific by +attr_id:syntax:value, or attr_id alone. attr_id should be the numeric +ID of the attribute, and syntax should be one of 's' (string), +\&'d' (integer), or 'x' (octet string). The value is the attribute value +to send. When attr_id is given alone, NULL is used as the attribute +value. Multiple attributes can be specified by using the option +several times. +.TP +\fB-n\fR +Indicates that no MPPE keys are expected. +.TP +\fB-W\fR +Wait for a control interface monitor before starting. +.TP +\fB-S\fR +Save configuration after authentication. +.SH "SEE ALSO" +.PP +\fBwpa_supplicant\fR(8) +.SH "LEGAL" +.PP +wpa_supplicant is copyright (c) 2003-2015, +Jouni Malinen <j@w1.fi> and +contributors. +All Rights Reserved. +.PP +This program is licensed under the BSD license (the one with +advertisement clause removed). diff --git a/wpa_supplicant/doc/docbook/eapol_test.sgml b/wpa_supplicant/doc/docbook/eapol_test.sgml new file mode 100644 index 0000000000000..e9af6d9617dfd --- /dev/null +++ b/wpa_supplicant/doc/docbook/eapol_test.sgml @@ -0,0 +1,205 @@ +<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN"> + +<refentry> + <refmeta> + <refentrytitle>eapol_test</refentrytitle> + <manvolnum>8</manvolnum> + </refmeta> + <refnamediv> + <refname>eapol_test</refname> + + <refpurpose>EAP peer and RADIUS client testing</refpurpose> + </refnamediv> + + <refsynopsisdiv> + <cmdsynopsis> + <command>eapol_test</command> + <arg>-nWS</arg> + <arg>-c<replaceable>config file</replaceable></arg> + <arg>-a<replaceable>server IP address</replaceable></arg> + <arg>-A<replaceable>client IP address</replaceable></arg> + <arg>-p<replaceable>UDP port</replaceable></arg> + <arg>-s<replaceable>shared secret</replaceable></arg> + <arg>-r<replaceable>re-authentications</replaceable></arg> + <arg>-t<replaceable>timeout</replaceable></arg> + <arg>-C<replaceable>Connect-Info</replaceable></arg> + <arg>-M<replaceable>MAC address</replaceable></arg> + <arg>-o<replaceable>file</replaceable></arg> + <arg>-N<replaceable>attr spec</replaceable></arg> + </cmdsynopsis> + <cmdsynopsis> + <command>eapol_test scard</command> + </cmdsynopsis> + <cmdsynopsis> + <command>eapol_test sim</command> + <arg>PIN</arg> + <arg>num triplets</arg> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1> + <title>Overview</title> + + <para>eapol_test is a program that links together the same EAP + peer implementation that wpa_supplicant is using and the RADIUS + authentication client code from hostapd. In addition, it has + minimal glue code to combine these two components in similar + ways to IEEE 802.1X/EAPOL Authenticator state machines. In other + words, it integrates IEEE 802.1X Authenticator (normally, an + access point) and IEEE 802.1X Supplicant (normally, a wireless + client) together to generate a single program that can be used to + test EAP methods without having to setup an access point and a + wireless client.</para> + + <para>The main uses for eapol_test are in interoperability testing + of EAP methods against RADIUS servers and in development testing + for new EAP methods. It can be easily used to automate EAP testing + for interoperability and regression since the program can be run + from shell scripts without require additional test components apart + from a RADIUS server. For example, the automated EAP tests described + in eap_testing.txt are implemented with eapol_test. Similarly, + eapol_test could be used to implement an automated regression + test suite for a RADIUS authentication server.</para> + + + <para>As an example:</para> + +<blockquote><programlisting> +eapol_test -ctest.conf -a127.0.0.1 -p1812 -ssecret -r1 +</programlisting></blockquote> + + <para>tries to complete EAP authentication based on the network + configuration from test.conf against the RADIUS server running + on the local host. A re-authentication is triggered to test fast + re-authentication. The configuration file uses the same format for + network blocks as wpa_supplicant.</para> + + </refsect1> + <refsect1> + <title>Command Arguments</title> + <variablelist> + <varlistentry> + <term>-c configuration file path</term> + + <listitem><para>A configuration to use. The configuration should + use the same format for network blocks as wpa_supplicant. + </para></listitem> + </varlistentry> + + <varlistentry> + <term>-a AS address</term> + + <listitem><para>IP address of the authentication server. The + default is '127.0.0.1'.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-A client address</term> + + <listitem><para>IP address of the client. The default is to + select an address automatically.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-p AS port</term> + + <listitem><para>UDP port of the authentication server. The + default is '1812'.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-s AS secret</term> + + <listitem><para>Shared secret with the authentication server. + The default is 'radius'.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-r count</term> + + <listitem><para>Number of reauthentications.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-t timeout</term> + + <listitem><para>Timeout in seconds. The default is 30.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-C info</term> + + <listitem><para>RADIUS Connect-Info. The default is + 'CONNECT 11Mbps 802.11b'.</para></listitem> + </varlistentry> + + + <varlistentry> + <term>-M mac address</term> + + <listitem><para>Client MAC address (Calling-Station-Id). The + default is '02:00:00:00:00:01'.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-o file</term> + + <listitem><para>Location to write out server certificate. + </para></listitem> + </varlistentry> + + <varlistentry> + <term>-N attr spec</term> + + <listitem><para>Send arbitrary attribute specific by + attr_id:syntax:value, or attr_id alone. attr_id should be the numeric + ID of the attribute, and syntax should be one of 's' (string), + 'd' (integer), or 'x' (octet string). The value is the attribute value + to send. When attr_id is given alone, NULL is used as the attribute + value. Multiple attributes can be specified by using the option + several times.</para></listitem> + </varlistentry> + + <varlistentry> + <term>-n</term> + + <listitem><para>Indicates that no MPPE keys are expected. + </para></listitem> + </varlistentry> + + <varlistentry> + <term>-W</term> + + <listitem><para>Wait for a control interface monitor before starting. + </para></listitem> + </varlistentry> + + <varlistentry> + <term>-S</term> + + <listitem><para>Save configuration after authentication. + </para></listitem> + </varlistentry> + + </variablelist> + </refsect1> + <refsect1> + <title>See Also</title> + <para> + <citerefentry> + <refentrytitle>wpa_supplicant</refentrytitle> + <manvolnum>8</manvolnum> + </citerefentry> + </para> + </refsect1> + <refsect1> + <title>Legal</title> + <para>wpa_supplicant is copyright (c) 2003-2015, + Jouni Malinen <email>j@w1.fi</email> and + contributors. + All Rights Reserved.</para> + + <para>This program is licensed under the BSD license (the one with + advertisement clause removed).</para> + </refsect1> +</refentry> diff --git a/wpa_supplicant/doc/docbook/wpa_background.8 b/wpa_supplicant/doc/docbook/wpa_background.8 index ba838cddd3dbe..d532ddc28635a 100644 --- a/wpa_supplicant/doc/docbook/wpa_background.8 +++ b/wpa_supplicant/doc/docbook/wpa_background.8 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_BACKGROUND" "8" "12 January 2013" "" "" +.TH "WPA_BACKGROUND" "8" "15 March 2015" "" "" .SH NAME wpa_background \- Background information on Wi-Fi Protected Access and IEEE 802.11i @@ -75,7 +75,7 @@ pre-authentication, and PMKSA caching). \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2012, +wpa_supplicant is copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_background.sgml b/wpa_supplicant/doc/docbook/wpa_background.sgml index eb3a0891b7cc0..afb8c3b7d78c9 100644 --- a/wpa_supplicant/doc/docbook/wpa_background.sgml +++ b/wpa_supplicant/doc/docbook/wpa_background.sgml @@ -90,7 +90,7 @@ <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2012, + <para>wpa_supplicant is copyright (c) 2003-2015, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/doc/docbook/wpa_cli.8 b/wpa_supplicant/doc/docbook/wpa_cli.8 index 886e9b015df4e..f0f1d979299fb 100644 --- a/wpa_supplicant/doc/docbook/wpa_cli.8 +++ b/wpa_supplicant/doc/docbook/wpa_cli.8 @@ -3,13 +3,13 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_CLI" "8" "12 January 2013" "" "" +.TH "WPA_CLI" "8" "15 March 2015" "" "" .SH NAME wpa_cli \- WPA command line client .SH SYNOPSIS -\fBwpa_cli\fR [ \fB-p \fIpath to ctrl sockets\fB\fR ] [ \fB-i \fIifname\fB\fR ] [ \fB-hvB\fR ] [ \fB-a \fIaction file\fB\fR ] [ \fB-P \fIpid file\fB\fR ] [ \fB\fIcommand ...\fB\fR ] +\fBwpa_cli\fR [ \fB-p \fIpath to ctrl sockets\fB\fR ] [ \fB-g \fIpath to global ctrl_interface socket\fB\fR ] [ \fB-i \fIifname\fB\fR ] [ \fB-hvB\fR ] [ \fB-a \fIaction file\fB\fR ] [ \fB-P \fIpid file\fB\fR ] [ \fB-G \fIping interval\fB\fR ] [ \fB\fIcommand ...\fB\fR ] .SH "OVERVIEW" .PP @@ -95,6 +95,11 @@ CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar Change the path where control sockets should be found. .TP +\fB-g control socket path\fR +Connect to the global control socket at the +indicated path rather than an interface-specific control +socket. +.TP \fB-i ifname\fR Specify the interface that is being configured. By default, choose the first interface found with @@ -127,6 +132,10 @@ network, and WPA_ID_STR contains the content of the id_str option. Set the location of the PID file. .TP +\fB-G ping interval\fR +Set the interval (in seconds) at which +wpa_cli pings the supplicant. +.TP \fBcommand\fR Run a command. The available commands are listed in the next section. @@ -201,7 +210,7 @@ exit wpa_cli \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2012, +wpa_supplicant is copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_cli.sgml b/wpa_supplicant/doc/docbook/wpa_cli.sgml index c080c07de0dd8..47947c1deef55 100644 --- a/wpa_supplicant/doc/docbook/wpa_cli.sgml +++ b/wpa_supplicant/doc/docbook/wpa_cli.sgml @@ -15,10 +15,12 @@ <cmdsynopsis> <command>wpa_cli</command> <arg>-p <replaceable>path to ctrl sockets</replaceable></arg> + <arg>-g <replaceable>path to global ctrl_interface socket</replaceable></arg> <arg>-i <replaceable>ifname</replaceable></arg> <arg>-hvB</arg> <arg>-a <replaceable>action file</replaceable></arg> <arg>-P <replaceable>pid file</replaceable></arg> + <arg>-G <replaceable>ping interval</replaceable></arg> <arg><replaceable>command ...</replaceable></arg> </cmdsynopsis> </refsynopsisdiv> @@ -111,6 +113,14 @@ CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar </varlistentry> <varlistentry> + <term>-g control socket path</term> + + <listitem><para>Connect to the global control socket at the + indicated path rather than an interface-specific control + socket.</para></listitem> + </varlistentry> + + <varlistentry> <term>-i ifname</term> <listitem><para>Specify the interface that is being @@ -161,6 +171,13 @@ CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar </varlistentry> <varlistentry> + <term>-G ping interval</term> + + <listitem><para>Set the interval (in seconds) at which + wpa_cli pings the supplicant.</para></listitem> + </varlistentry> + + <varlistentry> <term>command</term> <listitem><para>Run a command. The available commands are @@ -328,7 +345,7 @@ CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2012, + <para>wpa_supplicant is copyright (c) 2003-2015, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/doc/docbook/wpa_gui.8 b/wpa_supplicant/doc/docbook/wpa_gui.8 index f94beb3e0b865..1a172186b8d72 100644 --- a/wpa_supplicant/doc/docbook/wpa_gui.8 +++ b/wpa_supplicant/doc/docbook/wpa_gui.8 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_GUI" "8" "12 January 2013" "" "" +.TH "WPA_GUI" "8" "15 March 2015" "" "" .SH NAME wpa_gui \- WPA Graphical User Interface @@ -42,7 +42,7 @@ shown. \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2012, +wpa_supplicant is copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_gui.sgml b/wpa_supplicant/doc/docbook/wpa_gui.sgml index 0ab641921e69e..84766db3c4597 100644 --- a/wpa_supplicant/doc/docbook/wpa_gui.sgml +++ b/wpa_supplicant/doc/docbook/wpa_gui.sgml @@ -74,7 +74,7 @@ </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2012, + <para>wpa_supplicant is copyright (c) 2003-2015, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/doc/docbook/wpa_passphrase.8 b/wpa_supplicant/doc/docbook/wpa_passphrase.8 index d9c1e6cff2cfb..60fba4302d8c5 100644 --- a/wpa_supplicant/doc/docbook/wpa_passphrase.8 +++ b/wpa_supplicant/doc/docbook/wpa_passphrase.8 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_PASSPHRASE" "8" "12 January 2013" "" "" +.TH "WPA_PASSPHRASE" "8" "15 March 2015" "" "" .SH NAME wpa_passphrase \- Generate a WPA PSK from an ASCII passphrase for a SSID @@ -31,7 +31,7 @@ passphrase will be read from standard input. \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2012, +wpa_supplicant is copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml index 336c03b4b78df..b381e4092e5b6 100644 --- a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml +++ b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml @@ -62,7 +62,7 @@ </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2012, + <para>wpa_supplicant is copyright (c) 2003-2015, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/doc/docbook/wpa_priv.8 b/wpa_supplicant/doc/docbook/wpa_priv.8 index 108f7ee073abe..8b6f6946e04f4 100644 --- a/wpa_supplicant/doc/docbook/wpa_priv.8 +++ b/wpa_supplicant/doc/docbook/wpa_priv.8 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_PRIV" "8" "12 January 2013" "" "" +.TH "WPA_PRIV" "8" "15 March 2015" "" "" .SH NAME wpa_priv \- wpa_supplicant privilege separation helper @@ -111,7 +111,7 @@ processes at the same time, if desired. \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2012, +wpa_supplicant is copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_priv.sgml b/wpa_supplicant/doc/docbook/wpa_priv.sgml index eb907a8bc200a..d13a5dbbfff5e 100644 --- a/wpa_supplicant/doc/docbook/wpa_priv.sgml +++ b/wpa_supplicant/doc/docbook/wpa_priv.sgml @@ -137,7 +137,7 @@ wpa_supplicant -i ath0 -c wpa_supplicant.conf </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2012, + <para>wpa_supplicant is copyright (c) 2003-2015, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.8 b/wpa_supplicant/doc/docbook/wpa_supplicant.8 index 7941dcf7b40e6..35192a3ae42f0 100644 --- a/wpa_supplicant/doc/docbook/wpa_supplicant.8 +++ b/wpa_supplicant/doc/docbook/wpa_supplicant.8 @@ -3,13 +3,13 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_SUPPLICANT" "8" "12 January 2013" "" "" +.TH "WPA_SUPPLICANT" "8" "15 March 2015" "" "" .SH NAME wpa_supplicant \- Wi-Fi Protected Access client and IEEE 802.1X supplicant .SH SYNOPSIS -\fBwpa_supplicant\fR [ \fB-BddfhKLqqtuvW\fR ] [ \fB-i\fIifname\fB\fR ] [ \fB-c\fIconfig file\fB\fR ] [ \fB-D\fIdriver\fB\fR ] [ \fB-P\fIPID_file\fB\fR ] [ \fB-f\fIoutput file\fB\fR ] +\fBwpa_supplicant\fR [ \fB-BddfhKLqqsTtuvW\fR ] [ \fB-i\fIifname\fB\fR ] [ \fB-c\fIconfig file\fB\fR ] [ \fB-D\fIdriver\fB\fR ] [ \fB-P\fIPID_file\fB\fR ] [ \fB-f\fIoutput file\fB\fR ] .SH "OVERVIEW" .PP @@ -264,8 +264,15 @@ more). Driver to use (can be multiple drivers: nl80211,wext). (Per interface, see the available options below.) .TP +\fB-e entropy file\fR +File for \fBwpa_supplicant\fR to use to +maintain its internal entropy store in over restarts. +.TP \fB-f output file\fR -Log output to specified file instead of stdout. +Log output to specified file instead of stdout. (This +is only available if \fBwpa_supplicant\fR was +built with the CONFIG_DEBUG_FILE +option.) .TP \fB-g global ctrl_interface\fR Path to global ctrl_interface socket. If specified, interface @@ -283,6 +290,14 @@ Help. Show a usage message. \fB-L\fR Show license (BSD). .TP +\fB-o override driver\fR +Override the driver parameter for new +interfaces. +.TP +\fB-O override ctrl_interface\fR +Override the ctrl_interface parameter for new +interfaces. +.TP \fB-p\fR Driver parameters. (Per interface) .TP @@ -293,9 +308,27 @@ Path to PID file. Decrease debugging verbosity (\fB-qq\fR even less). .TP +\fB-s\fR +Log output to syslog instead of stdout. (This is only +available if \fBwpa_supplicant\fR was built +with the CONFIG_DEBUG_SYSLOG +option.) +.TP +\fB-T\fR +Log output to Linux tracing in addition to any other +destinations. (This is only available +if \fBwpa_supplicant\fR was built with +the CONFIG_DEBUG_LINUX_TRACING +option.) +.TP +\fB-t\fR +Include timestamp in debug messages. +.TP \fB-u\fR -Enabled DBus control interface. If enabled, interface -definitions may be omitted. +Enable DBus control interface. If enabled, interface +definitions may be omitted. (This is only available +if \fBwpa_supplicant\fR was built with +the CONFIG_DBUS option.) .TP \fB-v\fR Show version. @@ -502,7 +535,7 @@ in. \fBwpa_passphrase\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2012, +wpa_supplicant is copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 b/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 index 6f57aa079ad6e..851311c38f3b6 100644 --- a/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 +++ b/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_SUPPLICANT.CONF" "5" "12 January 2013" "" "" +.TH "WPA_SUPPLICANT.CONF" "5" "15 March 2015" "" "" .SH NAME wpa_supplicant.conf \- configuration file for wpa_supplicant diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml index aa20e571139b9..46c21b5c91a33 100644 --- a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml +++ b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml @@ -12,7 +12,7 @@ <refsynopsisdiv> <cmdsynopsis> <command>wpa_supplicant</command> - <arg>-BddfhKLqqtuvW</arg> + <arg>-BddfhKLqqsTtuvW</arg> <arg>-i<replaceable>ifname</replaceable></arg> <arg>-c<replaceable>config file</replaceable></arg> <arg>-D<replaceable>driver</replaceable></arg> @@ -344,9 +344,20 @@ </varlistentry> <varlistentry> + <term>-e entropy file</term> + <listitem> + <para>File for <command>wpa_supplicant</command> to use to + maintain its internal entropy store in over restarts.</para> + </listitem> + </varlistentry> + + <varlistentry> <term>-f output file</term> <listitem> - <para>Log output to specified file instead of stdout.</para> + <para>Log output to specified file instead of stdout. (This + is only available if <command>wpa_supplicant</command> was + built with the <literal>CONFIG_DEBUG_FILE</literal> + option.)</para> </listitem> </varlistentry> @@ -387,6 +398,22 @@ </varlistentry> <varlistentry> + <term>-o override driver</term> + <listitem> + <para>Override the driver parameter for new + interfaces.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-O override ctrl_interface</term> + <listitem> + <para>Override the ctrl_interface parameter for new + interfaces.</para> + </listitem> + </varlistentry> + + <varlistentry> <term>-p</term> <listitem> <para>Driver parameters. (Per interface)</para> @@ -409,10 +436,40 @@ </varlistentry> <varlistentry> + <term>-s</term> + <listitem> + <para>Log output to syslog instead of stdout. (This is only + available if <command>wpa_supplicant</command> was built + with the <literal>CONFIG_DEBUG_SYSLOG</literal> + option.)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-T</term> + <listitem> + <para>Log output to Linux tracing in addition to any other + destinations. (This is only available + if <command>wpa_supplicant</command> was built with + the <literal>CONFIG_DEBUG_LINUX_TRACING</literal> + option.)</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>-t</term> + <listitem> + <para>Include timestamp in debug messages.</para> + </listitem> + </varlistentry> + + <varlistentry> <term>-u</term> <listitem> - <para>Enabled DBus control interface. If enabled, interface - definitions may be omitted.</para> + <para>Enable DBus control interface. If enabled, interface + definitions may be omitted. (This is only available + if <command>wpa_supplicant</command> was built with + the <literal>CONFIG_DBUS</literal> option.)</para> </listitem> </varlistentry> @@ -679,7 +736,7 @@ fi </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2012, + <para>wpa_supplicant is copyright (c) 2003-2015, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index 847600d20eefd..65b430d4a6c0c 100644 --- a/wpa_supplicant/driver_i.h +++ b/wpa_supplicant/driver_i.h @@ -1,6 +1,6 @@ /* * wpa_supplicant - Internal driver interface wrappers - * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -65,9 +65,35 @@ static inline int wpa_drv_associate(struct wpa_supplicant *wpa_s, return -1; } +static inline int wpa_drv_init_mesh(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->init_mesh) + return wpa_s->driver->init_mesh(wpa_s->drv_priv); + return -1; +} + +static inline int wpa_drv_join_mesh(struct wpa_supplicant *wpa_s, + struct wpa_driver_mesh_join_params *params) +{ + if (wpa_s->driver->join_mesh) + return wpa_s->driver->join_mesh(wpa_s->drv_priv, params); + return -1; +} + +static inline int wpa_drv_leave_mesh(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->leave_mesh) + return wpa_s->driver->leave_mesh(wpa_s->drv_priv); + return -1; +} + static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->test_failure == WPAS_TEST_FAILURE_SCAN_TRIGGER) + return -EBUSY; +#endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->driver->scan2) return wpa_s->driver->scan2(wpa_s->drv_priv, params); return -1; @@ -117,11 +143,16 @@ static inline int wpa_drv_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid) static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s, 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) -{ + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + if (alg != WPA_ALG_NONE) { + if (key_idx >= 0 && key_idx <= 6) + wpa_s->keys_cleared &= ~BIT(key_idx); + else + wpa_s->keys_cleared = 0; + } if (wpa_s->driver->set_key) { - wpa_s->keys_cleared = 0; return wpa_s->driver->set_key(wpa_s->ifname, wpa_s->drv_priv, alg, addr, key_idx, set_tx, seq, seq_len, key, key_len); @@ -129,6 +160,17 @@ static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s, return -1; } +static inline int wpa_drv_sta_deauth(struct wpa_supplicant *wpa_s, + const u8 *addr, int reason_code) +{ + if (wpa_s->driver->sta_deauth) { + return wpa_s->driver->sta_deauth(wpa_s->drv_priv, + wpa_s->own_addr, addr, + reason_code); + } + return -1; +} + static inline int wpa_drv_deauthenticate(struct wpa_supplicant *wpa_s, const u8 *addr, int reason_code) { @@ -190,6 +232,14 @@ static inline const char * wpa_drv_get_ifname(struct wpa_supplicant *wpa_s) return NULL; } +static inline const char * +wpa_driver_get_radio_name(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->driver->get_radio_name) + return wpa_s->driver->get_radio_name(wpa_s->drv_priv); + return NULL; +} + static inline const u8 * wpa_drv_get_mac_addr(struct wpa_supplicant *wpa_s) { if (wpa_s->driver->get_mac_addr) { @@ -198,16 +248,6 @@ static inline const u8 * wpa_drv_get_mac_addr(struct wpa_supplicant *wpa_s) return NULL; } -static inline int wpa_drv_send_eapol(struct wpa_supplicant *wpa_s, - const u8 *dst, u16 proto, - const u8 *data, size_t data_len) -{ - if (wpa_s->driver->send_eapol) - return wpa_s->driver->send_eapol(wpa_s->drv_priv, dst, proto, - data, data_len); - return -1; -} - static inline int wpa_drv_set_operstate(struct wpa_supplicant *wpa_s, int state) { @@ -264,16 +304,6 @@ static inline int wpa_drv_update_ft_ies(struct wpa_supplicant *wpa_s, return -1; } -static inline int wpa_drv_send_ft_action(struct wpa_supplicant *wpa_s, - u8 action, const u8 *target_ap, - const u8 *ies, size_t ies_len) -{ - if (wpa_s->driver->send_ft_action) - return wpa_s->driver->send_ft_action(wpa_s->drv_priv, action, - target_ap, ies, ies_len); - return -1; -} - static inline int wpa_drv_set_ap(struct wpa_supplicant *wpa_s, struct wpa_driver_ap_params *params) { @@ -369,7 +399,7 @@ static inline int wpa_drv_if_add(struct wpa_supplicant *wpa_s, 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, bridge); + if_addr, bridge, 0); return -1; } @@ -505,188 +535,359 @@ static inline int wpa_drv_ampdu(struct wpa_supplicant *wpa_s, int ampdu) 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) +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, + u32 peer_capab, int initiator, + 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, peer_capab, + initiator, 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); +} + +#ifdef ANDROID +static inline int wpa_drv_driver_cmd(struct wpa_supplicant *wpa_s, + char *cmd, char *buf, size_t buf_len) +{ + if (!wpa_s->driver->driver_cmd) + return -1; + return wpa_s->driver->driver_cmd(wpa_s->drv_priv, cmd, buf, buf_len); +} +#endif /* ANDROID */ + +static inline void wpa_drv_set_rekey_info(struct wpa_supplicant *wpa_s, + const u8 *kek, size_t kek_len, + const u8 *kck, size_t kck_len, + const u8 *replay_ctr) +{ + if (!wpa_s->driver->set_rekey_info) + return; + wpa_s->driver->set_rekey_info(wpa_s->drv_priv, kek, kek_len, + kck, kck_len, replay_ctr); +} + +static inline int wpa_drv_radio_disable(struct wpa_supplicant *wpa_s, + int disabled) { - if (!wpa_s->driver->p2p_find) + if (!wpa_s->driver->radio_disable) return -1; - return wpa_s->driver->p2p_find(wpa_s->drv_priv, timeout, type); + return wpa_s->driver->radio_disable(wpa_s->drv_priv, disabled); } -static inline int wpa_drv_p2p_stop_find(struct wpa_supplicant *wpa_s) +static inline int wpa_drv_switch_channel(struct wpa_supplicant *wpa_s, + struct csa_settings *settings) { - if (!wpa_s->driver->p2p_stop_find) + if (!wpa_s->driver->switch_channel) return -1; - return wpa_s->driver->p2p_stop_find(wpa_s->drv_priv); + return wpa_s->driver->switch_channel(wpa_s->drv_priv, settings); } -static inline int wpa_drv_p2p_listen(struct wpa_supplicant *wpa_s, - unsigned int timeout) +static inline int wpa_drv_add_ts(struct wpa_supplicant *wpa_s, u8 tsid, + const u8 *address, u8 user_priority, + u16 admitted_time) { - if (!wpa_s->driver->p2p_listen) + if (!wpa_s->driver->add_tx_ts) return -1; - return wpa_s->driver->p2p_listen(wpa_s->drv_priv, timeout); + return wpa_s->driver->add_tx_ts(wpa_s->drv_priv, tsid, address, + user_priority, admitted_time); } -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) +static inline int wpa_drv_del_ts(struct wpa_supplicant *wpa_s, u8 tid, + const u8 *address) { - if (!wpa_s->driver->p2p_connect) + if (!wpa_s->driver->del_tx_ts) 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); + return wpa_s->driver->del_tx_ts(wpa_s->drv_priv, tid, address); } -static inline int wpa_drv_wps_success_cb(struct wpa_supplicant *wpa_s, - const u8 *peer_addr) +static inline int wpa_drv_tdls_enable_channel_switch( + struct wpa_supplicant *wpa_s, const u8 *addr, u8 oper_class, + const struct hostapd_freq_params *freq_params) { - if (!wpa_s->driver->wps_success_cb) + if (!wpa_s->driver->tdls_enable_channel_switch) return -1; - return wpa_s->driver->wps_success_cb(wpa_s->drv_priv, peer_addr); + return wpa_s->driver->tdls_enable_channel_switch(wpa_s->drv_priv, addr, + oper_class, + freq_params); } static inline int -wpa_drv_p2p_group_formation_failed(struct wpa_supplicant *wpa_s) +wpa_drv_tdls_disable_channel_switch(struct wpa_supplicant *wpa_s, + const u8 *addr) { - if (!wpa_s->driver->p2p_group_formation_failed) + if (!wpa_s->driver->tdls_disable_channel_switch) return -1; - return wpa_s->driver->p2p_group_formation_failed(wpa_s->drv_priv); + return wpa_s->driver->tdls_disable_channel_switch(wpa_s->drv_priv, + addr); } -static inline int wpa_drv_p2p_set_params(struct wpa_supplicant *wpa_s, - const struct p2p_params *params) +static inline int wpa_drv_wnm_oper(struct wpa_supplicant *wpa_s, + enum wnm_oper oper, const u8 *peer, + u8 *buf, u16 *buf_len) { - if (!wpa_s->driver->p2p_set_params) + if (!wpa_s->driver->wnm_oper) return -1; - return wpa_s->driver->p2p_set_params(wpa_s->drv_priv, params); + return wpa_s->driver->wnm_oper(wpa_s->drv_priv, oper, peer, buf, + buf_len); } -static inline int wpa_drv_p2p_prov_disc_req(struct wpa_supplicant *wpa_s, - const u8 *peer_addr, - u16 config_methods, int join) +static inline int wpa_drv_status(struct wpa_supplicant *wpa_s, + char *buf, size_t buflen) { - if (!wpa_s->driver->p2p_prov_disc_req) + if (!wpa_s->driver->status) return -1; - return wpa_s->driver->p2p_prov_disc_req(wpa_s->drv_priv, peer_addr, - config_methods, join); + return wpa_s->driver->status(wpa_s->drv_priv, buf, buflen); } -static inline u64 wpa_drv_p2p_sd_request(struct wpa_supplicant *wpa_s, - const u8 *dst, - const struct wpabuf *tlvs) +static inline int wpa_drv_set_qos_map(struct wpa_supplicant *wpa_s, + const u8 *qos_map_set, u8 qos_map_set_len) { - if (!wpa_s->driver->p2p_sd_request) - return 0; - return wpa_s->driver->p2p_sd_request(wpa_s->drv_priv, dst, tlvs); + if (!wpa_s->driver->set_qos_map) + return -1; + return wpa_s->driver->set_qos_map(wpa_s->drv_priv, qos_map_set, + qos_map_set_len); } -static inline int wpa_drv_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, - u64 req) +static inline int wpa_drv_wowlan(struct wpa_supplicant *wpa_s, + const struct wowlan_triggers *triggers) { - if (!wpa_s->driver->p2p_sd_cancel_request) + if (!wpa_s->driver->set_wowlan) return -1; - return wpa_s->driver->p2p_sd_cancel_request(wpa_s->drv_priv, req); + return wpa_s->driver->set_wowlan(wpa_s->drv_priv, triggers); } -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) +static inline int wpa_drv_vendor_cmd(struct wpa_supplicant *wpa_s, + int vendor_id, int subcmd, const u8 *data, + size_t data_len, struct wpabuf *buf) { - if (!wpa_s->driver->p2p_sd_response) + if (!wpa_s->driver->vendor_cmd) return -1; - return wpa_s->driver->p2p_sd_response(wpa_s->drv_priv, freq, dst, - dialog_token, resp_tlvs); + return wpa_s->driver->vendor_cmd(wpa_s->drv_priv, vendor_id, subcmd, + data, data_len, buf); } -static inline int wpa_drv_p2p_service_update(struct wpa_supplicant *wpa_s) +static inline int wpa_drv_roaming(struct wpa_supplicant *wpa_s, int allowed, + const u8 *bssid) { - if (!wpa_s->driver->p2p_service_update) + if (!wpa_s->driver->roaming) return -1; - return wpa_s->driver->p2p_service_update(wpa_s->drv_priv); + return wpa_s->driver->roaming(wpa_s->drv_priv, allowed, bssid); } -static inline int wpa_drv_p2p_reject(struct wpa_supplicant *wpa_s, - const u8 *addr) +static inline int wpa_drv_set_mac_addr(struct wpa_supplicant *wpa_s, + const u8 *addr) { - if (!wpa_s->driver->p2p_reject) + if (!wpa_s->driver->set_mac_addr) return -1; - return wpa_s->driver->p2p_reject(wpa_s->drv_priv, addr); + return wpa_s->driver->set_mac_addr(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) + +#ifdef CONFIG_MACSEC + +static inline int wpa_drv_macsec_init(struct wpa_supplicant *wpa_s, + struct macsec_init_params *params) { - if (!wpa_s->driver->p2p_invite) + if (!wpa_s->driver->macsec_init) return -1; - return wpa_s->driver->p2p_invite(wpa_s->drv_priv, peer, role, bssid, - ssid, ssid_len, go_dev_addr, - persistent_group); + return wpa_s->driver->macsec_init(wpa_s->drv_priv, params); } -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) +static inline int wpa_drv_macsec_deinit(struct wpa_supplicant *wpa_s) { - 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; + if (!wpa_s->driver->macsec_deinit) + return -1; + return wpa_s->driver->macsec_deinit(wpa_s->drv_priv); } -static inline int wpa_drv_tdls_oper(struct wpa_supplicant *wpa_s, - enum tdls_oper oper, const u8 *peer) +static inline int wpa_drv_enable_protect_frames(struct wpa_supplicant *wpa_s, + Boolean enabled) { - if (!wpa_s->driver->tdls_oper) + if (!wpa_s->driver->enable_protect_frames) return -1; - return wpa_s->driver->tdls_oper(wpa_s->drv_priv, oper, peer); + return wpa_s->driver->enable_protect_frames(wpa_s->drv_priv, enabled); } -static inline void wpa_drv_set_rekey_info(struct wpa_supplicant *wpa_s, - const u8 *kek, const u8 *kck, - const u8 *replay_ctr) +static inline int wpa_drv_set_replay_protect(struct wpa_supplicant *wpa_s, + Boolean enabled, u32 window) { - if (!wpa_s->driver->set_rekey_info) - return; - wpa_s->driver->set_rekey_info(wpa_s->drv_priv, kek, kck, replay_ctr); + if (!wpa_s->driver->set_replay_protect) + return -1; + return wpa_s->driver->set_replay_protect(wpa_s->drv_priv, enabled, + window); } -static inline int wpa_drv_radio_disable(struct wpa_supplicant *wpa_s, - int disabled) +static inline int wpa_drv_set_current_cipher_suite(struct wpa_supplicant *wpa_s, + const u8 *cs, size_t cs_len) { - if (!wpa_s->driver->radio_disable) + if (!wpa_s->driver->set_current_cipher_suite) return -1; - return wpa_s->driver->radio_disable(wpa_s->drv_priv, disabled); + return wpa_s->driver->set_current_cipher_suite(wpa_s->drv_priv, cs, + cs_len); } -static inline int wpa_drv_switch_channel(struct wpa_supplicant *wpa_s, - unsigned int freq) +static inline int wpa_drv_enable_controlled_port(struct wpa_supplicant *wpa_s, + Boolean enabled) { - if (!wpa_s->driver->switch_channel) + if (!wpa_s->driver->enable_controlled_port) return -1; - return wpa_s->driver->switch_channel(wpa_s->drv_priv, freq); + return wpa_s->driver->enable_controlled_port(wpa_s->drv_priv, enabled); } -static inline int wpa_drv_wnm_oper(struct wpa_supplicant *wpa_s, - enum wnm_oper oper, const u8 *peer, - u8 *buf, u16 *buf_len) +static inline int wpa_drv_get_receive_lowest_pn(struct wpa_supplicant *wpa_s, + u32 channel, u8 an, + u32 *lowest_pn) { - if (!wpa_s->driver->wnm_oper) + if (!wpa_s->driver->get_receive_lowest_pn) return -1; - return wpa_s->driver->wnm_oper(wpa_s->drv_priv, oper, peer, buf, - buf_len); + return wpa_s->driver->get_receive_lowest_pn(wpa_s->drv_priv, channel, + an, lowest_pn); +} + +static inline int wpa_drv_get_transmit_next_pn(struct wpa_supplicant *wpa_s, + u32 channel, u8 an, + u32 *next_pn) +{ + if (!wpa_s->driver->get_transmit_next_pn) + return -1; + return wpa_s->driver->get_transmit_next_pn(wpa_s->drv_priv, channel, + an, next_pn); +} + +static inline int wpa_drv_set_transmit_next_pn(struct wpa_supplicant *wpa_s, + u32 channel, u8 an, + u32 next_pn) +{ + if (!wpa_s->driver->set_transmit_next_pn) + return -1; + return wpa_s->driver->set_transmit_next_pn(wpa_s->drv_priv, channel, + an, next_pn); +} + +static inline int wpa_drv_get_available_receive_sc(struct wpa_supplicant *wpa_s, + u32 *channel) +{ + if (!wpa_s->driver->get_available_receive_sc) + return -1; + return wpa_s->driver->get_available_receive_sc(wpa_s->drv_priv, + channel); +} + +static inline int +wpa_drv_create_receive_sc(struct wpa_supplicant *wpa_s, u32 channel, + const u8 *sci_addr, u16 sci_port, + unsigned int conf_offset, int validation) +{ + if (!wpa_s->driver->create_receive_sc) + return -1; + return wpa_s->driver->create_receive_sc(wpa_s->drv_priv, channel, + sci_addr, sci_port, conf_offset, + validation); +} + +static inline int wpa_drv_delete_receive_sc(struct wpa_supplicant *wpa_s, + u32 channel) +{ + if (!wpa_s->driver->delete_receive_sc) + return -1; + return wpa_s->driver->delete_receive_sc(wpa_s->drv_priv, channel); +} + +static inline int wpa_drv_create_receive_sa(struct wpa_supplicant *wpa_s, + u32 channel, u8 an, + u32 lowest_pn, const u8 *sak) +{ + if (!wpa_s->driver->create_receive_sa) + return -1; + return wpa_s->driver->create_receive_sa(wpa_s->drv_priv, channel, an, + lowest_pn, sak); +} + +static inline int wpa_drv_enable_receive_sa(struct wpa_supplicant *wpa_s, + u32 channel, u8 an) +{ + if (!wpa_s->driver->enable_receive_sa) + return -1; + return wpa_s->driver->enable_receive_sa(wpa_s->drv_priv, channel, an); +} + +static inline int wpa_drv_disable_receive_sa(struct wpa_supplicant *wpa_s, + u32 channel, u8 an) +{ + if (!wpa_s->driver->disable_receive_sa) + return -1; + return wpa_s->driver->disable_receive_sa(wpa_s->drv_priv, channel, an); +} + +static inline int +wpa_drv_get_available_transmit_sc(struct wpa_supplicant *wpa_s, u32 *channel) +{ + if (!wpa_s->driver->get_available_transmit_sc) + return -1; + return wpa_s->driver->get_available_transmit_sc(wpa_s->drv_priv, + channel); +} + +static inline int +wpa_drv_create_transmit_sc(struct wpa_supplicant *wpa_s, u32 channel, + const u8 *sci_addr, u16 sci_port, + unsigned int conf_offset) +{ + if (!wpa_s->driver->create_transmit_sc) + return -1; + return wpa_s->driver->create_transmit_sc(wpa_s->drv_priv, channel, + sci_addr, sci_port, + conf_offset); +} + +static inline int wpa_drv_delete_transmit_sc(struct wpa_supplicant *wpa_s, + u32 channel) +{ + if (!wpa_s->driver->delete_transmit_sc) + return -1; + return wpa_s->driver->delete_transmit_sc(wpa_s->drv_priv, channel); +} + +static inline int wpa_drv_create_transmit_sa(struct wpa_supplicant *wpa_s, + u32 channel, u8 an, + u32 next_pn, + Boolean confidentiality, + const u8 *sak) +{ + if (!wpa_s->driver->create_transmit_sa) + return -1; + return wpa_s->driver->create_transmit_sa(wpa_s->drv_priv, channel, an, + next_pn, confidentiality, sak); +} + +static inline int wpa_drv_enable_transmit_sa(struct wpa_supplicant *wpa_s, + u32 channel, u8 an) +{ + if (!wpa_s->driver->enable_transmit_sa) + return -1; + return wpa_s->driver->enable_transmit_sa(wpa_s->drv_priv, channel, an); +} + +static inline int wpa_drv_disable_transmit_sa(struct wpa_supplicant *wpa_s, + u32 channel, u8 an) +{ + if (!wpa_s->driver->disable_transmit_sa) + return -1; + return wpa_s->driver->disable_transmit_sa(wpa_s->drv_priv, channel, an); } +#endif /* CONFIG_MACSEC */ #endif /* DRIVER_I_H */ diff --git a/wpa_supplicant/eap_proxy_dummy.mak b/wpa_supplicant/eap_proxy_dummy.mak new file mode 100644 index 0000000000000..e69de29bb2d1d --- /dev/null +++ b/wpa_supplicant/eap_proxy_dummy.mak diff --git a/wpa_supplicant/eap_proxy_dummy.mk b/wpa_supplicant/eap_proxy_dummy.mk new file mode 100644 index 0000000000000..e69de29bb2d1d --- /dev/null +++ b/wpa_supplicant/eap_proxy_dummy.mk diff --git a/wpa_supplicant/eap_register.c b/wpa_supplicant/eap_register.c index d1eb4ff3b0a6f..ece57166c0997 100644 --- a/wpa_supplicant/eap_register.c +++ b/wpa_supplicant/eap_register.c @@ -40,6 +40,13 @@ int eap_register_methods(void) ret = eap_peer_unauth_tls_register(); #endif /* EAP_UNAUTH_TLS */ +#ifdef EAP_TLS +#ifdef CONFIG_HS20 + if (ret == 0) + ret = eap_peer_wfa_unauth_tls_register(); +#endif /* CONFIG_HS20 */ +#endif /* EAP_TLS */ + #ifdef EAP_MSCHAPv2 if (ret == 0) ret = eap_peer_mschapv2_register(); @@ -135,6 +142,11 @@ int eap_register_methods(void) ret = eap_peer_pwd_register(); #endif /* EAP_PWD */ +#ifdef EAP_EKE + if (ret == 0) + ret = eap_peer_eke_register(); +#endif /* EAP_EKE */ + #ifdef EAP_SERVER_IDENTITY if (ret == 0) ret = eap_server_identity_register(); diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c index 80fe2c6b2011f..9b7af30550bd4 100644 --- a/wpa_supplicant/eapol_test.c +++ b/wpa_supplicant/eapol_test.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - test code - * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -21,18 +21,15 @@ #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" +#include "wpas_glue.h" -extern int wpa_debug_level; -extern int wpa_debug_show_keys; - struct wpa_driver_ops *wpa_drivers[] = { NULL }; @@ -49,6 +46,7 @@ struct eapol_test_data { int eapol_test_num_reauths; int no_mppe_keys; int num_mppe_ok, num_mppe_mismatch; + int req_eap_key_name; u8 radius_identifier; struct radius_msg *last_recv_radius; @@ -61,6 +59,8 @@ struct eapol_test_data { u8 authenticator_pmk[PMK_LEN]; size_t authenticator_pmk_len; + u8 authenticator_eap_key_name[256]; + size_t authenticator_eap_key_name_len; int radius_access_accept_received; int radius_access_reject_received; int auth_timed_out; @@ -73,6 +73,9 @@ struct eapol_test_data { struct extra_radius_attr *extra_attrs; FILE *server_cert_file; + + const char *pcsc_reader; + const char *pcsc_pin; }; static struct eapol_test_data eapol_test; @@ -211,6 +214,13 @@ static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e, goto fail; } + if (e->req_eap_key_name && + !radius_msg_add_attr(msg, RADIUS_ATTR_EAP_KEY_NAME, (u8 *) "\0", + 1)) { + printf("Could not add EAP-Key-Name\n"); + goto fail; + } + if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_NAS_IP_ADDRESS) && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, (u8 *) &e->own_ip_addr, 4)) { @@ -336,6 +346,8 @@ static int eapol_test_compare_pmk(struct eapol_test_data *e) { u8 pmk[PMK_LEN]; int ret = 1; + const u8 *sess_id; + size_t sess_id_len; if (eapol_sm_get_key(e->wpa_s->eapol, pmk, PMK_LEN) == 0) { wpa_hexdump(MSG_DEBUG, "PMK from EAPOL", pmk, PMK_LEN); @@ -364,14 +376,37 @@ static int eapol_test_compare_pmk(struct eapol_test_data *e) else if (!e->no_mppe_keys) e->num_mppe_ok++; + sess_id = eapol_sm_get_session_id(e->wpa_s->eapol, &sess_id_len); + if (!sess_id) + return ret; + if (e->authenticator_eap_key_name_len == 0) { + wpa_printf(MSG_INFO, "No EAP-Key-Name received from server"); + return ret; + } + + if (e->authenticator_eap_key_name_len != sess_id_len || + os_memcmp(e->authenticator_eap_key_name, sess_id, sess_id_len) != 0) + { + wpa_printf(MSG_INFO, + "Locally derived EAP Session-Id does not match EAP-Key-Name from server"); + wpa_hexdump(MSG_DEBUG, "EAP Session-Id", sess_id, sess_id_len); + wpa_hexdump(MSG_DEBUG, "EAP-Key-Name from server", + e->authenticator_eap_key_name, + e->authenticator_eap_key_name_len); + } else { + wpa_printf(MSG_INFO, + "Locally derived EAP Session-Id matches EAP-Key-Name from server"); + } + return ret; } -static void eapol_sm_cb(struct eapol_sm *eapol, int success, void *ctx) +static void eapol_sm_cb(struct eapol_sm *eapol, enum eapol_supp_result result, + void *ctx) { struct eapol_test_data *e = ctx; - printf("eapol_sm_cb: success=%d\n", success); + printf("eapol_sm_cb: result=%d\n", result); e->eapol_test_num_reauths--; if (e->eapol_test_num_reauths < 0) eloop_terminate(); @@ -396,7 +431,56 @@ static void eapol_test_write_cert(FILE *f, const char *subject, } +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) +static void eapol_test_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field, + const char *default_txt) +{ + struct eapol_test_data *e = ctx; + struct wpa_supplicant *wpa_s = e->wpa_s; + struct wpa_ssid *ssid = wpa_s->current_ssid; + const char *field_name, *txt = NULL; + char *buf; + size_t buflen; + int len; + + if (ssid == NULL) + return; + + 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_name, ssid->id, txt); + if (os_snprintf_error(buflen, len)) { + os_free(buf); + return; + } + if (ssid->ssid && buflen > len + ssid->ssid_len) { + os_memcpy(buf + len, ssid->ssid, ssid->ssid_len); + len += ssid->ssid_len; + buf[len] = '\0'; + } + buf[buflen - 1] = '\0'; + wpa_msg(wpa_s, MSG_INFO, "%s", buf); + os_free(buf); +} +#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +#define eapol_test_eap_param_needed NULL +#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + + static void eapol_test_cert_cb(void *ctx, int depth, const char *subject, + const char *altsubject[], int num_altsubject, const char *cert_hash, const struct wpabuf *cert) { @@ -426,6 +510,14 @@ static void eapol_test_cert_cb(void *ctx, int depth, const char *subject, eapol_test_write_cert(e->server_cert_file, subject, cert); } + + if (altsubject) { + int i; + + for (i = 0; i < num_altsubject; i++) + wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT + "depth=%d %s", depth, altsubject[i]); + } } @@ -485,6 +577,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->openssl_ciphers = wpa_s->conf->openssl_ciphers; + ctx->eap_param_needed = eapol_test_eap_param_needed; ctx->cert_cb = eapol_test_cert_cb; ctx->cert_in_cb = 1; ctx->set_anon_id = eapol_test_set_anon_id; @@ -502,6 +596,7 @@ static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s, eapol_conf.required_keys = 0; eapol_conf.fast_reauth = wpa_s->conf->fast_reauth; eapol_conf.workaround = ssid->eap_workaround; + eapol_conf.external_sim = wpa_s->conf->external_sim; eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf); eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard); @@ -701,6 +796,8 @@ static void ieee802_1x_get_keys(struct eapol_test_data *e, size_t shared_secret_len) { struct radius_ms_mppe_keys *keys; + u8 *buf; + size_t len; keys = radius_msg_get_ms_keys(msg, req, shared_secret, shared_secret_len); @@ -739,6 +836,14 @@ static void ieee802_1x_get_keys(struct eapol_test_data *e, os_free(keys->recv); os_free(keys); } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_EAP_KEY_NAME, &buf, &len, + NULL) == 0) { + os_memcpy(e->authenticator_eap_key_name, buf, len); + e->authenticator_eap_key_name_len = len; + } else { + e->authenticator_eap_key_name_len = 0; + } } @@ -833,7 +938,11 @@ static void wpa_init_conf(struct eapol_test_data *e, *pos++ = a[3]; } #else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ - inet_aton(authsrv, &as->addr.u.v4); + if (inet_aton(authsrv, &as->addr.u.v4) < 0) { + wpa_printf(MSG_ERROR, "Invalid IP address '%s'", + authsrv); + assert(0); + } #endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ as->addr.af = AF_INET; as->port = port; @@ -862,7 +971,7 @@ static void wpa_init_conf(struct eapol_test_data *e, } -static int scard_test(void) +static int scard_test(struct eapol_test_data *e) { struct scard_data *scard; size_t len; @@ -893,10 +1002,10 @@ static int scard_test(void) unsigned char aka_ik[IK_LEN]; unsigned char aka_ck[CK_LEN]; - scard = scard_init(SCARD_TRY_BOTH, NULL); + scard = scard_init(e->pcsc_reader); if (scard == NULL) return -1; - if (scard_set_pin(scard, "1234")) { + if (scard_set_pin(scard, e->pcsc_pin)) { wpa_printf(MSG_WARNING, "PIN validation failed"); scard_deinit(scard); return -1; @@ -971,7 +1080,7 @@ failed: } -static int scard_get_triplets(int argc, char *argv[]) +static int scard_get_triplets(struct eapol_test_data *e, int argc, char *argv[]) { struct scard_data *scard; size_t len; @@ -993,7 +1102,7 @@ static int scard_get_triplets(int argc, char *argv[]) wpa_debug_level = 99; } - scard = scard_init(SCARD_GSM_SIM_ONLY, NULL); + scard = scard_init(e->pcsc_reader); if (scard == NULL) { printf("Failed to open smartcard connection\n"); return -1; @@ -1047,11 +1156,12 @@ static void eapol_test_terminate(int sig, void *signal_ctx) static void usage(void) { printf("usage:\n" - "eapol_test [-nWS] -c<conf> [-a<AS IP>] [-p<AS port>] " + "eapol_test [-enWS] -c<conf> [-a<AS IP>] [-p<AS port>] " "[-s<AS secret>]\\\n" " [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n" " [-M<client MAC address>] [-o<server cert file] \\\n" - " [-N<attr spec>] \\\n" + " [-N<attr spec>] [-R<PC/SC reader>] " + "[-P<PC/SC PIN>] \\\n" " [-A<client IP>]\n" "eapol_test scard\n" "eapol_test sim <PIN> <num triplets> [debug]\n" @@ -1067,6 +1177,7 @@ static void usage(void) " -A<client IP> = IP address of the client, default: select " "automatically\n" " -r<count> = number of re-authentications\n" + " -e = Request EAP-Key-Name\n" " -W = wait for a control interface monitor before starting\n" " -S = save configuration after authentication\n" " -n = no MPPE keys expected\n" @@ -1095,6 +1206,7 @@ static void usage(void) int main(int argc, char *argv[]) { + struct wpa_global global; struct wpa_supplicant wpa_s; int c, ret = 1, wait_for_monitor = 0, save_config = 0; char *as_addr = "127.0.0.1"; @@ -1114,12 +1226,13 @@ int main(int argc, char *argv[]) os_memset(&eapol_test, 0, sizeof(eapol_test)); eapol_test.connect_info = "CONNECT 11Mbps 802.11b"; os_memcpy(eapol_test.own_addr, "\x02\x00\x00\x00\x00\x01", ETH_ALEN); + eapol_test.pcsc_pin = "1234"; wpa_debug_level = 0; wpa_debug_show_keys = 1; for (;;) { - c = getopt(argc, argv, "a:A:c:C:M:nN:o:p:r:s:St:W"); + c = getopt(argc, argv, "a:A:c:C:eM:nN:o:p:P:r:R:s:St:W"); if (c < 0) break; switch (c) { @@ -1135,6 +1248,9 @@ int main(int argc, char *argv[]) case 'C': eapol_test.connect_info = optarg; break; + case 'e': + eapol_test.req_eap_key_name = 1; + break; case 'M': if (hwaddr_aton(optarg, eapol_test.own_addr)) { usage(); @@ -1157,9 +1273,15 @@ int main(int argc, char *argv[]) case 'p': as_port = atoi(optarg); break; + case 'P': + eapol_test.pcsc_pin = optarg; + break; case 'r': eapol_test.eapol_test_num_reauths = atoi(optarg); break; + case 'R': + eapol_test.pcsc_reader = optarg; + break; case 's': as_secret = optarg; break; @@ -1207,11 +1329,11 @@ int main(int argc, char *argv[]) } if (argc > optind && os_strcmp(argv[optind], "scard") == 0) { - return scard_test(); + return scard_test(&eapol_test); } if (argc > optind && os_strcmp(argv[optind], "sim") == 0) { - return scard_get_triplets(argc - optind - 1, + return scard_get_triplets(&eapol_test, argc - optind - 1, &argv[optind + 1]); } @@ -1231,9 +1353,13 @@ int main(int argc, char *argv[]) return -1; } + os_memset(&global, 0, sizeof(global)); os_memset(&wpa_s, 0, sizeof(wpa_s)); + wpa_s.global = &global; eapol_test.wpa_s = &wpa_s; - wpa_s.conf = wpa_config_read(conf); + dl_list_init(&wpa_s.bss); + dl_list_init(&wpa_s.bss_id); + wpa_s.conf = wpa_config_read(conf, NULL); if (wpa_s.conf == NULL) { printf("Failed to parse configuration file '%s'.\n", conf); return -1; @@ -1243,6 +1369,11 @@ int main(int argc, char *argv[]) return -1; } + if (eapol_test.pcsc_reader) { + os_free(wpa_s.conf->pcsc_reader); + wpa_s.conf->pcsc_reader = os_strdup(eapol_test.pcsc_reader); + } + wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, as_secret, cli_addr); wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s); diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index baca363f574f9..d275ca424e6ee 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - Driver event processing - * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -42,17 +42,26 @@ #include "scan.h" #include "offchannel.h" #include "interworking.h" +#include "mesh.h" +#include "mesh_mpm.h" +#include "wmm_ac.h" + + +#ifndef CONFIG_NO_SCAN_PROCESSING +static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, + int new_scan, int own_request); +#endif /* CONFIG_NO_SCAN_PROCESSING */ static int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { - struct os_time now; + struct os_reltime now; if (ssid == NULL || ssid->disabled_until.sec == 0) return 0; - os_get_time(&now); + os_get_reltime(&now); if (ssid->disabled_until.sec > now.sec) return ssid->disabled_until.sec - now.sec; @@ -62,13 +71,46 @@ static int wpas_temp_disabled(struct wpa_supplicant *wpa_s, } +static struct wpa_bss * wpa_supplicant_get_new_bss( + struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wpa_bss *bss = NULL; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (ssid->ssid_len > 0) + bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len); + if (!bss) + bss = wpa_bss_get_bssid(wpa_s, bssid); + + return bss; +} + + +static void wpa_supplicant_update_current_bss(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid); + + if (!bss) { + wpa_supplicant_update_scan_results(wpa_s); + + /* Get the BSS from the new scan results */ + bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid); + } + + if (bss) + wpa_s->current_bss = bss; +} + + static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid, *old_ssid; int res; - if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) + if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) { + wpa_supplicant_update_current_bss(wpa_s); return 0; + } wpa_dbg(wpa_s, MSG_DEBUG, "Select network based on association " "information"); @@ -102,8 +144,9 @@ static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) { u8 wpa_ie[80]; size_t wpa_ie_len = sizeof(wpa_ie); - wpa_supplicant_set_suites(wpa_s, NULL, ssid, - wpa_ie, &wpa_ie_len); + if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, + wpa_ie, &wpa_ie_len) < 0) + wpa_dbg(wpa_s, MSG_DEBUG, "Could not set WPA suites"); } else { wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); } @@ -112,6 +155,9 @@ static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) eapol_sm_invalidate_cached_session(wpa_s->eapol); old_ssid = wpa_s->current_ssid; wpa_s->current_ssid = ssid; + + wpa_supplicant_update_current_bss(wpa_s); + wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); wpa_supplicant_initiate_eapol(wpa_s); if (old_ssid != wpa_s->current_ssid) @@ -129,6 +175,15 @@ void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx) wpa_s->countermeasures = 0; wpa_drv_set_countermeasures(wpa_s, 0); wpa_msg(wpa_s, MSG_INFO, "WPA: TKIP countermeasures stopped"); + + /* + * It is possible that the device is sched scanning, which means + * that a connection attempt will be done only when we receive + * scan results. However, in this case, it would be preferable + * to scan and connect immediately, so cancel the sched_scan and + * issue a regular scan flow. + */ + wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); } } @@ -156,20 +211,12 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) 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_SME - wpa_s->sme.prev_bssid_set = 0; -#endif /* CONFIG_SME */ + sme_clear_on_disassoc(wpa_s); #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); @@ -180,7 +227,10 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); wpa_s->ap_ies_from_associnfo = 0; wpa_s->current_ssid = NULL; + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); wpa_s->key_mgmt = 0; + + wpas_rrm_reset(wpa_s); } @@ -199,7 +249,7 @@ static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s) ie.pmkid + i * PMKID_LEN, NULL, NULL, 0); if (pmksa_set == 0) { - eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1); + eapol_sm_notify_pmkid_attempt(wpa_s->eapol); break; } } @@ -265,12 +315,13 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, { #ifdef IEEE8021X_EAPOL #ifdef PCSC_FUNCS - int aka = 0, sim = 0, type; + int aka = 0, sim = 0; - if (ssid->eap.pcsc == NULL || wpa_s->scard != NULL) + if ((ssid != NULL && ssid->eap.pcsc == NULL) || + wpa_s->scard != NULL || wpa_s->conf->external_sim) return 0; - if (ssid->eap.eap_methods == NULL) { + if (ssid == NULL || ssid->eap.eap_methods == NULL) { sim = 1; aka = 1; } else { @@ -304,14 +355,8 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, 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) - type = SCARD_USIM_ONLY; - else - type = SCARD_GSM_SIM_ONLY; - wpa_s->scard = scard_init(type, NULL); + wpa_s->scard = scard_init(wpa_s->conf->pcsc_reader); if (wpa_s->scard == NULL) { wpa_msg(wpa_s, MSG_WARNING, "Failed to initialize SIM " "(pcsc-lite)"); @@ -327,10 +372,24 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, #ifndef CONFIG_NO_SCAN_PROCESSING + +static int has_wep_key(struct wpa_ssid *ssid) +{ + int i; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (ssid->wep_key_len[i]) + return 1; + } + + return 0; +} + + static int wpa_supplicant_match_privacy(struct wpa_bss *bss, struct wpa_ssid *ssid) { - int i, privacy = 0; + int privacy = 0; if (ssid->mixed_cell) return 1; @@ -340,12 +399,9 @@ static int wpa_supplicant_match_privacy(struct wpa_bss *bss, return 1; #endif /* CONFIG_WPS */ - for (i = 0; i < NUM_WEP_KEYS; i++) { - if (ssid->wep_key_len[i]) { - privacy = 1; - break; - } - } + if (has_wep_key(ssid)) + privacy = 1; + #ifdef IEEE8021X_EAPOL if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && ssid->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | @@ -356,6 +412,9 @@ static int wpa_supplicant_match_privacy(struct wpa_bss *bss, if (wpa_key_mgmt_wpa(ssid->key_mgmt)) privacy = 1; + if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN) + privacy = 1; + if (bss->caps & IEEE80211_CAP_PRIVACY) return privacy; return !privacy; @@ -426,8 +485,7 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IEEE80211W if (!(ie.capabilities & WPA_CAPABILITY_MFPC) && - (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? - wpa_s->conf->pmf : ssid->ieee80211w) == + wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) { wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - no mgmt " "frame protection"); @@ -497,6 +555,12 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, return 0; } + if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && + wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE)) { + wpa_dbg(wpa_s, MSG_DEBUG, " allow in OSEN"); + return 1; + } + if (!wpa_key_mgmt_wpa(ssid->key_mgmt)) { wpa_dbg(wpa_s, MSG_DEBUG, " allow in non-WPA/WPA2"); return 1; @@ -523,24 +587,6 @@ static int freq_allowed(int *freqs, int freq) } -static int ht_supported(const struct hostapd_hw_modes *mode) -{ - 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; - } - - /* - * 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; -} - - static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) { const struct hostapd_hw_modes *mode = NULL, *modes; @@ -606,6 +652,18 @@ static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) continue; } + /* There's also a VHT selector for 802.11ac */ + if (flagged && ((rate_ie[j] & 0x7f) == + BSS_MEMBERSHIP_SELECTOR_VHT_PHY)) { + if (!vht_supported(mode)) { + wpa_dbg(wpa_s, MSG_DEBUG, + " hardware does not support " + "VHT PHY"); + return 0; + } + continue; + } + if (!flagged) continue; @@ -620,9 +678,10 @@ static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) * 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); + wpa_dbg(wpa_s, MSG_DEBUG, + " hardware does not support required rate %d.%d Mbps (freq=%d mode==%d num_rates=%d)", + r / 10, r % 10, + bss->freq, mode->mode, mode->num_rates); return 0; } } @@ -632,15 +691,60 @@ static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) } +/* + * Test whether BSS is in an ESS. + * This is done differently in DMG (60 GHz) and non-DMG bands + */ +static int bss_is_ess(struct wpa_bss *bss) +{ + if (bss_is_dmg(bss)) { + return (bss->caps & IEEE80211_CAP_DMG_MASK) == + IEEE80211_CAP_DMG_AP; + } + + return ((bss->caps & (IEEE80211_CAP_ESS | IEEE80211_CAP_IBSS)) == + IEEE80211_CAP_ESS); +} + + +static int match_mac_mask(const u8 *addr_a, const u8 *addr_b, const u8 *mask) +{ + size_t i; + + for (i = 0; i < ETH_ALEN; i++) { + if ((addr_a[i] & mask[i]) != (addr_b[i] & mask[i])) + return 0; + } + return 1; +} + + +static int addr_in_list(const u8 *addr, const u8 *list, size_t num) +{ + size_t i; + + for (i = 0; i < num; i++) { + const u8 *a = list + i * ETH_ALEN * 2; + const u8 *m = a + ETH_ALEN; + + if (match_mac_mask(a, addr, m)) + return 1; + } + return 0; +} + + static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, int i, struct wpa_bss *bss, - struct wpa_ssid *group) + struct wpa_ssid *group, + int only_first_ssid) { u8 wpa_ie_len, rsn_ie_len; int wpa; struct wpa_blacklist *e; const u8 *ie; struct wpa_ssid *ssid; + int osen; ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); wpa_ie_len = ie ? ie[1] : 0; @@ -648,11 +752,18 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); rsn_ie_len = ie ? ie[1] : 0; + ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); + osen = ie != NULL; + wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' " - "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s", + "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s%s%s", i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len), wpa_ie_len, rsn_ie_len, bss->caps, bss->level, - wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : ""); + wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "", + (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) || + wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) ? + " p2p" : "", + osen ? " osen=1" : ""); e = wpa_blacklist_get(wpa_s, bss->bssid); if (e) { @@ -692,7 +803,7 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, wpa = wpa_ie_len > 0 || rsn_ie_len > 0; - for (ssid = group; ssid; ssid = ssid->pnext) { + for (ssid = group; ssid; ssid = only_first_ssid ? NULL : ssid->pnext) { int check_ssid = wpa ? 1 : (ssid->ssid_len != 0); int res; @@ -747,10 +858,28 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, continue; } + /* check blacklist */ + if (ssid->num_bssid_blacklist && + addr_in_list(bss->bssid, ssid->bssid_blacklist, + ssid->num_bssid_blacklist)) { + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - BSSID blacklisted"); + continue; + } + + /* if there is a whitelist, only accept those APs */ + if (ssid->num_bssid_whitelist && + !addr_in_list(bss->bssid, ssid->bssid_whitelist, + ssid->num_bssid_whitelist)) { + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - BSSID not in whitelist"); + continue; + } + if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss)) continue; - if (!wpa && + if (!osen && !wpa && !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) && !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) && !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) { @@ -759,15 +888,26 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, continue; } + if (wpa && !wpa_key_mgmt_wpa(ssid->key_mgmt) && + has_wep_key(ssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - ignore WPA/WPA2 AP for WEP network block"); + continue; + } + + if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && !osen) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - non-OSEN network " + "not allowed"); + continue; + } + if (!wpa_supplicant_match_privacy(bss, ssid)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - privacy " "mismatch"); continue; } - if (bss->caps & IEEE80211_CAP_IBSS) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - IBSS (adhoc) " - "network"); + if (!bss_is_ess(bss)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - not ESS network"); continue; } @@ -784,6 +924,39 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, } #ifdef CONFIG_P2P + if (ssid->p2p_group && + !wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) && + !wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - no P2P IE seen"); + continue; + } + + if (!is_zero_ether_addr(ssid->go_p2p_dev_addr)) { + struct wpabuf *p2p_ie; + u8 dev_addr[ETH_ALEN]; + + ie = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE); + if (ie == NULL) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - no P2P element"); + continue; + } + p2p_ie = wpa_bss_get_vendor_ie_multi( + bss, P2P_IE_VENDOR_TYPE); + if (p2p_ie == NULL) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - could not fetch P2P element"); + continue; + } + + if (p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr) < 0 + || os_memcmp(dev_addr, ssid->go_p2p_dev_addr, + ETH_ALEN) != 0) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - no matching GO P2P Device Address in P2P element"); + wpabuf_free(p2p_ie); + continue; + } + wpabuf_free(p2p_ie); + } + /* * TODO: skip the AP if its P2P IE has Group Formation * bit set in the P2P Group Capability Bitmap and we @@ -803,16 +976,22 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, static struct wpa_bss * wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, struct wpa_ssid *group, - struct wpa_ssid **selected_ssid) + struct wpa_ssid **selected_ssid, + int only_first_ssid) { unsigned int i; - wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d", - group->priority); + if (only_first_ssid) + wpa_dbg(wpa_s, MSG_DEBUG, "Try to find BSS matching pre-selected network id=%d", + group->id); + else + wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d", + group->priority); for (i = 0; i < wpa_s->last_scan_res_used; i++) { struct wpa_bss *bss = wpa_s->last_scan_res[i]; - *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group); + *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group, + only_first_ssid); if (!*selected_ssid) continue; wpa_dbg(wpa_s, MSG_DEBUG, " selected BSS " MACSTR @@ -826,22 +1005,41 @@ wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, } -static struct wpa_bss * -wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, - struct wpa_ssid **selected_ssid) +struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid **selected_ssid) { struct wpa_bss *selected = NULL; int prio; + struct wpa_ssid *next_ssid = NULL; if (wpa_s->last_scan_res == NULL || wpa_s->last_scan_res_used == 0) return NULL; /* no scan results from last update */ + if (wpa_s->next_ssid) { + struct wpa_ssid *ssid; + + /* check that next_ssid is still valid */ + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid == wpa_s->next_ssid) + break; + } + next_ssid = ssid; + wpa_s->next_ssid = NULL; + } + while (selected == NULL) { for (prio = 0; prio < wpa_s->conf->num_prio; prio++) { + if (next_ssid && next_ssid->priority == + wpa_s->conf->pssid[prio]->priority) { + selected = wpa_supplicant_select_bss( + wpa_s, next_ssid, selected_ssid, 1); + if (selected) + break; + } selected = wpa_supplicant_select_bss( wpa_s, wpa_s->conf->pssid[prio], - selected_ssid); + selected_ssid, 0); if (selected) break; } @@ -872,9 +1070,6 @@ static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_DEBUG, "Short-circuit new scan request " "since there are no enabled networks"); wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); -#ifdef CONFIG_P2P - wpa_s->sta_scan_pending = 0; -#endif /* CONFIG_P2P */ return; } @@ -891,8 +1086,12 @@ int wpa_supplicant_connect(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP "PBC session overlap"); #ifdef CONFIG_P2P - if (wpas_p2p_notif_pbc_overlap(wpa_s) == 1) + if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT || + wpa_s->p2p_in_provisioning) { + eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb, + wpa_s, NULL); return -1; + } #endif /* CONFIG_P2P */ #ifdef CONFIG_WPS @@ -901,6 +1100,15 @@ int wpa_supplicant_connect(struct wpa_supplicant *wpa_s, return -1; } + wpa_msg(wpa_s, MSG_DEBUG, + "Considering connect request: reassociate: %d selected: " + MACSTR " bssid: " MACSTR " pending: " MACSTR + " wpa_state: %s ssid=%p current_ssid=%p", + wpa_s->reassociate, MAC2STR(selected->bssid), + MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid), + wpa_supplicant_state_txt(wpa_s->wpa_state), + ssid, wpa_s->current_ssid); + /* * Do not trigger new association unless the BSSID has changed or if * reassociation is requested. If we are in process of associating with @@ -910,22 +1118,21 @@ int wpa_supplicant_connect(struct wpa_supplicant *wpa_s, (os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 && ((wpa_s->wpa_state != WPA_ASSOCIATING && wpa_s->wpa_state != WPA_AUTHENTICATING) || - os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) != - 0))) { + (!is_zero_ether_addr(wpa_s->pending_bssid) && + os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) != + 0) || + (is_zero_ether_addr(wpa_s->pending_bssid) && + ssid != wpa_s->current_ssid)))) { if (wpa_supplicant_scard_init(wpa_s, ssid)) { wpa_supplicant_req_new_scan(wpa_s, 10, 0); 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_msg(wpa_s, MSG_DEBUG, "Request association with " MACSTR, + MAC2STR(selected->bssid)); wpa_supplicant_associate(wpa_s, selected, ssid); } else { - wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with the " - "selected AP"); + wpa_dbg(wpa_s, MSG_DEBUG, "Already associated or trying to " + "connect with the selected AP"); } return 0; @@ -944,7 +1151,8 @@ wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s) if (wpas_network_disabled(wpa_s, ssid)) continue; if (ssid->mode == IEEE80211_MODE_IBSS || - ssid->mode == IEEE80211_MODE_AP) + ssid->mode == IEEE80211_MODE_AP || + ssid->mode == IEEE80211_MODE_MESH) return ssid; } } @@ -1016,10 +1224,14 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, #ifndef CONFIG_NO_ROAMING 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); + wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR + " level=%d snr=%d est_throughput=%u", + MAC2STR(current_bss->bssid), current_bss->level, + current_bss->snr, current_bss->est_throughput); + wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS: " MACSTR + " level=%d snr=%d est_throughput=%u", + MAC2STR(selected->bssid), selected->level, + selected->snr, selected->est_throughput); if (wpa_s->current_ssid->bssid_set && os_memcmp(selected->bssid, wpa_s->current_ssid->bssid, ETH_ALEN) == @@ -1029,6 +1241,12 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, return 1; } + if (selected->est_throughput > current_bss->est_throughput + 5000) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Allow reassociation - selected BSS has better estimated throughput"); + return 1; + } + if (current_bss->level < 0 && current_bss->level > selected->level) { wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better " "signal level"); @@ -1064,9 +1282,11 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, /* Return != 0 if no scan results could be fetched or if scan results should not * be shared with other virtual interfaces. */ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) + union wpa_event_data *data, + int own_request) { - struct wpa_scan_results *scan_res; + struct wpa_scan_results *scan_res = NULL; + int ret = 0; int ap = 0; #ifndef CONFIG_NO_RANDOM_POOL size_t i, num; @@ -1079,33 +1299,20 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, wpa_supplicant_notify_scanning(wpa_s, 0); -#ifdef CONFIG_P2P - if (wpa_s->global->p2p_cb_on_scan_complete && - !wpa_s->global->p2p_disabled && - wpa_s->global->p2p != NULL && !wpa_s->sta_scan_pending && - !wpa_s->scan_res_handler) { - wpa_s->global->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"); - wpa_s->sta_scan_pending = 1; - wpa_supplicant_req_scan(wpa_s, 5, 0); - return -1; - } - } - wpa_s->sta_scan_pending = 0; -#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) + if (wpa_s->conf->ap_scan == 2 || ap || + wpa_s->scan_res_handler == scan_only_handler) + return -1; + if (!own_request) 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 -1; + ret = -1; + goto scan_work_done; } #ifndef CONFIG_NO_RANDOM_POOL @@ -1124,16 +1331,16 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_NO_RANDOM_POOL */ - if (wpa_s->scan_res_handler) { + if (own_request && wpa_s->scan_res_handler && + (wpa_s->own_scan_running || !wpa_s->radio->external_scan_running)) { 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 -2; + ret = -2; + goto scan_work_done; } if (ap) { @@ -1142,63 +1349,90 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, 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 0; + goto scan_work_done; } - wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available"); - wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS); + wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available (own=%u ext=%u)", + wpa_s->own_scan_running, wpa_s->radio->external_scan_running); + if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && + wpa_s->manual_scan_use_id && wpa_s->own_scan_running) { + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u", + wpa_s->manual_scan_id); + wpa_s->manual_scan_use_id = 0; + } else { + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS); + } wpas_notify_scan_results(wpa_s); wpas_notify_scan_done(wpa_s, 1); - if (sme_proc_obss_scan(wpa_s) > 0) { + if (!wpa_s->own_scan_running && wpa_s->radio->external_scan_running) { + wpa_dbg(wpa_s, MSG_DEBUG, "Do not use results from externally requested scan operation for network selection"); wpa_scan_results_free(scan_res); return 0; } - if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) { - wpa_scan_results_free(scan_res); - return 0; - } + if (wnm_scan_process(wpa_s, 1) > 0) + goto scan_work_done; - if (autoscan_notify_scan(wpa_s, scan_res)) { - wpa_scan_results_free(scan_res); - return 0; - } + if (sme_proc_obss_scan(wpa_s) > 0) + goto scan_work_done; + + if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) + goto scan_work_done; + + if (autoscan_notify_scan(wpa_s, scan_res)) + goto scan_work_done; if (wpa_s->disconnected) { wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); - wpa_scan_results_free(scan_res); - return 0; + goto scan_work_done; } if (!wpas_driver_bss_selection(wpa_s) && - bgscan_notify_scan(wpa_s, scan_res) == 1) { - wpa_scan_results_free(scan_res); - return 0; - } + bgscan_notify_scan(wpa_s, scan_res) == 1) + goto scan_work_done; wpas_wps_update_ap_info(wpa_s, scan_res); wpa_scan_results_free(scan_res); - return wpas_select_network_from_last_scan(wpa_s); + if (wpa_s->scan_work) { + struct wpa_radio_work *work = wpa_s->scan_work; + wpa_s->scan_work = NULL; + radio_work_done(work); + } + + return wpas_select_network_from_last_scan(wpa_s, 1, own_request); + +scan_work_done: + wpa_scan_results_free(scan_res); + if (wpa_s->scan_work) { + struct wpa_radio_work *work = wpa_s->scan_work; + wpa_s->scan_work = NULL; + radio_work_done(work); + } + return ret; } -int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s) +static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, + int new_scan, int own_request) { struct wpa_bss *selected; struct wpa_ssid *ssid = NULL; + if (wpa_s->p2p_mgmt) + return 0; /* no normal connection on p2p_mgmt interface */ + selected = wpa_supplicant_pick_network(wpa_s, &ssid); if (selected) { int skip; skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid); if (skip) { - wpa_supplicant_rsn_preauth_scan_results(wpa_s); + if (new_scan) + wpa_supplicant_rsn_preauth_scan_results(wpa_s); return 0; } @@ -1206,30 +1440,52 @@ int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s) wpa_dbg(wpa_s, MSG_DEBUG, "Connect failed"); return -1; } - wpa_supplicant_rsn_preauth_scan_results(wpa_s); + if (new_scan) + wpa_supplicant_rsn_preauth_scan_results(wpa_s); /* * Do not notify other virtual radios of scan results since we do not * want them to start other associations at the same time. */ return 1; } else { +#ifdef CONFIG_MESH + if (wpa_s->ifmsh) { + wpa_msg(wpa_s, MSG_INFO, + "Avoiding join because we already joined a mesh group"); + return 0; + } +#endif /* CONFIG_MESH */ wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found"); ssid = wpa_supplicant_pick_new_network(wpa_s); if (ssid) { 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 { + if (new_scan) + wpa_supplicant_rsn_preauth_scan_results(wpa_s); + } else if (own_request) { + /* + * No SSID found. If SCAN results are as a result of + * own scan request and not due to a scan request on + * another shared interface, try another scan. + */ int timeout_sec = wpa_s->scan_interval; int timeout_usec = 0; #ifdef CONFIG_P2P - if (wpas_p2p_scan_no_go_seen(wpa_s) == 1) + int res; + + res = wpas_p2p_scan_no_go_seen(wpa_s); + if (res == 2) + return 2; + if (res == 1) return 0; - if (wpa_s->p2p_in_provisioning) { + if (wpa_s->p2p_in_provisioning || + wpa_s->show_group_started || + wpa_s->p2p_in_invitation) { /* * Use shorter wait during P2P Provisioning - * state to speed up group formation. + * state and during P2P join-a-group operation + * to speed up group formation. */ timeout_sec = 0; timeout_usec = 250000; @@ -1251,6 +1507,16 @@ int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s) return 1; } #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_WPS + if (wpa_s->after_wps > 0 || wpas_wps_searching(wpa_s)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Use shorter wait during WPS processing"); + timeout_sec = 0; + timeout_usec = 500000; + wpa_supplicant_req_new_scan(wpa_s, timeout_sec, + timeout_usec); + return 0; + } +#endif /* CONFIG_WPS */ if (wpa_supplicant_req_sched_scan(wpa_s)) wpa_supplicant_req_new_scan(wpa_s, timeout_sec, timeout_usec); @@ -1260,13 +1526,21 @@ int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s) } -static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) +static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) { - const char *rn, *rn2; struct wpa_supplicant *ifs; + int res; - if (_wpa_supplicant_event_scan_results(wpa_s, data) != 0) { + res = _wpa_supplicant_event_scan_results(wpa_s, data, 1); + if (res == 2) { + /* + * Interface may have been removed, so must not dereference + * wpa_s after this. + */ + return 1; + } + if (res != 0) { /* * If no scan results could be fetched, then no need to * notify those interfaces that did not actually request @@ -1274,39 +1548,48 @@ static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, * interface, do not notify other interfaces to avoid concurrent * operations during a connection attempt. */ - return; + return 0; } /* - * Check other interfaces to see if they have the same radio-name. If + * Check other interfaces to see if they share the same radio. 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) { + dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, + radio_list) { + if (ifs != wpa_s) { wpa_printf(MSG_DEBUG, "%s: Updating scan results from " "sibling", ifs->ifname); - _wpa_supplicant_event_scan_results(ifs, data); + _wpa_supplicant_event_scan_results(ifs, data, 0); } } + + return 0; } #endif /* CONFIG_NO_SCAN_PROCESSING */ +int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_NO_SCAN_PROCESSING + return -1; +#else /* CONFIG_NO_SCAN_PROCESSING */ + struct os_reltime now; + + if (wpa_s->last_scan_res_used <= 0) + return -1; + + os_get_reltime(&now); + if (os_reltime_expired(&now, &wpa_s->last_scan, 5)) { + wpa_printf(MSG_DEBUG, "Fast associate: Old scan results"); + return -1; + } + + return wpas_select_network_from_last_scan(wpa_s, 0, 1); +#endif /* CONFIG_NO_SCAN_PROCESSING */ +} + #ifdef CONFIG_WNM static void wnm_bss_keep_alive(void *eloop_ctx, void *sock_ctx) @@ -1387,11 +1670,51 @@ void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s) } +#ifdef CONFIG_INTERWORKING + +static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map, + size_t len) +{ + int res; + + wpa_hexdump(MSG_DEBUG, "Interworking: QoS Map Set", qos_map, len); + res = wpa_drv_set_qos_map(wpa_s, qos_map, len); + if (res) { + wpa_printf(MSG_DEBUG, "Interworking: Failed to configure QoS Map Set to the driver"); + } + + return res; +} + + +static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s, + const u8 *ies, size_t ies_len) +{ + struct ieee802_11_elems elems; + + if (ies == NULL) + return; + + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) + return; + + if (elems.qos_map_set) { + wpas_qos_map_set(wpa_s, elems.qos_map_set, + elems.qos_map_set_len); + } +} + +#endif /* CONFIG_INTERWORKING */ + + static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { int l, len, found = 0, wpa_found, rsn_found; const u8 *p; +#ifdef CONFIG_IEEE80211R + u8 bssid[ETH_ALEN]; +#endif /* CONFIG_IEEE80211R */ wpa_dbg(wpa_s, MSG_DEBUG, "Association info event"); if (data->assoc_info.req_ies) @@ -1408,6 +1731,10 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); #endif /* CONFIG_WNM */ +#ifdef CONFIG_INTERWORKING + interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len); +#endif /* CONFIG_INTERWORKING */ } if (data->assoc_info.beacon_ies) wpa_hexdump(MSG_DEBUG, "beacon_ies", @@ -1446,7 +1773,6 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IEEE80211R #ifdef CONFIG_SME if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) { - u8 bssid[ETH_ALEN]; if (wpa_drv_get_bssid(wpa_s, bssid) < 0 || wpa_ft_validate_reassoc_resp(wpa_s->wpa, data->assoc_info.resp_ies, @@ -1504,6 +1830,23 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_SME */ + /* Process FT when SME is in the driver */ + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && + wpa_ft_is_completed(wpa_s->wpa)) { + if (wpa_drv_get_bssid(wpa_s, bssid) < 0 || + wpa_ft_validate_reassoc_resp(wpa_s->wpa, + data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len, + bssid) < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "FT: Validation of " + "Reassociation Response failed"); + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_INVALID_IE); + return -1; + } + wpa_dbg(wpa_s, MSG_DEBUG, "FT: Reassociation Response done"); + } + wpa_sm_set_ft_params(wpa_s->wpa, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); #endif /* CONFIG_IEEE80211R */ @@ -1560,21 +1903,6 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, } -static struct wpa_bss * wpa_supplicant_get_new_bss( - struct wpa_supplicant *wpa_s, const u8 *bssid) -{ - struct wpa_bss *bss = NULL; - struct wpa_ssid *ssid = wpa_s->current_ssid; - - if (ssid->ssid_len > 0) - bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len); - if (!bss) - bss = wpa_bss_get_bssid(wpa_s, bssid); - - return bss; -} - - static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s) { const u8 *bss_wpa = NULL, *bss_rsn = NULL; @@ -1604,10 +1932,11 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, { u8 bssid[ETH_ALEN]; int ft_completed; - struct wpa_driver_capa capa; #ifdef CONFIG_AP if (wpa_s->ap_iface) { + if (!data) + return; hostapd_notif_assoc(wpa_s->ap_iface->bss[0], data->assoc_info.addr, data->assoc_info.req_ies, @@ -1645,20 +1974,6 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, wpa_s, WLAN_REASON_DEAUTH_LEAVING); return; } - if (wpa_s->current_ssid) { - struct wpa_bss *bss = NULL; - - bss = wpa_supplicant_get_new_bss(wpa_s, bssid); - if (!bss) { - wpa_supplicant_update_scan_results(wpa_s); - - /* Get the BSS from the new scan results */ - bss = wpa_supplicant_get_new_bss(wpa_s, bssid); - } - - if (bss) - wpa_s->current_bss = bss; - } if (wpa_s->conf->ap_scan == 1 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION) { @@ -1671,6 +1986,7 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, #ifdef CONFIG_SME os_memcpy(wpa_s->sme.prev_bssid, bssid, ETH_ALEN); wpa_s->sme.prev_bssid_set = 1; + wpa_s->sme.last_unprot_disconnect.sec = 0; #endif /* CONFIG_SME */ wpa_msg(wpa_s, MSG_INFO, "Associated with " MACSTR, MAC2STR(bssid)); @@ -1706,6 +2022,17 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE || (wpa_s->current_ssid && wpa_s->current_ssid->mode == IEEE80211_MODE_IBSS)) { + if (wpa_s->current_ssid && + wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE && + (wpa_s->drv_flags & + WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) { + /* + * Set the key after having received joined-IBSS event + * from the driver. + */ + wpa_supplicant_set_wpa_none_key(wpa_s, + wpa_s->current_ssid); + } wpa_supplicant_cancel_auth_timeout(wpa_s); wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); } else if (!ft_completed) { @@ -1746,9 +2073,9 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, wpa_s->last_eapol_matches_bssid = 0; if (wpa_s->pending_eapol_rx) { - struct os_time now, age; - os_get_time(&now); - os_time_sub(&now, &wpa_s->pending_eapol_rx_time, &age); + struct os_reltime now, age; + os_get_reltime(&now); + os_reltime_sub(&now, &wpa_s->pending_eapol_rx_time, &age); if (age.sec == 0 && age.usec < 100000 && os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) == 0) { @@ -1766,8 +2093,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, 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 && - capa.flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE) { + wpa_s->current_ssid && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) { /* Set static WEP keys again */ wpa_set_wep_keys(wpa_s, wpa_s->current_ssid); } @@ -1791,6 +2118,15 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, #endif /* CONFIG_IBSS_RSN */ wpas_wps_notify_assoc(wpa_s, bssid); + + if (data) { + wmm_ac_notify_assoc(wpa_s, data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len, + &data->assoc_info.wmm_params); + + if (wpa_s->reassoc_same_bss) + wmm_ac_restore_tspecs(wpa_s); + } } @@ -1881,13 +2217,18 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, if (could_be_psk_mismatch(wpa_s, reason_code, locally_generated)) { wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - " "pre-shared key may be incorrect"); - wpas_auth_failed(wpa_s); - } - if (!wpa_s->auto_reconnect_disabled || - wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) { + if (wpas_p2p_4way_hs_failed(wpa_s) > 0) + return; /* P2P group removed */ + wpas_auth_failed(wpa_s, "WRONG_KEY"); + } + if (!wpa_s->disconnected && + (!wpa_s->auto_reconnect_disabled || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPS || + wpas_wps_searching(wpa_s))) { wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect enabled: try to " - "reconnect (wps=%d wpa_state=%d)", + "reconnect (wps=%d/%d wpa_state=%d)", wpa_s->key_mgmt == WPA_KEY_MGMT_WPS, + wpas_wps_searching(wpa_s), wpa_s->wpa_state); if (wpa_s->wpa_state == WPA_COMPLETED && wpa_s->current_ssid && @@ -1927,7 +2268,6 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, wpas_notify_disconnect_reason(wpa_s); if (wpa_supplicant_dynamic_keys(wpa_s)) { wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys"); - wpa_s->keys_cleared = 0; wpa_clear_keys(wpa_s, wpa_s->bssid); } last_ssid = wpa_s->current_ssid; @@ -1938,7 +2278,12 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, wpa_s->current_ssid = last_ssid; } - if (fast_reconnect) { + if (fast_reconnect && + !wpas_network_disabled(wpa_s, fast_reconnect_ssid) && + !disallowed_bssid(wpa_s, fast_reconnect->bssid) && + !disallowed_ssid(wpa_s, fast_reconnect->ssid, + fast_reconnect->ssid_len) && + !wpas_temp_disabled(wpa_s, fast_reconnect_ssid)) { #ifndef CONFIG_NO_SCAN_PROCESSING wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS"); if (wpa_supplicant_connect(wpa_s, fast_reconnect, @@ -1947,6 +2292,14 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s, wpa_supplicant_req_scan(wpa_s, 0, 100000); } #endif /* CONFIG_NO_SCAN_PROCESSING */ + } else if (fast_reconnect) { + /* + * Could not reconnect to the same BSS due to network being + * disabled. Use a new scan to match the alternative behavior + * above, i.e., to continue automatic reconnection attempt in a + * way that enforces disabled network rules. + */ + wpa_supplicant_req_scan(wpa_s, 0, 100000); } } @@ -1971,13 +2324,13 @@ wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s, union wpa_event_data *data) { int pairwise; - struct os_time t; + struct os_reltime t; wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected"); pairwise = (data && data->michael_mic_failure.unicast); - os_get_time(&t); - if ((wpa_s->last_michael_mic_error && - t.sec - wpa_s->last_michael_mic_error <= 60) || + os_get_reltime(&t); + if ((wpa_s->last_michael_mic_error.sec && + !os_reltime_expired(&t, &wpa_s->last_michael_mic_error, 60)) || wpa_s->pending_mic_error_report) { if (wpa_s->pending_mic_error_report) { /* @@ -2055,7 +2408,7 @@ wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s, wpa_sm_key_request(wpa_s->wpa, 1, pairwise); #endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */ } - wpa_s->last_michael_mic_error = t.sec; + wpa_s->last_michael_mic_error = t; wpa_s->mic_errors_seen++; } @@ -2090,7 +2443,6 @@ wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s, 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_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed"); @@ -2099,10 +2451,6 @@ wpa_supplicant_event_interface_status(struct wpa_supplicant *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)) @@ -2133,11 +2481,23 @@ static void wpa_supplicant_event_tdls(struct wpa_supplicant *wpa_s, return; switch (data->tdls.oper) { case TDLS_REQUEST_SETUP: - wpa_tdls_start(wpa_s->wpa, data->tdls.peer); + wpa_tdls_remove(wpa_s->wpa, data->tdls.peer); + if (wpa_tdls_is_external_setup(wpa_s->wpa)) + wpa_tdls_start(wpa_s->wpa, data->tdls.peer); + else + wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, data->tdls.peer); break; case TDLS_REQUEST_TEARDOWN: - wpa_tdls_send_teardown(wpa_s->wpa, data->tdls.peer, - data->tdls.reason_code); + if (wpa_tdls_is_external_setup(wpa_s->wpa)) + wpa_tdls_teardown_link(wpa_s->wpa, data->tdls.peer, + data->tdls.reason_code); + else + wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, + data->tdls.peer); + break; + case TDLS_REQUEST_DISCOVER: + wpa_tdls_send_discovery_request(wpa_s->wpa, + data->tdls.peer); break; } } @@ -2200,6 +2560,23 @@ static void wpa_supplicant_event_ibss_rsn_start(struct wpa_supplicant *wpa_s, ibss_rsn_start(wpa_s->ibss_rsn, data->ibss_rsn_start.peer); } + + +static void wpa_supplicant_event_ibss_auth(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (ssid == NULL) + return; + + /* check if the ssid is correctly configured as IBSS/RSN */ + if (ssid->mode != WPAS_MODE_IBSS || !wpa_key_mgmt_wpa(ssid->key_mgmt)) + return; + + ibss_rsn_handle_auth(wpa_s->ibss_rsn, data->rx_mgmt.frame, + data->rx_mgmt.frame_len); +} #endif /* CONFIG_IBSS_RSN */ @@ -2283,12 +2660,393 @@ static void wpa_supplicant_event_unprot_disassoc(struct wpa_supplicant *wpa_s, } +static void wpas_event_disconnect(struct wpa_supplicant *wpa_s, const u8 *addr, + u16 reason_code, int locally_generated, + const u8 *ie, size_t ie_len, int deauth) +{ +#ifdef CONFIG_AP + if (wpa_s->ap_iface && addr) { + hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], addr); + return; + } + + if (wpa_s->ap_iface) { + wpa_dbg(wpa_s, MSG_DEBUG, "Ignore deauth event in AP mode"); + return; + } +#endif /* CONFIG_AP */ + + if (!locally_generated) + wpa_s->own_disconnect_req = 0; + + wpa_supplicant_event_disassoc(wpa_s, reason_code, locally_generated); + + if (((reason_code == WLAN_REASON_IEEE_802_1X_AUTH_FAILED || + ((wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || + (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) && + eapol_sm_failed(wpa_s->eapol))) && + !wpa_s->eap_expected_failure)) + wpas_auth_failed(wpa_s, "AUTH_FAILED"); + +#ifdef CONFIG_P2P + if (deauth && reason_code > 0) { + if (wpas_p2p_deauth_notif(wpa_s, addr, reason_code, ie, ie_len, + locally_generated) > 0) { + /* + * The interface was removed, so cannot continue + * processing any additional operations after this. + */ + return; + } + } +#endif /* CONFIG_P2P */ + + wpa_supplicant_event_disassoc_finish(wpa_s, reason_code, + locally_generated); +} + + +static void wpas_event_disassoc(struct wpa_supplicant *wpa_s, + struct disassoc_info *info) +{ + u16 reason_code = 0; + int locally_generated = 0; + const u8 *addr = NULL; + const u8 *ie = NULL; + size_t ie_len = 0; + + wpa_dbg(wpa_s, MSG_DEBUG, "Disassociation notification"); + + if (info) { + addr = info->addr; + ie = info->ie; + ie_len = info->ie_len; + reason_code = info->reason_code; + locally_generated = info->locally_generated; + wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s", reason_code, + locally_generated ? " (locally generated)" : ""); + if (addr) + wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR, + MAC2STR(addr)); + wpa_hexdump(MSG_DEBUG, "Disassociation frame IE(s)", + ie, ie_len); + } + +#ifdef CONFIG_AP + if (wpa_s->ap_iface && info && info->addr) { + hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], info->addr); + return; + } + + if (wpa_s->ap_iface) { + wpa_dbg(wpa_s, MSG_DEBUG, "Ignore disassoc event in AP mode"); + return; + } +#endif /* CONFIG_AP */ + +#ifdef CONFIG_P2P + if (info) { + wpas_p2p_disassoc_notif( + wpa_s, info->addr, reason_code, info->ie, info->ie_len, + locally_generated); + } +#endif /* CONFIG_P2P */ + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) + sme_event_disassoc(wpa_s, info); + + wpas_event_disconnect(wpa_s, addr, reason_code, locally_generated, + ie, ie_len, 0); +} + + +static void wpas_event_deauth(struct wpa_supplicant *wpa_s, + struct deauth_info *info) +{ + u16 reason_code = 0; + int locally_generated = 0; + const u8 *addr = NULL; + const u8 *ie = NULL; + size_t ie_len = 0; + + wpa_dbg(wpa_s, MSG_DEBUG, "Deauthentication notification"); + + if (info) { + addr = info->addr; + ie = info->ie; + ie_len = info->ie_len; + reason_code = info->reason_code; + locally_generated = info->locally_generated; + wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s", + reason_code, + locally_generated ? " (locally generated)" : ""); + if (addr) { + wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR, + MAC2STR(addr)); + } + wpa_hexdump(MSG_DEBUG, "Deauthentication frame IE(s)", + ie, ie_len); + } + + wpa_reset_ft_completed(wpa_s->wpa); + + wpas_event_disconnect(wpa_s, addr, reason_code, + locally_generated, ie, ie_len, 1); +} + + +static const char * reg_init_str(enum reg_change_initiator init) +{ + switch (init) { + case REGDOM_SET_BY_CORE: + return "CORE"; + case REGDOM_SET_BY_USER: + return "USER"; + case REGDOM_SET_BY_DRIVER: + return "DRIVER"; + case REGDOM_SET_BY_COUNTRY_IE: + return "COUNTRY_IE"; + case REGDOM_BEACON_HINT: + return "BEACON_HINT"; + } + return "?"; +} + + +static const char * reg_type_str(enum reg_type type) +{ + switch (type) { + case REGDOM_TYPE_UNKNOWN: + return "UNKNOWN"; + case REGDOM_TYPE_COUNTRY: + return "COUNTRY"; + case REGDOM_TYPE_WORLD: + return "WORLD"; + case REGDOM_TYPE_CUSTOM_WORLD: + return "CUSTOM_WORLD"; + case REGDOM_TYPE_INTERSECTION: + return "INTERSECTION"; + } + return "?"; +} + + +static void wpa_supplicant_update_channel_list( + struct wpa_supplicant *wpa_s, struct channel_list_changed *info) +{ + struct wpa_supplicant *ifs; + + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REGDOM_CHANGE "init=%s type=%s%s%s", + reg_init_str(info->initiator), reg_type_str(info->type), + info->alpha2[0] ? " alpha2=" : "", + info->alpha2[0] ? info->alpha2 : ""); + + if (wpa_s->drv_priv == NULL) + return; /* 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); + + wpas_p2p_update_channel_list(wpa_s); + + /* + * Check other interfaces to see if they share the same radio. If + * so, they get updated with this same hw mode info. + */ + dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, + radio_list) { + if (ifs != wpa_s) { + wpa_printf(MSG_DEBUG, "%s: Updating hw mode", + ifs->ifname); + free_hw_features(ifs); + ifs->hw.modes = wpa_drv_get_hw_feature_data( + ifs, &ifs->hw.num_modes, &ifs->hw.flags); + } + } +} + + +static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, + const u8 *frame, size_t len, int freq, + int rssi) +{ + const struct ieee80211_mgmt *mgmt; + const u8 *payload; + size_t plen; + u8 category; + + if (len < IEEE80211_HDRLEN + 2) + return; + + mgmt = (const struct ieee80211_mgmt *) frame; + payload = frame + IEEE80211_HDRLEN; + category = *payload++; + plen = len - IEEE80211_HDRLEN - 1; + + wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR + " Category=%u DataLen=%d freq=%d MHz", + MAC2STR(mgmt->sa), category, (int) plen, freq); + + if (category == WLAN_ACTION_WMM) { + wmm_ac_rx_action(wpa_s, mgmt->da, mgmt->sa, payload, plen); + return; + } + +#ifdef CONFIG_IEEE80211R + if (category == WLAN_ACTION_FT) { + ft_rx_action(wpa_s, payload, plen); + return; + } +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_IEEE80211W +#ifdef CONFIG_SME + if (category == WLAN_ACTION_SA_QUERY) { + sme_sa_query_rx(wpa_s, mgmt->sa, payload, plen); + return; + } +#endif /* CONFIG_SME */ +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_WNM + if (mgmt->u.action.category == WLAN_ACTION_WNM) { + ieee802_11_rx_wnm_action(wpa_s, mgmt, len); + return; + } +#endif /* CONFIG_WNM */ + +#ifdef CONFIG_GAS + if ((mgmt->u.action.category == WLAN_ACTION_PUBLIC || + mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) && + gas_query_rx(wpa_s->gas, mgmt->da, mgmt->sa, mgmt->bssid, + mgmt->u.action.category, + payload, plen, freq) == 0) + return; +#endif /* CONFIG_GAS */ + +#ifdef CONFIG_TDLS + if (category == WLAN_ACTION_PUBLIC && plen >= 4 && + payload[0] == WLAN_TDLS_DISCOVERY_RESPONSE) { + wpa_dbg(wpa_s, MSG_DEBUG, + "TDLS: Received Discovery Response from " MACSTR, + MAC2STR(mgmt->sa)); + return; + } +#endif /* CONFIG_TDLS */ + +#ifdef CONFIG_INTERWORKING + if (category == WLAN_ACTION_QOS && plen >= 1 && + payload[0] == QOS_QOS_MAP_CONFIG) { + const u8 *pos = payload + 1; + size_t qlen = plen - 1; + wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: Received QoS Map Configure frame from " + MACSTR, MAC2STR(mgmt->sa)); + if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) == 0 && + qlen > 2 && pos[0] == WLAN_EID_QOS_MAP_SET && + pos[1] <= qlen - 2 && pos[1] >= 16) + wpas_qos_map_set(wpa_s, pos + 2, pos[1]); + return; + } +#endif /* CONFIG_INTERWORKING */ + + if (category == WLAN_ACTION_RADIO_MEASUREMENT && + payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) { + wpas_rrm_process_neighbor_rep(wpa_s, payload + 1, plen - 1); + return; + } + + if (category == WLAN_ACTION_RADIO_MEASUREMENT && + payload[0] == WLAN_RRM_LINK_MEASUREMENT_REQUEST) { + wpas_rrm_handle_link_measurement_request(wpa_s, mgmt->sa, + payload + 1, plen - 1, + rssi); + return; + } + + wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid, + category, payload, plen, freq); + if (wpa_s->ifmsh) + mesh_mpm_action_rx(wpa_s, mgmt, len); +} + + +static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s, + union wpa_event_data *event) +{ +#ifdef CONFIG_P2P + struct wpa_supplicant *ifs; +#endif /* CONFIG_P2P */ + struct wpa_freq_range_list *list; + char *str = NULL; + + list = &event->freq_range; + + if (list->num) + str = freq_range_list_str(list); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AVOID_FREQ "ranges=%s", + str ? str : ""); + +#ifdef CONFIG_P2P + if (freq_range_list_parse(&wpa_s->global->p2p_go_avoid_freq, str)) { + wpa_dbg(wpa_s, MSG_ERROR, "%s: Failed to parse freq range", + __func__); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Update channel list based on frequency avoid event"); + wpas_p2p_update_channel_list(wpa_s); + } + + for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { + int freq; + if (!ifs->current_ssid || + !ifs->current_ssid->p2p_group || + (ifs->current_ssid->mode != WPAS_MODE_P2P_GO && + ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION)) + continue; + + freq = ifs->current_ssid->frequency; + if (!freq_range_list_includes(list, freq)) { + wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating frequency %d MHz in safe range", + freq); + continue; + } + + wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating in unsafe frequency %d MHz", + freq); + /* TODO: Consider using CSA or removing the group within + * wpa_supplicant */ + wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP); + } +#endif /* CONFIG_P2P */ + + os_free(str); +} + + +static void wpa_supplicant_event_assoc_auth(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + wpa_dbg(wpa_s, MSG_DEBUG, + "Connection authorized by device, previous state %d", + wpa_s->wpa_state); + if (wpa_s->wpa_state == WPA_ASSOCIATED) { + 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); + } + wpa_sm_set_rx_replay_ctr(wpa_s->wpa, data->assoc_info.key_replay_ctr); + wpa_sm_set_ptk_kck_kek(wpa_s->wpa, data->assoc_info.ptk_kck, + data->assoc_info.ptk_kck_len, + data->assoc_info.ptk_kek, + data->assoc_info.ptk_kek_len); +} + + 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; - int locally_generated = 0; if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED && event != EVENT_INTERFACE_ENABLED && @@ -2325,129 +3083,62 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; case EVENT_ASSOC: wpa_supplicant_event_assoc(wpa_s, data); + if (data && data->assoc_info.authorized) + wpa_supplicant_event_assoc_auth(wpa_s, data); break; case EVENT_DISASSOC: - wpa_dbg(wpa_s, MSG_DEBUG, "Disassociation notification"); - if (data) { - wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s", - data->disassoc_info.reason_code, - data->disassoc_info.locally_generated ? - " (locally generated)" : ""); - 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->disassoc_info.reason_code; - locally_generated = - data->disassoc_info.locally_generated; - 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, - locally_generated); -#endif /* CONFIG_P2P */ - } - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) - sme_event_disassoc(wpa_s, data); - /* fall through */ + wpas_event_disassoc(wpa_s, + data ? &data->disassoc_info : NULL); + break; case EVENT_DEAUTH: - if (event == EVENT_DEAUTH) { - wpa_dbg(wpa_s, MSG_DEBUG, - "Deauthentication notification"); - if (data) { - reason_code = data->deauth_info.reason_code; - locally_generated = - data->deauth_info.locally_generated; - wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s", - data->deauth_info.reason_code, - data->deauth_info.locally_generated ? - " (locally generated)" : ""); - 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_AP - if (wpa_s->ap_iface && data && data->deauth_info.addr) { - hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], - 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, - locally_generated); - if (reason_code == WLAN_REASON_IEEE_802_1X_AUTH_FAILED || - ((wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || - (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) && - eapol_sm_failed(wpa_s->eapol))) - wpas_auth_failed(wpa_s); -#ifdef CONFIG_P2P - if (event == EVENT_DEAUTH && data) { - if (wpas_p2p_deauth_notif(wpa_s, - data->deauth_info.addr, - reason_code, - data->deauth_info.ie, - data->deauth_info.ie_len, - locally_generated) > 0) { - /* - * The interface was removed, so cannot - * continue processing any additional - * operations after this. - */ - break; - } - } -#endif /* CONFIG_P2P */ - wpa_supplicant_event_disassoc_finish(wpa_s, reason_code, - locally_generated); + wpas_event_deauth(wpa_s, + data ? &data->deauth_info : NULL); break; case EVENT_MICHAEL_MIC_FAILURE: wpa_supplicant_event_michael_mic_failure(wpa_s, data); break; #ifndef CONFIG_NO_SCAN_PROCESSING - case EVENT_SCAN_RESULTS: - wpa_supplicant_event_scan_results(wpa_s, data); -#ifdef CONFIG_P2P - if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled && - wpa_s->global->p2p != NULL && - wpa_s->wpa_state != WPA_AUTHENTICATING && - wpa_s->wpa_state != WPA_ASSOCIATING) { - wpa_s->global->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 " - "continued after scan result processing"); + case EVENT_SCAN_STARTED: + os_get_reltime(&wpa_s->scan_start_time); + if (wpa_s->own_scan_requested) { + struct os_reltime diff; + + os_reltime_sub(&wpa_s->scan_start_time, + &wpa_s->scan_trigger_time, &diff); + wpa_dbg(wpa_s, MSG_DEBUG, "Own scan request started a scan in %ld.%06ld seconds", + diff.sec, diff.usec); + wpa_s->own_scan_requested = 0; + wpa_s->own_scan_running = 1; + if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && + wpa_s->manual_scan_use_id) { + wpa_msg_ctrl(wpa_s, MSG_INFO, + WPA_EVENT_SCAN_STARTED "id=%u", + wpa_s->manual_scan_id); + } else { + wpa_msg_ctrl(wpa_s, MSG_INFO, + WPA_EVENT_SCAN_STARTED); + } + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "External program started a scan"); + wpa_s->radio->external_scan_running = 1; + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED); } - } -#endif /* CONFIG_P2P */ + break; + case EVENT_SCAN_RESULTS: + if (os_reltime_initialized(&wpa_s->scan_start_time)) { + struct os_reltime now, diff; + os_get_reltime(&now); + os_reltime_sub(&now, &wpa_s->scan_start_time, &diff); + wpa_s->scan_start_time.sec = 0; + wpa_s->scan_start_time.usec = 0; + wpa_dbg(wpa_s, MSG_DEBUG, "Scan completed in %ld.%06ld seconds", + diff.sec, diff.usec); + } + if (wpa_supplicant_event_scan_results(wpa_s, data)) + break; /* interface may have been removed */ + wpa_s->own_scan_running = 0; + wpa_s->radio->external_scan_running = 0; + radio_work_check_next(wpa_s); break; #endif /* CONFIG_NO_SCAN_PROCESSING */ case EVENT_ASSOCINFO: @@ -2505,10 +3196,24 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } break; case EVENT_AUTH_TIMED_OUT: + /* It is possible to get this event from earlier connection */ + if (wpa_s->current_ssid && + wpa_s->current_ssid->mode == WPAS_MODE_MESH) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Ignore AUTH_TIMED_OUT in mesh configuration"); + break; + } if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_auth_timed_out(wpa_s, data); break; case EVENT_ASSOC_TIMED_OUT: + /* It is possible to get this event from earlier connection */ + if (wpa_s->current_ssid && + wpa_s->current_ssid->mode == WPAS_MODE_MESH) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Ignore ASSOC_TIMED_OUT in mesh configuration"); + break; + } if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) sme_event_assoc_timed_out(wpa_s, data); break; @@ -2596,22 +3301,66 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; } -#ifdef CONFIG_AP wpas_ap_ch_switch(wpa_s, data->ch_switch.freq, data->ch_switch.ht_enabled, - data->ch_switch.ch_offset); -#endif /* CONFIG_AP */ + data->ch_switch.ch_offset, + data->ch_switch.ch_width, + data->ch_switch.cf1, + data->ch_switch.cf2); + break; +#ifdef NEED_AP_MLME + case EVENT_DFS_RADAR_DETECTED: + if (data) + wpas_event_dfs_radar_detected(wpa_s, &data->dfs_event); break; + case EVENT_DFS_CAC_STARTED: + if (data) + wpas_event_dfs_cac_started(wpa_s, &data->dfs_event); + break; + case EVENT_DFS_CAC_FINISHED: + if (data) + wpas_event_dfs_cac_finished(wpa_s, &data->dfs_event); + break; + case EVENT_DFS_CAC_ABORTED: + if (data) + wpas_event_dfs_cac_aborted(wpa_s, &data->dfs_event); + break; + case EVENT_DFS_NOP_FINISHED: + if (data) + wpas_event_dfs_cac_nop_finished(wpa_s, + &data->dfs_event); + break; +#endif /* NEED_AP_MLME */ +#endif /* CONFIG_AP */ case EVENT_RX_MGMT: { u16 fc, stype; const struct ieee80211_mgmt *mgmt; +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->ext_mgmt_frame_handling) { + struct rx_mgmt *rx = &data->rx_mgmt; + size_t hex_len = 2 * rx->frame_len + 1; + char *hex = os_malloc(hex_len); + if (hex) { + wpa_snprintf_hex(hex, hex_len, + rx->frame, rx->frame_len); + wpa_msg(wpa_s, MSG_INFO, "MGMT-RX freq=%d datarate=%u ssi_signal=%d %s", + rx->freq, rx->datarate, rx->ssi_signal, + hex); + os_free(hex); + } + break; + } +#endif /* CONFIG_TESTING_OPTIONS */ + mgmt = (const struct ieee80211_mgmt *) data->rx_mgmt.frame; fc = le_to_host16(mgmt->frame_control); stype = WLAN_FC_GET_STYPE(fc); +#ifdef CONFIG_AP if (wpa_s->ap_iface == NULL) { +#endif /* CONFIG_AP */ #ifdef CONFIG_P2P if (stype == WLAN_FC_STYPE_PROBE_REQ && data->rx_mgmt.frame_len > 24) { @@ -2627,9 +3376,34 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; } #endif /* CONFIG_P2P */ +#ifdef CONFIG_IBSS_RSN + if (wpa_s->current_ssid && + wpa_s->current_ssid->mode == WPAS_MODE_IBSS && + stype == WLAN_FC_STYPE_AUTH && + data->rx_mgmt.frame_len >= 30) { + wpa_supplicant_event_ibss_auth(wpa_s, data); + break; + } +#endif /* CONFIG_IBSS_RSN */ + + if (stype == WLAN_FC_STYPE_ACTION) { + wpas_event_rx_mgmt_action( + wpa_s, data->rx_mgmt.frame, + data->rx_mgmt.frame_len, + data->rx_mgmt.freq, + data->rx_mgmt.ssi_signal); + break; + } + + if (wpa_s->ifmsh) { + mesh_mpm_mgmt_rx(wpa_s, &data->rx_mgmt); + break; + } + wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received " "management frame in non-AP mode"); break; +#ifdef CONFIG_AP } if (stype == WLAN_FC_STYPE_PROBE_REQ && @@ -2645,65 +3419,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } ap_mgmt_rx(wpa_s, &data->rx_mgmt); - break; - } #endif /* CONFIG_AP */ - case EVENT_RX_ACTION: - 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, - data->rx_action.len); - 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_WNM - if (data->rx_action.category == WLAN_ACTION_WNM) { - ieee802_11_rx_wnm_action(wpa_s, &data->rx_action); - break; - } -#endif /* CONFIG_WNM */ -#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 */ -#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; + } case EVENT_RX_PROBE_REQ: if (data->rx_probe_req.sa == NULL || data->rx_probe_req.ie == NULL) @@ -2720,14 +3438,12 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, 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, data->rx_probe_req.ssi_signal); -#endif /* CONFIG_P2P */ break; case EVENT_REMAIN_ON_CHANNEL: #ifdef CONFIG_OFFCHANNEL @@ -2735,93 +3451,32 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, 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, data->eapol_rx.data_len); break; case EVENT_SIGNAL_CHANGE: + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SIGNAL_CHANGE + "above=%d signal=%d noise=%d txrate=%d", + data->signal_change.above_threshold, + data->signal_change.current_signal, + data->signal_change.current_noise, + data->signal_change.current_txrate); + wpa_bss_update_level(wpa_s->current_bss, + data->signal_change.current_signal); bgscan_notify_signal_change( wpa_s, data->signal_change.above_threshold, data->signal_change.current_signal, @@ -2832,10 +3487,17 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, wpa_dbg(wpa_s, MSG_DEBUG, "Interface was enabled"); if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { wpa_supplicant_update_mac_addr(wpa_s); + if (wpa_s->p2p_mgmt) { + wpa_supplicant_set_state(wpa_s, + WPA_DISCONNECTED); + break; + } + #ifdef CONFIG_AP if (!wpa_s->ap_iface) { wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + wpa_s->scan_req = NORMAL_SCAN_REQ; wpa_supplicant_req_scan(wpa_s, 0, 0); } else wpa_supplicant_set_state(wpa_s, @@ -2848,25 +3510,59 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; case EVENT_INTERFACE_DISABLED: wpa_dbg(wpa_s, MSG_DEBUG, "Interface was disabled"); +#ifdef CONFIG_P2P + if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO || + (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group && + wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO)) { + /* + * Mark interface disabled if this happens to end up not + * being removed as a separate P2P group interface. + */ + wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED); + /* + * The interface was externally disabled. Remove + * it assuming an external entity will start a + * new session if needed. + */ + if (wpa_s->current_ssid && + wpa_s->current_ssid->p2p_group) + wpas_p2p_interface_unavailable(wpa_s); + else + wpas_p2p_disconnect(wpa_s); + /* + * wpa_s instance may have been freed, so must not use + * it here anymore. + */ + break; + } + if (wpa_s->p2p_scan_work && wpa_s->global->p2p && + p2p_in_progress(wpa_s->global->p2p) > 1) { + /* This radio work will be cancelled, so clear P2P + * state as well. + */ + p2p_stop_find(wpa_s->global->p2p); + } +#endif /* CONFIG_P2P */ + + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) { + /* + * Indicate disconnection to keep ctrl_iface events + * consistent. + */ + wpa_supplicant_event_disassoc( + wpa_s, WLAN_REASON_DEAUTH_LEAVING, 1); + } wpa_supplicant_mark_disassoc(wpa_s); + radio_remove_works(wpa_s, NULL, 0); + 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 */ + wpa_supplicant_update_channel_list( + wpa_s, &data->channel_list_changed); 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 " @@ -2876,11 +3572,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, 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, @@ -2898,7 +3592,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, #endif /* CONFIG_AP */ #ifdef CONFIG_TDLS if (data) - wpa_tdls_disable_link(wpa_s->wpa, data->low_ack.addr); + wpa_tdls_disable_unreachable_link(wpa_s->wpa, + data->low_ack.addr); #endif /* CONFIG_TDLS */ break; case EVENT_IBSS_PEER_LOST: @@ -2916,6 +3611,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->driver_gtk_rekey.replay_ctr); break; case EVENT_SCHED_SCAN_STOPPED: + wpa_s->pno = 0; wpa_s->sched_scanning = 0; wpa_supplicant_notify_scanning(wpa_s, 0); @@ -2923,17 +3619,44 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; /* - * If we timed out, start a new sched scan to continue - * searching for more SSIDs. + * Start a new sched scan to continue searching for more SSIDs + * either if timed out or PNO schedule scan is pending. */ - if (wpa_s->sched_scan_timed_out) + if (wpa_s->sched_scan_timed_out) { wpa_supplicant_req_sched_scan(wpa_s); + } else if (wpa_s->pno_sched_pending) { + wpa_s->pno_sched_pending = 0; + wpas_start_pno(wpa_s); + } + break; case EVENT_WPS_BUTTON_PUSHED: #ifdef CONFIG_WPS wpas_wps_start_pbc(wpa_s, NULL, 0); #endif /* CONFIG_WPS */ break; + case EVENT_AVOID_FREQUENCIES: + wpa_supplicant_notify_avoid_freq(wpa_s, data); + break; + case EVENT_CONNECT_FAILED_REASON: +#ifdef CONFIG_AP + if (!wpa_s->ap_iface || !data) + break; + hostapd_event_connect_failed_reason( + wpa_s->ap_iface->bss[0], + data->connect_failed_reason.addr, + data->connect_failed_reason.code); +#endif /* CONFIG_AP */ + break; + case EVENT_NEW_PEER_CANDIDATE: +#ifdef CONFIG_MESH + if (!wpa_s->ifmsh || !data) + break; + wpa_mesh_notify_peer(wpa_s, data->mesh_peer.peer, + data->mesh_peer.ies, + data->mesh_peer.ie_len); +#endif /* CONFIG_MESH */ + break; default: wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event); break; diff --git a/wpa_supplicant/examples/p2p-action.sh b/wpa_supplicant/examples/p2p-action.sh index 8759f5401e8ea..797d43a0088a5 100755 --- a/wpa_supplicant/examples/p2p-action.sh +++ b/wpa_supplicant/examples/p2p-action.sh @@ -34,13 +34,26 @@ if [ "$CMD" = "P2P-GROUP-STARTED" ]; then # 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 + -F192.168.42.11,192.168.42.99 --listen-address 192.168.42.1 -z -p 0 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 + ipaddr=`echo "$*" | sed 's/.* ip_addr=\([^ ]*\).*/\1/'` + ipmask=`echo "$*" | sed 's/.* ip_mask=\([^ ]*\).*/\1/'` + goipaddr=`echo "$*" | sed 's/.* go_ip_addr=\([^ ]*\).*/\1/'` + if echo "$ipaddr$ipmask$goipaddr" | grep -q ' '; then + ipaddr="" + ipmask="" + goipaddr="" + fi + if [ -n "$ipaddr" ]; then + sudo ifconfig $GIFNAME "$ipaddr" netmask "$ipmask" + sudo ip ro re default via "$goipaddr" + exit 0 + fi dhclient -pf /var/run/dhclient-$GIFNAME.pid \ -lf /var/run/dhclient.leases-$GIFNAME \ -nw \ diff --git a/wpa_supplicant/examples/p2p-nfc.py b/wpa_supplicant/examples/p2p-nfc.py new file mode 100755 index 0000000000000..91eba28908edc --- /dev/null +++ b/wpa_supplicant/examples/p2p-nfc.py @@ -0,0 +1,654 @@ +#!/usr/bin/python +# +# Example nfcpy to wpa_supplicant wrapper for P2P NFC operations +# Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi> +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. + +import os +import sys +import time +import random +import threading +import argparse + +import nfc +import nfc.ndef +import nfc.llcp +import nfc.handover + +import logging + +import wpaspy + +wpas_ctrl = '/var/run/wpa_supplicant' +ifname = None +init_on_touch = False +in_raw_mode = False +prev_tcgetattr = 0 +include_wps_req = True +include_p2p_req = True +no_input = False +srv = None +continue_loop = True +terminate_now = False +summary_file = None +success_file = None + +def summary(txt): + print txt + if summary_file: + with open(summary_file, 'a') as f: + f.write(txt + "\n") + +def success_report(txt): + summary(txt) + if success_file: + with open(success_file, 'a') as f: + f.write(txt + "\n") + +def wpas_connect(): + ifaces = [] + if os.path.isdir(wpas_ctrl): + try: + ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)] + except OSError, error: + print "Could not find wpa_supplicant: ", error + return None + + if len(ifaces) < 1: + print "No wpa_supplicant control interface found" + return None + + for ctrl in ifaces: + if ifname: + if ifname not in ctrl: + continue + try: + print "Trying to use control interface " + ctrl + wpas = wpaspy.Ctrl(ctrl) + return wpas + except Exception, e: + pass + return None + + +def wpas_tag_read(message): + wpas = wpas_connect() + if (wpas == None): + return False + cmd = "WPS_NFC_TAG_READ " + str(message).encode("hex") + global force_freq + if force_freq: + cmd = cmd + " freq=" + force_freq + if "FAIL" in wpas.request(cmd): + return False + return True + + +def wpas_get_handover_req(): + wpas = wpas_connect() + if (wpas == None): + return None + res = wpas.request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip() + if "FAIL" in res: + return None + return res.decode("hex") + +def wpas_get_handover_req_wps(): + wpas = wpas_connect() + if (wpas == None): + return None + res = wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip() + if "FAIL" in res: + return None + return res.decode("hex") + + +def wpas_get_handover_sel(tag=False): + wpas = wpas_connect() + if (wpas == None): + return None + if tag: + res = wpas.request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip() + else: + res = wpas.request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip() + if "FAIL" in res: + return None + return res.decode("hex") + + +def wpas_get_handover_sel_wps(): + wpas = wpas_connect() + if (wpas == None): + return None + res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR"); + if "FAIL" in res: + return None + return res.rstrip().decode("hex") + + +def wpas_report_handover(req, sel, type): + wpas = wpas_connect() + if (wpas == None): + return None + cmd = "NFC_REPORT_HANDOVER " + type + " P2P " + str(req).encode("hex") + " " + str(sel).encode("hex") + global force_freq + if force_freq: + cmd = cmd + " freq=" + force_freq + return wpas.request(cmd) + + +def wpas_report_handover_wsc(req, sel, type): + wpas = wpas_connect() + if (wpas == None): + return None + cmd = "NFC_REPORT_HANDOVER " + type + " WPS " + str(req).encode("hex") + " " + str(sel).encode("hex") + if force_freq: + cmd = cmd + " freq=" + force_freq + return wpas.request(cmd) + + +def p2p_handover_client(llc): + message = nfc.ndef.HandoverRequestMessage(version="1.2") + message.nonce = random.randint(0, 0xffff) + + global include_p2p_req + if include_p2p_req: + data = wpas_get_handover_req() + if (data == None): + summary("Could not get handover request carrier record from wpa_supplicant") + return + print "Handover request carrier record from wpa_supplicant: " + data.encode("hex") + datamsg = nfc.ndef.Message(data) + message.add_carrier(datamsg[0], "active", datamsg[1:]) + + global include_wps_req + if include_wps_req: + print "Handover request (pre-WPS):" + try: + print message.pretty() + except Exception, e: + print e + + data = wpas_get_handover_req_wps() + if data: + print "Add WPS request in addition to P2P" + datamsg = nfc.ndef.Message(data) + message.add_carrier(datamsg[0], "active", datamsg[1:]) + + print "Handover request:" + try: + print message.pretty() + except Exception, e: + print e + print str(message).encode("hex") + + client = nfc.handover.HandoverClient(llc) + try: + summary("Trying to initiate NFC connection handover") + client.connect() + summary("Connected for handover") + except nfc.llcp.ConnectRefused: + summary("Handover connection refused") + client.close() + return + except Exception, e: + summary("Other exception: " + str(e)) + client.close() + return + + summary("Sending handover request") + + if not client.send(message): + summary("Failed to send handover request") + client.close() + return + + summary("Receiving handover response") + message = client._recv() + if message is None: + summary("No response received") + client.close() + return + if message.type != "urn:nfc:wkt:Hs": + summary("Response was not Hs - received: " + message.type) + client.close() + return + + print "Received message" + try: + print message.pretty() + except Exception, e: + print e + print str(message).encode("hex") + message = nfc.ndef.HandoverSelectMessage(message) + summary("Handover select received") + try: + print message.pretty() + except Exception, e: + print e + + for carrier in message.carriers: + print "Remote carrier type: " + carrier.type + if carrier.type == "application/vnd.wfa.p2p": + print "P2P carrier type match - send to wpa_supplicant" + if "OK" in wpas_report_handover(data, carrier.record, "INIT"): + success_report("P2P handover reported successfully (initiator)") + else: + summary("P2P handover report rejected") + break + + print "Remove peer" + client.close() + print "Done with handover" + global only_one + if only_one: + print "only_one -> stop loop" + global continue_loop + continue_loop = False + + global no_wait + if no_wait: + print "Trying to exit.." + global terminate_now + terminate_now = True + + +class HandoverServer(nfc.handover.HandoverServer): + def __init__(self, llc): + super(HandoverServer, self).__init__(llc) + self.sent_carrier = None + self.ho_server_processing = False + self.success = False + + # override to avoid parser error in request/response.pretty() in nfcpy + # due to new WSC handover format + def _process_request(self, request): + summary("received handover request {}".format(request.type)) + response = nfc.ndef.Message("\xd1\x02\x01Hs\x12") + if not request.type == 'urn:nfc:wkt:Hr': + summary("not a handover request") + else: + try: + request = nfc.ndef.HandoverRequestMessage(request) + except nfc.ndef.DecodeError as e: + summary("error decoding 'Hr' message: {}".format(e)) + else: + response = self.process_request(request) + summary("send handover response {}".format(response.type)) + return response + + def process_request(self, request): + self.ho_server_processing = True + clear_raw_mode() + print "HandoverServer - request received" + try: + print "Parsed handover request: " + request.pretty() + except Exception, e: + print e + + sel = nfc.ndef.HandoverSelectMessage(version="1.2") + + found = False + + for carrier in request.carriers: + print "Remote carrier type: " + carrier.type + if carrier.type == "application/vnd.wfa.p2p": + print "P2P carrier type match - add P2P carrier record" + found = True + self.received_carrier = carrier.record + print "Carrier record:" + try: + print carrier.record.pretty() + except Exception, e: + print e + data = wpas_get_handover_sel() + if data is None: + print "Could not get handover select carrier record from wpa_supplicant" + continue + print "Handover select carrier record from wpa_supplicant:" + print data.encode("hex") + self.sent_carrier = data + if "OK" in wpas_report_handover(self.received_carrier, self.sent_carrier, "RESP"): + success_report("P2P handover reported successfully (responder)") + else: + summary("P2P handover report rejected") + break + + message = nfc.ndef.Message(data); + sel.add_carrier(message[0], "active", message[1:]) + break + + for carrier in request.carriers: + if found: + break + print "Remote carrier type: " + carrier.type + if carrier.type == "application/vnd.wfa.wsc": + print "WSC carrier type match - add WSC carrier record" + found = True + self.received_carrier = carrier.record + print "Carrier record:" + try: + print carrier.record.pretty() + except Exception, e: + print e + data = wpas_get_handover_sel_wps() + if data is None: + print "Could not get handover select carrier record from wpa_supplicant" + continue + print "Handover select carrier record from wpa_supplicant:" + print data.encode("hex") + self.sent_carrier = data + if "OK" in wpas_report_handover_wsc(self.received_carrier, self.sent_carrier, "RESP"): + success_report("WSC handover reported successfully") + else: + summary("WSC handover report rejected") + break + + message = nfc.ndef.Message(data); + sel.add_carrier(message[0], "active", message[1:]) + found = True + break + + print "Handover select:" + try: + print sel.pretty() + except Exception, e: + print e + print str(sel).encode("hex") + + summary("Sending handover select") + self.success = True + return sel + + +def clear_raw_mode(): + import sys, tty, termios + global prev_tcgetattr, in_raw_mode + if not in_raw_mode: + return + fd = sys.stdin.fileno() + termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr) + in_raw_mode = False + + +def getch(): + import sys, tty, termios, select + global prev_tcgetattr, in_raw_mode + fd = sys.stdin.fileno() + prev_tcgetattr = termios.tcgetattr(fd) + ch = None + try: + tty.setraw(fd) + in_raw_mode = True + [i, o, e] = select.select([fd], [], [], 0.05) + if i: + ch = sys.stdin.read(1) + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr) + in_raw_mode = False + return ch + + +def p2p_tag_read(tag): + success = False + if len(tag.ndef.message): + for record in tag.ndef.message: + print "record type " + record.type + if record.type == "application/vnd.wfa.wsc": + summary("WPS tag - send to wpa_supplicant") + success = wpas_tag_read(tag.ndef.message) + break + if record.type == "application/vnd.wfa.p2p": + summary("P2P tag - send to wpa_supplicant") + success = wpas_tag_read(tag.ndef.message) + break + else: + summary("Empty tag") + + if success: + success_report("Tag read succeeded") + + return success + + +def rdwr_connected_p2p_write(tag): + summary("Tag found - writing - " + str(tag)) + global p2p_sel_data + tag.ndef.message = str(p2p_sel_data) + success_report("Tag write succeeded") + print "Done - remove tag" + global only_one + if only_one: + global continue_loop + continue_loop = False + global p2p_sel_wait_remove + return p2p_sel_wait_remove + +def wps_write_p2p_handover_sel(clf, wait_remove=True): + print "Write P2P handover select" + data = wpas_get_handover_sel(tag=True) + if (data == None): + summary("Could not get P2P handover select from wpa_supplicant") + return + + global p2p_sel_wait_remove + p2p_sel_wait_remove = wait_remove + global p2p_sel_data + p2p_sel_data = nfc.ndef.HandoverSelectMessage(version="1.2") + message = nfc.ndef.Message(data); + p2p_sel_data.add_carrier(message[0], "active", message[1:]) + print "Handover select:" + try: + print p2p_sel_data.pretty() + except Exception, e: + print e + print str(p2p_sel_data).encode("hex") + + print "Touch an NFC tag" + clf.connect(rdwr={'on-connect': rdwr_connected_p2p_write}) + + +def rdwr_connected(tag): + global only_one, no_wait + summary("Tag connected: " + str(tag)) + + if tag.ndef: + print "NDEF tag: " + tag.type + try: + print tag.ndef.message.pretty() + except Exception, e: + print e + success = p2p_tag_read(tag) + if only_one and success: + global continue_loop + continue_loop = False + else: + summary("Not an NDEF tag - remove tag") + return True + + return not no_wait + + +def llcp_worker(llc): + global init_on_touch + if init_on_touch: + print "Starting handover client" + p2p_handover_client(llc) + return + + global no_input + if no_input: + print "Wait for handover to complete" + else: + print "Wait for handover to complete - press 'i' to initiate ('w' for WPS only, 'p' for P2P only)" + global srv + global wait_connection + while not wait_connection and srv.sent_carrier is None: + if srv.ho_server_processing: + time.sleep(0.025) + elif no_input: + time.sleep(0.5) + else: + global include_wps_req, include_p2p_req + res = getch() + if res == 'i': + include_wps_req = True + include_p2p_req = True + elif res == 'p': + include_wps_req = False + include_p2p_req = True + elif res == 'w': + include_wps_req = True + include_p2p_req = False + else: + continue + clear_raw_mode() + print "Starting handover client" + p2p_handover_client(llc) + return + + clear_raw_mode() + print "Exiting llcp_worker thread" + +def llcp_startup(clf, llc): + print "Start LLCP server" + global srv + srv = HandoverServer(llc) + return llc + +def llcp_connected(llc): + print "P2P LLCP connected" + global wait_connection + wait_connection = False + global init_on_touch + if not init_on_touch: + global srv + srv.start() + if init_on_touch or not no_input: + threading.Thread(target=llcp_worker, args=(llc,)).start() + return True + +def terminate_loop(): + global terminate_now + return terminate_now + +def main(): + clf = nfc.ContactlessFrontend() + + parser = argparse.ArgumentParser(description='nfcpy to wpa_supplicant integration for P2P and WPS NFC operations') + parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO, + action='store_const', dest='loglevel', + help='verbose debug output') + parser.add_argument('-q', const=logging.WARNING, action='store_const', + dest='loglevel', help='be quiet') + parser.add_argument('--only-one', '-1', action='store_true', + help='run only one operation and exit') + parser.add_argument('--init-on-touch', '-I', action='store_true', + help='initiate handover on touch') + parser.add_argument('--no-wait', action='store_true', + help='do not wait for tag to be removed before exiting') + parser.add_argument('--ifname', '-i', + help='network interface name') + parser.add_argument('--no-wps-req', '-N', action='store_true', + help='do not include WPS carrier record in request') + parser.add_argument('--no-input', '-a', action='store_true', + help='do not use stdout input to initiate handover') + parser.add_argument('--tag-read-only', '-t', action='store_true', + help='tag read only (do not allow connection handover)') + parser.add_argument('--handover-only', action='store_true', + help='connection handover only (do not allow tag read)') + parser.add_argument('--freq', '-f', + help='forced frequency of operating channel in MHz') + parser.add_argument('--summary', + help='summary file for writing status updates') + parser.add_argument('--success', + help='success file for writing success update') + parser.add_argument('command', choices=['write-p2p-sel'], + nargs='?') + args = parser.parse_args() + + global only_one + only_one = args.only_one + + global no_wait + no_wait = args.no_wait + + global force_freq + force_freq = args.freq + + logging.basicConfig(level=args.loglevel) + + global init_on_touch + init_on_touch = args.init_on_touch + + if args.ifname: + global ifname + ifname = args.ifname + print "Selected ifname " + ifname + + if args.no_wps_req: + global include_wps_req + include_wps_req = False + + if args.summary: + global summary_file + summary_file = args.summary + + if args.success: + global success_file + success_file = args.success + + if args.no_input: + global no_input + no_input = True + + clf = nfc.ContactlessFrontend() + global wait_connection + + try: + if not clf.open("usb"): + print "Could not open connection with an NFC device" + raise SystemExit + + if args.command == "write-p2p-sel": + wps_write_p2p_handover_sel(clf, wait_remove=not args.no_wait) + raise SystemExit + + global continue_loop + while continue_loop: + print "Waiting for a tag or peer to be touched" + wait_connection = True + try: + if args.tag_read_only: + if not clf.connect(rdwr={'on-connect': rdwr_connected}): + break + elif args.handover_only: + if not clf.connect(llcp={'on-startup': llcp_startup, + 'on-connect': llcp_connected}, + terminate=terminate_loop): + break + else: + if not clf.connect(rdwr={'on-connect': rdwr_connected}, + llcp={'on-startup': llcp_startup, + 'on-connect': llcp_connected}, + terminate=terminate_loop): + break + except Exception, e: + print "clf.connect failed" + + global srv + if only_one and srv and srv.success: + raise SystemExit + + except KeyboardInterrupt: + raise SystemExit + finally: + clf.close() + + raise SystemExit + +if __name__ == '__main__': + main() diff --git a/wpa_supplicant/examples/wps-ap-cli b/wpa_supplicant/examples/wps-ap-cli index 7c6b0aa8e892d..cc2cff2ebc24f 100755 --- a/wpa_supplicant/examples/wps-ap-cli +++ b/wpa_supplicant/examples/wps-ap-cli @@ -14,11 +14,13 @@ pbc() enter_pin() { echo "Enter a PIN from a station to be enrolled to the network." - read -p "Enrollee PIN: " pin + echo -n "Enrollee PIN: " + read pin cpin=`$CLI wps_check_pin "$pin" | tail -1` if [ "$cpin" = "FAIL-CHECKSUM" ]; then echo "Checksum digit is not valid" - read -p "Do you want to use this PIN (y/n)? " resp + echo -n "Do you want to use this PIN (y/n)? " + read resp case "$resp" in y*) cpin=`echo "$pin" | sed "s/[^1234567890]//g"` @@ -50,7 +52,8 @@ main_menu() echo "3: Show current configuration" echo "0: Exit wps-ap-cli" - read -p "Command: " cmd + echo -n "Command: " + read cmd case "$cmd" in 1) diff --git a/wpa_supplicant/examples/wps-nfc.py b/wpa_supplicant/examples/wps-nfc.py index 0cfc1f684dc9f..7459eb9ae5744 100755 --- a/wpa_supplicant/examples/wps-nfc.py +++ b/wpa_supplicant/examples/wps-nfc.py @@ -1,7 +1,7 @@ #!/usr/bin/python # # Example nfcpy to wpa_supplicant wrapper for WPS NFC operations -# Copyright (c) 2012, Jouni Malinen <j@w1.fi> +# Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi> # # This software may be distributed under the terms of the BSD license. # See README for more details. @@ -9,15 +9,37 @@ import os import sys import time +import random +import threading +import argparse import nfc import nfc.ndef import nfc.llcp import nfc.handover -import wpactrl +import logging + +import wpaspy wpas_ctrl = '/var/run/wpa_supplicant' +srv = None +continue_loop = True +terminate_now = False +summary_file = None +success_file = None + +def summary(txt): + print txt + if summary_file: + with open(summary_file, 'a') as f: + f.write(txt + "\n") + +def success_report(txt): + summary(txt) + if success_file: + with open(success_file, 'a') as f: + f.write(txt + "\n") def wpas_connect(): ifaces = [] @@ -34,10 +56,9 @@ def wpas_connect(): for ctrl in ifaces: try: - wpas = wpactrl.WPACtrl(ctrl) + wpas = wpaspy.Ctrl(ctrl) return wpas - except wpactrl.error, error: - print "Error: ", error + except Exception, e: pass return None @@ -45,111 +66,453 @@ def wpas_connect(): def wpas_tag_read(message): wpas = wpas_connect() if (wpas == None): - return - print wpas.request("WPS_NFC_TAG_READ " + message.encode("hex")) + return False + if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")): + return False + return True + +def wpas_get_config_token(id=None): + wpas = wpas_connect() + if (wpas == None): + return None + if id: + ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF " + id) + else: + ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF") + if "FAIL" in ret: + return None + return ret.rstrip().decode("hex") + + +def wpas_get_er_config_token(uuid): + wpas = wpas_connect() + if (wpas == None): + return None + ret = wpas.request("WPS_ER_NFC_CONFIG_TOKEN NDEF " + uuid) + if "FAIL" in ret: + return None + return ret.rstrip().decode("hex") + +def wpas_get_password_token(): + wpas = wpas_connect() + if (wpas == None): + return None + ret = wpas.request("WPS_NFC_TOKEN NDEF") + if "FAIL" in ret: + return None + return ret.rstrip().decode("hex") def wpas_get_handover_req(): wpas = wpas_connect() if (wpas == None): return None - return wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS").rstrip().decode("hex") + ret = wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR") + if "FAIL" in ret: + return None + return ret.rstrip().decode("hex") -def wpas_put_handover_sel(message): +def wpas_get_handover_sel(uuid): wpas = wpas_connect() if (wpas == None): - return - print wpas.request("NFC_RX_HANDOVER_SEL " + str(message).encode("hex")) + return None + if uuid is None: + res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip() + else: + res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR " + uuid).rstrip() + if "FAIL" in res: + return None + return res.decode("hex") -def wps_handover_init(peer): - print "Trying to initiate WPS handover" +def wpas_report_handover(req, sel, type): + wpas = wpas_connect() + if (wpas == None): + return None + return wpas.request("NFC_REPORT_HANDOVER " + type + " WPS " + + str(req).encode("hex") + " " + + str(sel).encode("hex")) + + +class HandoverServer(nfc.handover.HandoverServer): + def __init__(self, llc): + super(HandoverServer, self).__init__(llc) + self.sent_carrier = None + self.ho_server_processing = False + self.success = False + + # override to avoid parser error in request/response.pretty() in nfcpy + # due to new WSC handover format + def _process_request(self, request): + summary("received handover request {}".format(request.type)) + response = nfc.ndef.Message("\xd1\x02\x01Hs\x12") + if not request.type == 'urn:nfc:wkt:Hr': + summary("not a handover request") + else: + try: + request = nfc.ndef.HandoverRequestMessage(request) + except nfc.ndef.DecodeError as e: + summary("error decoding 'Hr' message: {}".format(e)) + else: + response = self.process_request(request) + summary("send handover response {}".format(response.type)) + return response + + def process_request(self, request): + self.ho_server_processing = True + summary("HandoverServer - request received") + try: + print "Parsed handover request: " + request.pretty() + except Exception, e: + print e + + sel = nfc.ndef.HandoverSelectMessage(version="1.2") + + for carrier in request.carriers: + print "Remote carrier type: " + carrier.type + if carrier.type == "application/vnd.wfa.wsc": + summary("WPS carrier type match - add WPS carrier record") + data = wpas_get_handover_sel(self.uuid) + if data is None: + summary("Could not get handover select carrier record from wpa_supplicant") + continue + print "Handover select carrier record from wpa_supplicant:" + print data.encode("hex") + self.sent_carrier = data + if "OK" in wpas_report_handover(carrier.record, self.sent_carrier, "RESP"): + success_report("Handover reported successfully (responder)") + else: + summary("Handover report rejected (responder)") + + message = nfc.ndef.Message(data); + sel.add_carrier(message[0], "active", message[1:]) + + print "Handover select:" + try: + print sel.pretty() + except Exception, e: + print e + print str(sel).encode("hex") + + summary("Sending handover select") + self.success = True + return sel + + +def wps_handover_init(llc): + summary("Trying to initiate WPS handover") data = wpas_get_handover_req() if (data == None): - print "Could not get handover request message from wpa_supplicant" + summary("Could not get handover request carrier record from wpa_supplicant") return - print "Handover request from wpa_supplicant: " + data.encode("hex") - message = nfc.ndef.Message(data) - print "Parsed handover request: " + message.pretty() + print "Handover request carrier record from wpa_supplicant: " + data.encode("hex") - nfc.llcp.activate(peer); - time.sleep(0.5) + message = nfc.ndef.HandoverRequestMessage(version="1.2") + message.nonce = random.randint(0, 0xffff) + datamsg = nfc.ndef.Message(data) + message.add_carrier(datamsg[0], "active", datamsg[1:]) - client = nfc.handover.HandoverClient() + print "Handover request:" try: - print "Trying handover"; + print message.pretty() + except Exception, e: + print e + print str(message).encode("hex") + + client = nfc.handover.HandoverClient(llc) + try: + summary("Trying to initiate NFC connection handover") client.connect() - print "Connected for handover" + summary("Connected for handover") except nfc.llcp.ConnectRefused: - print "Handover connection refused" - nfc.llcp.shutdown() + summary("Handover connection refused") + client.close() + return + except Exception, e: + summary("Other exception: " + str(e)) client.close() return - print "Sending handover request" + summary("Sending handover request") if not client.send(message): - print "Failed to send handover request" + summary("Failed to send handover request") + client.close() + return - print "Receiving handover response" + summary("Receiving handover response") message = client._recv() - print "Handover select received" - print message.pretty() - wpas_put_handover_sel(message) + if message is None: + summary("No response received") + client.close() + return + if message.type != "urn:nfc:wkt:Hs": + summary("Response was not Hs - received: " + message.type) + client.close() + return + + print "Received message" + try: + print message.pretty() + except Exception, e: + print e + print str(message).encode("hex") + message = nfc.ndef.HandoverSelectMessage(message) + summary("Handover select received") + try: + print message.pretty() + except Exception, e: + print e + + for carrier in message.carriers: + print "Remote carrier type: " + carrier.type + if carrier.type == "application/vnd.wfa.wsc": + print "WPS carrier type match - send to wpa_supplicant" + if "OK" in wpas_report_handover(data, carrier.record, "INIT"): + success_report("Handover reported successfully (initiator)") + else: + summary("Handover report rejected (initiator)") + # nfcpy does not support the new format.. + #wifi = nfc.ndef.WifiConfigRecord(carrier.record) + #print wifi.pretty() print "Remove peer" - nfc.llcp.shutdown() client.close() print "Done with handover" - - -def wps_tag_read(tag): + global only_one + if only_one: + global continue_loop + continue_loop = False + + global no_wait + if no_wait: + print "Trying to exit.." + global terminate_now + terminate_now = True + +def wps_tag_read(tag, wait_remove=True): + success = False if len(tag.ndef.message): - message = nfc.ndef.Message(tag.ndef.message) - print "message type " + message.type - - for record in message: + for record in tag.ndef.message: print "record type " + record.type if record.type == "application/vnd.wfa.wsc": - print "WPS tag - send to wpa_supplicant" - wpas_tag_read(tag.ndef.message) + summary("WPS tag - send to wpa_supplicant") + success = wpas_tag_read(tag.ndef.message) break else: - print "Empty tag" - - print "Remove tag" - while tag.is_present: + summary("Empty tag") + + if success: + success_report("Tag read succeeded") + + if wait_remove: + print "Remove tag" + while tag.is_present: + time.sleep(0.1) + + return success + + +def rdwr_connected_write(tag): + summary("Tag found - writing - " + str(tag)) + global write_data + tag.ndef.message = str(write_data) + success_report("Tag write succeeded") + print "Done - remove tag" + global only_one + if only_one: + global continue_loop + continue_loop = False + global write_wait_remove + while write_wait_remove and tag.is_present: time.sleep(0.1) +def wps_write_config_tag(clf, id=None, wait_remove=True): + print "Write WPS config token" + global write_data, write_wait_remove + write_wait_remove = wait_remove + write_data = wpas_get_config_token(id) + if write_data == None: + print "Could not get WPS config token from wpa_supplicant" + sys.exit(1) + return + print "Touch an NFC tag" + clf.connect(rdwr={'on-connect': rdwr_connected_write}) + + +def wps_write_er_config_tag(clf, uuid, wait_remove=True): + print "Write WPS ER config token" + global write_data, write_wait_remove + write_wait_remove = wait_remove + write_data = wpas_get_er_config_token(uuid) + if write_data == None: + print "Could not get WPS config token from wpa_supplicant" + return + + print "Touch an NFC tag" + clf.connect(rdwr={'on-connect': rdwr_connected_write}) + + +def wps_write_password_tag(clf, wait_remove=True): + print "Write WPS password token" + global write_data, write_wait_remove + write_wait_remove = wait_remove + write_data = wpas_get_password_token() + if write_data == None: + print "Could not get WPS password token from wpa_supplicant" + return + + print "Touch an NFC tag" + clf.connect(rdwr={'on-connect': rdwr_connected_write}) + + +def rdwr_connected(tag): + global only_one, no_wait + summary("Tag connected: " + str(tag)) + + if tag.ndef: + print "NDEF tag: " + tag.type + try: + print tag.ndef.message.pretty() + except Exception, e: + print e + success = wps_tag_read(tag, not only_one) + if only_one and success: + global continue_loop + continue_loop = False + else: + summary("Not an NDEF tag - remove tag") + return True + + return not no_wait + + +def llcp_worker(llc): + global arg_uuid + if arg_uuid is None: + wps_handover_init(llc) + print "Exiting llcp_worker thread" + return + + global srv + global wait_connection + while not wait_connection and srv.sent_carrier is None: + if srv.ho_server_processing: + time.sleep(0.025) + +def llcp_startup(clf, llc): + global arg_uuid + if arg_uuid: + print "Start LLCP server" + global srv + srv = HandoverServer(llc) + if arg_uuid is "ap": + print "Trying to handle WPS handover" + srv.uuid = None + else: + print "Trying to handle WPS handover with AP " + arg_uuid + srv.uuid = arg_uuid + return llc + +def llcp_connected(llc): + print "P2P LLCP connected" + global wait_connection + wait_connection = False + global arg_uuid + if arg_uuid: + global srv + srv.start() + else: + threading.Thread(target=llcp_worker, args=(llc,)).start() + print "llcp_connected returning" + return True + + +def terminate_loop(): + global terminate_now + return terminate_now def main(): clf = nfc.ContactlessFrontend() + parser = argparse.ArgumentParser(description='nfcpy to wpa_supplicant integration for WPS NFC operations') + parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO, + action='store_const', dest='loglevel', + help='verbose debug output') + parser.add_argument('-q', const=logging.WARNING, action='store_const', + dest='loglevel', help='be quiet') + parser.add_argument('--only-one', '-1', action='store_true', + help='run only one operation and exit') + parser.add_argument('--no-wait', action='store_true', + help='do not wait for tag to be removed before exiting') + parser.add_argument('--uuid', + help='UUID of an AP (used for WPS ER operations)') + parser.add_argument('--id', + help='network id (used for WPS ER operations)') + parser.add_argument('--summary', + help='summary file for writing status updates') + parser.add_argument('--success', + help='success file for writing success update') + parser.add_argument('command', choices=['write-config', + 'write-er-config', + 'write-password'], + nargs='?') + args = parser.parse_args() + + global arg_uuid + arg_uuid = args.uuid + + global only_one + only_one = args.only_one + + global no_wait + no_wait = args.no_wait + + if args.summary: + global summary_file + summary_file = args.summary + + if args.success: + global success_file + success_file = args.success + + logging.basicConfig(level=args.loglevel) + try: - while True: - print "Waiting for a tag or peer to be touched" + if not clf.open("usb"): + print "Could not open connection with an NFC device" + raise SystemExit - while True: - general_bytes = nfc.llcp.startup({}) - tag = clf.poll(general_bytes) - if tag == None: - continue + if args.command == "write-config": + wps_write_config_tag(clf, id=args.id, wait_remove=not args.no_wait) + raise SystemExit - if isinstance(tag, nfc.DEP): - wps_handover_init(tag) - break + if args.command == "write-er-config": + wps_write_er_config_tag(clf, args.uuid, wait_remove=not args.no_wait) + raise SystemExit - if tag.ndef: - wps_tag_read(tag) - break + if args.command == "write-password": + wps_write_password_tag(clf, wait_remove=not args.no_wait) + raise SystemExit - if tag: - print "Not an NDEF tag - remove tag" - while tag.is_present: - time.sleep(0.1) + global continue_loop + while continue_loop: + print "Waiting for a tag or peer to be touched" + wait_connection = True + try: + if not clf.connect(rdwr={'on-connect': rdwr_connected}, + llcp={'on-startup': llcp_startup, + 'on-connect': llcp_connected}, + terminate=terminate_loop): break + except Exception, e: + print "clf.connect failed" + + global srv + if only_one and srv and srv.success: + raise SystemExit except KeyboardInterrupt: raise SystemExit diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c index 27bcc7aa6931d..10ecce7b4d3d9 100644 --- a/wpa_supplicant/gas_query.c +++ b/wpa_supplicant/gas_query.c @@ -1,7 +1,8 @@ /* * Generic advertisement service (GAS) query * Copyright (c) 2009, Atheros Communications - * Copyright (c) 2011, Qualcomm Atheros + * Copyright (c) 2011-2014, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -13,6 +14,8 @@ #include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/gas.h" +#include "common/wpa_ctrl.h" +#include "rsn_supp/wpa.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "offchannel.h" @@ -20,7 +23,7 @@ /** GAS query timeout in seconds */ -#define GAS_QUERY_TIMEOUT_PERIOD 5 +#define GAS_QUERY_TIMEOUT_PERIOD 2 /** @@ -28,6 +31,7 @@ */ struct gas_query_pending { struct dl_list list; + struct gas_query *gas; u8 addr[ETH_ALEN]; u8 dialog_token; u8 next_frag_id; @@ -35,8 +39,10 @@ struct gas_query_pending { unsigned int offchannel_tx_started:1; int freq; u16 status_code; + struct wpabuf *req; struct wpabuf *adv_proto; struct wpabuf *resp; + struct os_reltime last_oper; void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, enum gas_query_result result, const struct wpabuf *adv_proto, @@ -50,6 +56,8 @@ struct gas_query_pending { struct gas_query { struct wpa_supplicant *wpa_s; struct dl_list pending; /* struct gas_query_pending */ + struct gas_query_pending *current; + struct wpa_radio_work *work; }; @@ -57,6 +65,16 @@ static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx); static void gas_query_timeout(void *eloop_data, void *user_ctx); +static int ms_from_time(struct os_reltime *last) +{ + struct os_reltime now, res; + + os_get_reltime(&now); + os_reltime_sub(&now, last, &res); + return res.sec * 1000 + res.usec / 1000; +} + + /** * gas_query_init - Initialize GAS query component * @wpa_s: Pointer to wpa_supplicant data @@ -77,10 +95,58 @@ struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s) } +static const char * gas_result_txt(enum gas_query_result result) +{ + switch (result) { + case GAS_QUERY_SUCCESS: + return "SUCCESS"; + case GAS_QUERY_FAILURE: + return "FAILURE"; + case GAS_QUERY_TIMEOUT: + return "TIMEOUT"; + case GAS_QUERY_PEER_ERROR: + return "PEER_ERROR"; + case GAS_QUERY_INTERNAL_ERROR: + return "INTERNAL_ERROR"; + case GAS_QUERY_CANCELLED: + return "CANCELLED"; + case GAS_QUERY_DELETED_AT_DEINIT: + return "DELETED_AT_DEINIT"; + } + + return "N/A"; +} + + +static void gas_query_free(struct gas_query_pending *query, int del_list) +{ + struct gas_query *gas = query->gas; + + if (del_list) + dl_list_del(&query->list); + + if (gas->work && gas->work->ctx == query) { + radio_work_done(gas->work); + gas->work = NULL; + } + + wpabuf_free(query->req); + wpabuf_free(query->adv_proto); + wpabuf_free(query->resp); + os_free(query); +} + + static void gas_query_done(struct gas_query *gas, struct gas_query_pending *query, enum gas_query_result result) { + wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR + " dialog_token=%u freq=%d status_code=%u result=%s", + MAC2STR(query->addr), query->dialog_token, query->freq, + query->status_code, gas_result_txt(result)); + if (gas->current == query) + gas->current = NULL; if (query->offchannel_tx_started) offchannel_send_action_done(gas->wpa_s); eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); @@ -88,9 +154,7 @@ static void gas_query_done(struct gas_query *gas, 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); + gas_query_free(query, 0); } @@ -138,17 +202,79 @@ static int gas_query_append(struct gas_query_pending *query, const u8 *data, } +static void gas_query_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) +{ + struct gas_query_pending *query; + struct gas_query *gas = wpa_s->gas; + int dur; + + if (gas->current == NULL) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: freq=%u dst=" + MACSTR " result=%d - no query in progress", + freq, MAC2STR(dst), result); + return; + } + + query = gas->current; + + dur = ms_from_time(&query->last_oper); + wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR + " result=%d query=%p dialog_token=%u dur=%d ms", + freq, MAC2STR(dst), result, query, query->dialog_token, dur); + if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination"); + return; + } + os_get_reltime(&query->last_oper); + + if (result == OFFCHANNEL_SEND_ACTION_SUCCESS) { + eloop_cancel_timeout(gas_query_timeout, gas, query); + eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, + gas_query_timeout, gas, query); + } + if (result == OFFCHANNEL_SEND_ACTION_FAILED) { + eloop_cancel_timeout(gas_query_timeout, gas, query); + eloop_register_timeout(0, 0, gas_query_timeout, gas, query); + } +} + + +static int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr) +{ + if (wpa_s->current_ssid == NULL || + wpa_s->wpa_state < WPA_4WAY_HANDSHAKE || + os_memcmp(addr, wpa_s->bssid, ETH_ALEN) != 0) + return 0; + return wpa_sm_pmf_enabled(wpa_s->wpa); +} + + static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query, struct wpabuf *req) { - int res; + unsigned int wait_time; + int res, prot = pmf_in_use(gas->wpa_s, query->addr); + wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u " - "freq=%d", MAC2STR(query->addr), - (unsigned int) wpabuf_len(req), query->freq); + "freq=%d prot=%d", MAC2STR(query->addr), + (unsigned int) wpabuf_len(req), query->freq, prot); + if (prot) { + u8 *categ = wpabuf_mhead_u8(req); + *categ = WLAN_ACTION_PROTECTED_DUAL; + } + os_get_reltime(&query->last_oper); + wait_time = 1000; + if (gas->wpa_s->max_remain_on_chan && + wait_time > gas->wpa_s->max_remain_on_chan) + wait_time = gas->wpa_s->max_remain_on_chan; 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); + wpabuf_head(req), wpabuf_len(req), + wait_time, gas_query_tx_status, 0); if (res == 0) query->offchannel_tx_started = 1; return res; @@ -271,6 +397,11 @@ static void gas_query_rx_comeback(struct gas_query *gas, if (frag_id != query->next_frag_id) { wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response " "from " MACSTR, MAC2STR(query->addr)); + if (frag_id + 1 == query->next_frag_id) { + wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible " + "retry of previous fragment"); + return; + } gas_query_done(gas, query, GAS_QUERY_PEER_ERROR); return; } @@ -291,27 +422,42 @@ static void gas_query_rx_comeback(struct gas_query *gas, /** - * gas_query_rx - Indicate reception of a Public Action frame + * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame * @gas: GAS query data from gas_query_init() * @da: Destination MAC address of the Action frame * @sa: Source MAC address of the Action frame * @bssid: BSSID of the Action frame + * @categ: Category of the Action frame * @data: Payload of the Action frame * @len: Length of @data * @freq: Frequency (in MHz) on which the frame was received * Returns: 0 if the Public Action frame was a GAS frame or -1 if not */ 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) + const u8 *bssid, u8 categ, 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; + int prot, pmf; + unsigned int left; if (gas == NULL || len < 4) return -1; + prot = categ == WLAN_ACTION_PROTECTED_DUAL; + pmf = pmf_in_use(gas->wpa_s, bssid); + if (prot && !pmf) { + wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled"); + return 0; + } + if (!prot && pmf) { + wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled"); + return 0; + } + pos = data; action = *pos++; dialog_token = *pos++; @@ -327,6 +473,9 @@ int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, return -1; } + wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR, + ms_from_time(&query->last_oper), MAC2STR(sa)); + 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 " @@ -344,7 +493,10 @@ int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, query->status_code = WPA_GET_LE16(pos); pos += 2; - if (query->status_code != WLAN_STATUS_SUCCESS) { + if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING && + action == WLAN_PA_GAS_COMEBACK_RESP) { + wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response"); + } else 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); @@ -392,17 +544,17 @@ int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa, resp_len = WPA_GET_LE16(pos); pos += 2; - if (pos + resp_len > data + len) { + left = data + len - pos; + if (resp_len > left) { wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in " "response from " MACSTR, MAC2STR(sa)); return 0; } - if (pos + resp_len < data + len) { + if (resp_len < left) { 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)); + left - resp_len, MAC2STR(sa)); } if (action == WLAN_PA_GAS_COMEBACK_RESP) @@ -421,8 +573,9 @@ 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)); + wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR + " dialog token %u", + MAC2STR(query->addr), query->dialog_token); gas_query_done(gas, query, GAS_QUERY_TIMEOUT); } @@ -441,12 +594,56 @@ static int gas_query_dialog_token_available(struct gas_query *gas, } +static void gas_query_start_cb(struct wpa_radio_work *work, int deinit) +{ + struct gas_query_pending *query = work->ctx; + struct gas_query *gas = query->gas; + struct wpa_supplicant *wpa_s = gas->wpa_s; + + if (deinit) { + if (work->started) { + gas->work = NULL; + gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT); + return; + } + + gas_query_free(query, 1); + return; + } + + if (wpas_update_random_addr_disassoc(wpa_s) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "Failed to assign random MAC address for GAS"); + gas_query_free(query, 1); + radio_work_done(work); + return; + } + + gas->work = work; + + if (gas_query_tx(gas, query, query->req) < 0) { + wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " + MACSTR, MAC2STR(query->addr)); + gas_query_free(query, 1); + return; + } + gas->current = query; + + wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u", + query->dialog_token); + eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, + gas_query_timeout, gas, query); + +} + + /** * gas_query_req - Request a GAS query * @gas: GAS query data from gas_query_init() * @dst: Destination MAC address for the query * @freq: Frequency (in MHz) for the channel on which to send the query - * @req: GAS query payload + * @req: GAS query payload (to be freed by gas_query module in case of success + * return) * @cb: Callback function for reporting GAS query result and response * @ctx: Context pointer to use with the @cb call * Returns: dialog token (>= 0) on success or -1 on failure @@ -461,43 +658,46 @@ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq, { struct gas_query_pending *query; int dialog_token; + static int next_start = 0; 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)) + if (gas_query_dialog_token_available( + gas, dst, (next_start + dialog_token) % 256)) break; } if (dialog_token == 256) return -1; /* Too many pending queries */ + dialog_token = (next_start + dialog_token) % 256; + next_start = (dialog_token + 1) % 256; query = os_zalloc(sizeof(*query)); if (query == NULL) return -1; + query->gas = gas; os_memcpy(query->addr, dst, ETH_ALEN); query->dialog_token = dialog_token; query->freq = freq; query->cb = cb; query->ctx = ctx; + query->req = req; 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)); - dl_list_del(&query->list); - os_free(query); + wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_START "addr=" MACSTR + " dialog_token=%u freq=%d", + MAC2STR(query->addr), query->dialog_token, query->freq); + + if (radio_add_work(gas->wpa_s, freq, "gas-query", 0, gas_query_start_cb, + query) < 0) { + gas_query_free(query, 1); return -1; } - eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, gas_query_timeout, - gas, query); - return dialog_token; } diff --git a/wpa_supplicant/gas_query.h b/wpa_supplicant/gas_query.h index 5c3d161adfb15..ad1349088ee1f 100644 --- a/wpa_supplicant/gas_query.h +++ b/wpa_supplicant/gas_query.h @@ -17,7 +17,8 @@ struct gas_query; 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); + const u8 *bssid, u8 categ, const u8 *data, size_t len, + int freq); /** * enum gas_query_result - GAS query result diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c index 14042419bebd7..b9cd68193b2d8 100644 --- a/wpa_supplicant/hs20_supplicant.c +++ b/wpa_supplicant/hs20_supplicant.c @@ -1,12 +1,13 @@ /* * Copyright (c) 2009, Atheros Communications, Inc. - * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "includes.h" +#include <sys/stat.h> #include "common.h" #include "eloop.h" @@ -14,34 +15,126 @@ #include "common/ieee802_11_defs.h" #include "common/gas.h" #include "common/wpa_ctrl.h" +#include "rsn_supp/wpa.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "config.h" +#include "scan.h" #include "bss.h" +#include "blacklist.h" #include "gas_query.h" #include "interworking.h" #include "hs20_supplicant.h" -void wpas_hs20_add_indication(struct wpabuf *buf) +#define OSU_MAX_ITEMS 10 + +struct osu_lang_string { + char lang[4]; + char text[253]; +}; + +struct osu_icon { + u16 width; + u16 height; + char lang[4]; + char icon_type[256]; + char filename[256]; + unsigned int id; + unsigned int failed:1; +}; + +struct osu_provider { + u8 bssid[ETH_ALEN]; + u8 osu_ssid[32]; + u8 osu_ssid_len; + char server_uri[256]; + u32 osu_methods; /* bit 0 = OMA-DM, bit 1 = SOAP-XML SPP */ + char osu_nai[256]; + struct osu_lang_string friendly_name[OSU_MAX_ITEMS]; + size_t friendly_name_count; + struct osu_lang_string serv_desc[OSU_MAX_ITEMS]; + size_t serv_desc_count; + struct osu_icon icon[OSU_MAX_ITEMS]; + size_t icon_count; +}; + + +void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id) { + u8 conf; + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); - wpabuf_put_u8(buf, 5); + wpabuf_put_u8(buf, pps_mo_id >= 0 ? 7 : 5); wpabuf_put_be24(buf, OUI_WFA); wpabuf_put_u8(buf, HS20_INDICATION_OUI_TYPE); - wpabuf_put_u8(buf, 0x00); /* Hotspot Configuration */ + conf = HS20_VERSION; + if (pps_mo_id >= 0) + conf |= HS20_PPS_MO_ID_PRESENT; + wpabuf_put_u8(buf, conf); + if (pps_mo_id >= 0) + wpabuf_put_le16(buf, pps_mo_id); } -struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, - size_t payload_len) +int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + struct wpa_bss *bss) +{ + if (!wpa_s->conf->hs20 || !ssid) + return 0; + + if (ssid->parent_cred) + return 1; + + if (bss && !wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) + return 0; + + /* + * This may catch some non-Hotspot 2.0 cases, but it is safer to do that + * than cause Hotspot 2.0 connections without indication element getting + * added. Non-Hotspot 2.0 APs should ignore the unknown vendor element. + */ + + if (!(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X)) + return 0; + if (!(ssid->pairwise_cipher & WPA_CIPHER_CCMP)) + return 0; + if (ssid->proto != WPA_PROTO_RSN) + return 0; + + return 1; +} + + +int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) +{ + struct wpa_cred *cred; + + if (ssid == NULL) + return 0; + + if (ssid->update_identifier) + return ssid->update_identifier; + + if (ssid->parent_cred == NULL) + return 0; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + if (ssid->parent_cred == cred) + return cred->update_identifier; + } + + return 0; +} + + +void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len, + struct wpabuf *buf) { - struct wpabuf *buf; u8 *len_pos; - buf = gas_anqp_build_initial_req(0, 100 + payload_len); if (buf == NULL) - return NULL; + return; len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); wpabuf_put_be24(buf, OUI_WFA); @@ -51,6 +144,11 @@ struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, wpabuf_put_u8(buf, 0); /* Reserved */ if (payload) wpabuf_put_data(buf, payload, payload_len); + } else if (stypes == BIT(HS20_STYPE_ICON_REQUEST)) { + wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST); + wpabuf_put_u8(buf, 0); /* Reserved */ + if (payload) + wpabuf_put_data(buf, payload, payload_len); } else { u8 i; wpabuf_put_u8(buf, HS20_STYPE_QUERY_LIST); @@ -63,6 +161,19 @@ struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, gas_anqp_set_element_len(buf, len_pos); gas_anqp_set_len(buf); +} + + +struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, + size_t payload_len) +{ + struct wpabuf *buf; + + buf = gas_anqp_build_initial_req(0, 100 + payload_len); + if (buf == NULL) + return NULL; + + hs20_put_anqp_req(stypes, payload, payload_len, buf); return buf; } @@ -96,23 +207,161 @@ int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, 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"); + wpabuf_free(buf); ret = -1; } else wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " "%u", res); - wpabuf_free(buf); return ret; } +static void hs20_set_osu_access_permission(const char *osu_dir, + const char *fname) +{ + struct stat statbuf; + + /* Get OSU directory information */ + if (stat(osu_dir, &statbuf) < 0) { + wpa_printf(MSG_WARNING, "Cannot stat the OSU directory %s", + osu_dir); + return; + } + + if (chmod(fname, statbuf.st_mode) < 0) { + wpa_printf(MSG_WARNING, + "Cannot change the permissions for %s", fname); + return; + } + + if (chown(fname, statbuf.st_uid, statbuf.st_gid) < 0) { + wpa_printf(MSG_WARNING, "Cannot change the ownership for %s", + fname); + } +} + +static int hs20_process_icon_binary_file(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *pos, + size_t slen) +{ + char fname[256]; + int png; + FILE *f; + u16 data_len; + + wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR " Icon Binary File", + MAC2STR(sa)); + + if (slen < 4) { + wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File " + "value from " MACSTR, MAC2STR(sa)); + return -1; + } + + wpa_printf(MSG_DEBUG, "HS 2.0: Download Status Code %u", *pos); + if (*pos != 0) + return -1; + pos++; + slen--; + + if ((size_t) 1 + pos[0] > slen) { + wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File " + "value from " MACSTR, MAC2STR(sa)); + return -1; + } + wpa_hexdump_ascii(MSG_DEBUG, "Icon Type", pos + 1, pos[0]); + png = os_strncasecmp((char *) pos + 1, "image/png", 9) == 0; + slen -= 1 + pos[0]; + pos += 1 + pos[0]; + + if (slen < 2) { + wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File " + "value from " MACSTR, MAC2STR(sa)); + return -1; + } + data_len = WPA_GET_LE16(pos); + pos += 2; + slen -= 2; + + if (data_len > slen) { + wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File " + "value from " MACSTR, MAC2STR(sa)); + return -1; + } + + wpa_printf(MSG_DEBUG, "Icon Binary Data: %u bytes", data_len); + if (wpa_s->conf->osu_dir == NULL) + return -1; + + wpa_s->osu_icon_id++; + if (wpa_s->osu_icon_id == 0) + wpa_s->osu_icon_id++; + snprintf(fname, sizeof(fname), "%s/osu-icon-%u.%s", + wpa_s->conf->osu_dir, wpa_s->osu_icon_id, + png ? "png" : "icon"); + f = fopen(fname, "wb"); + if (f == NULL) + return -1; + + hs20_set_osu_access_permission(wpa_s->conf->osu_dir, fname); + + if (fwrite(pos, slen, 1, f) != 1) { + fclose(f); + unlink(fname); + return -1; + } + fclose(f); + + wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP-ICON %s", fname); + return 0; +} + + +static void hs20_continue_icon_fetch(void *eloop_ctx, void *sock_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + if (wpa_s->fetch_osu_icon_in_progress) + hs20_next_osu_icon(wpa_s); +} + + +static void hs20_osu_icon_fetch_result(struct wpa_supplicant *wpa_s, int res) +{ + size_t i, j; + struct os_reltime now, tmp; + int dur; + + os_get_reltime(&now); + os_reltime_sub(&now, &wpa_s->osu_icon_fetch_start, &tmp); + dur = tmp.sec * 1000 + tmp.usec / 1000; + wpa_printf(MSG_DEBUG, "HS 2.0: Icon fetch dur=%d ms res=%d", + dur, res); + + for (i = 0; i < wpa_s->osu_prov_count; i++) { + struct osu_provider *osu = &wpa_s->osu_prov[i]; + for (j = 0; j < osu->icon_count; j++) { + struct osu_icon *icon = &osu->icon[j]; + if (icon->id || icon->failed) + continue; + if (res < 0) + icon->failed = 1; + else + icon->id = wpa_s->osu_icon_id; + return; + } + } +} + + void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, - const u8 *sa, const u8 *data, size_t slen) + struct wpa_bss *bss, const u8 *sa, + const u8 *data, size_t slen) { const u8 *pos = data; u8 subtype; - struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa); struct wpa_bss_anqp *anqp = NULL; + int ret; if (slen < 2) return; @@ -131,6 +380,11 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR " HS Capability List", MAC2STR(sa)); wpa_hexdump_ascii(MSG_DEBUG, "HS Capability List", pos, slen); + if (anqp) { + wpabuf_free(anqp->hs20_capability_list); + anqp->hs20_capability_list = + wpabuf_alloc_copy(pos, slen); + } break; case HS20_STYPE_OPERATOR_FRIENDLY_NAME: wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR @@ -178,8 +432,576 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, wpabuf_alloc_copy(pos, slen); } break; + case HS20_STYPE_OSU_PROVIDERS_LIST: + wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR + " OSU Providers list", MAC2STR(sa)); + wpa_s->num_prov_found++; + if (anqp) { + wpabuf_free(anqp->hs20_osu_providers_list); + anqp->hs20_osu_providers_list = + wpabuf_alloc_copy(pos, slen); + } + break; + case HS20_STYPE_ICON_BINARY_FILE: + ret = hs20_process_icon_binary_file(wpa_s, sa, pos, slen); + if (wpa_s->fetch_osu_icon_in_progress) { + hs20_osu_icon_fetch_result(wpa_s, ret); + eloop_cancel_timeout(hs20_continue_icon_fetch, + wpa_s, NULL); + eloop_register_timeout(0, 0, hs20_continue_icon_fetch, + wpa_s, NULL); + } + break; default: wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype); break; } } + + +void hs20_notify_parse_done(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->fetch_osu_icon_in_progress) + return; + if (eloop_is_timeout_registered(hs20_continue_icon_fetch, wpa_s, NULL)) + return; + /* + * We are going through icon fetch, but no icon response was received. + * Assume this means the current AP could not provide an answer to avoid + * getting stuck in fetch iteration. + */ + hs20_icon_fetch_failed(wpa_s); +} + + +static void hs20_free_osu_prov_entry(struct osu_provider *prov) +{ +} + + +void hs20_free_osu_prov(struct wpa_supplicant *wpa_s) +{ + size_t i; + for (i = 0; i < wpa_s->osu_prov_count; i++) + hs20_free_osu_prov_entry(&wpa_s->osu_prov[i]); + os_free(wpa_s->osu_prov); + wpa_s->osu_prov = NULL; + wpa_s->osu_prov_count = 0; +} + + +static void hs20_osu_fetch_done(struct wpa_supplicant *wpa_s) +{ + char fname[256]; + FILE *f; + size_t i, j; + + wpa_s->fetch_osu_info = 0; + wpa_s->fetch_osu_icon_in_progress = 0; + + if (wpa_s->conf->osu_dir == NULL) { + hs20_free_osu_prov(wpa_s); + wpa_s->fetch_anqp_in_progress = 0; + return; + } + + snprintf(fname, sizeof(fname), "%s/osu-providers.txt", + wpa_s->conf->osu_dir); + f = fopen(fname, "w"); + if (f == NULL) { + hs20_free_osu_prov(wpa_s); + return; + } + + hs20_set_osu_access_permission(wpa_s->conf->osu_dir, fname); + + for (i = 0; i < wpa_s->osu_prov_count; i++) { + struct osu_provider *osu = &wpa_s->osu_prov[i]; + if (i > 0) + fprintf(f, "\n"); + fprintf(f, "OSU-PROVIDER " MACSTR "\n" + "uri=%s\n" + "methods=%08x\n", + MAC2STR(osu->bssid), osu->server_uri, osu->osu_methods); + if (osu->osu_ssid_len) { + fprintf(f, "osu_ssid=%s\n", + wpa_ssid_txt(osu->osu_ssid, + osu->osu_ssid_len)); + } + if (osu->osu_nai[0]) + fprintf(f, "osu_nai=%s\n", osu->osu_nai); + for (j = 0; j < osu->friendly_name_count; j++) { + fprintf(f, "friendly_name=%s:%s\n", + osu->friendly_name[j].lang, + osu->friendly_name[j].text); + } + for (j = 0; j < osu->serv_desc_count; j++) { + fprintf(f, "desc=%s:%s\n", + osu->serv_desc[j].lang, + osu->serv_desc[j].text); + } + for (j = 0; j < osu->icon_count; j++) { + struct osu_icon *icon = &osu->icon[j]; + if (icon->failed) + continue; /* could not fetch icon */ + fprintf(f, "icon=%u:%u:%u:%s:%s:%s\n", + icon->id, icon->width, icon->height, icon->lang, + icon->icon_type, icon->filename); + } + } + fclose(f); + hs20_free_osu_prov(wpa_s); + + wpa_msg(wpa_s, MSG_INFO, "OSU provider fetch completed"); + wpa_s->fetch_anqp_in_progress = 0; +} + + +void hs20_next_osu_icon(struct wpa_supplicant *wpa_s) +{ + size_t i, j; + + wpa_printf(MSG_DEBUG, "HS 2.0: Ready to fetch next icon"); + + for (i = 0; i < wpa_s->osu_prov_count; i++) { + struct osu_provider *osu = &wpa_s->osu_prov[i]; + for (j = 0; j < osu->icon_count; j++) { + struct osu_icon *icon = &osu->icon[j]; + if (icon->id || icon->failed) + continue; + + wpa_printf(MSG_DEBUG, "HS 2.0: Try to fetch icon '%s' " + "from " MACSTR, icon->filename, + MAC2STR(osu->bssid)); + os_get_reltime(&wpa_s->osu_icon_fetch_start); + if (hs20_anqp_send_req(wpa_s, osu->bssid, + BIT(HS20_STYPE_ICON_REQUEST), + (u8 *) icon->filename, + os_strlen(icon->filename)) < 0) { + icon->failed = 1; + continue; + } + return; + } + } + + wpa_printf(MSG_DEBUG, "HS 2.0: No more icons to fetch"); + hs20_osu_fetch_done(wpa_s); +} + + +static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + const u8 *osu_ssid, u8 osu_ssid_len, + const u8 *pos, size_t len) +{ + struct osu_provider *prov; + const u8 *end = pos + len; + u16 len2; + const u8 *pos2; + u8 uri_len, osu_method_len, osu_nai_len; + + wpa_hexdump(MSG_DEBUG, "HS 2.0: Parsing OSU Provider", pos, len); + prov = os_realloc_array(wpa_s->osu_prov, + wpa_s->osu_prov_count + 1, + sizeof(*prov)); + if (prov == NULL) + return; + wpa_s->osu_prov = prov; + prov = &prov[wpa_s->osu_prov_count]; + os_memset(prov, 0, sizeof(*prov)); + + os_memcpy(prov->bssid, bss->bssid, ETH_ALEN); + os_memcpy(prov->osu_ssid, osu_ssid, osu_ssid_len); + prov->osu_ssid_len = osu_ssid_len; + + /* OSU Friendly Name Length */ + if (pos + 2 > end) { + wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU " + "Friendly Name Length"); + return; + } + len2 = WPA_GET_LE16(pos); + pos += 2; + if (len2 > end - pos) { + wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU " + "Friendly Name Duples"); + return; + } + pos2 = pos; + pos += len2; + + /* OSU Friendly Name Duples */ + while (pos2 + 4 <= pos && prov->friendly_name_count < OSU_MAX_ITEMS) { + struct osu_lang_string *f; + if (pos2 + 1 + pos2[0] > pos || pos2[0] < 3) { + wpa_printf(MSG_DEBUG, "Invalid OSU Friendly Name"); + break; + } + f = &prov->friendly_name[prov->friendly_name_count++]; + os_memcpy(f->lang, pos2 + 1, 3); + os_memcpy(f->text, pos2 + 1 + 3, pos2[0] - 3); + pos2 += 1 + pos2[0]; + } + + /* OSU Server URI */ + if (pos + 1 > end) { + wpa_printf(MSG_DEBUG, + "HS 2.0: Not enough room for OSU Server URI length"); + return; + } + uri_len = *pos++; + if (uri_len > end - pos) { + wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Server " + "URI"); + return; + } + os_memcpy(prov->server_uri, pos, uri_len); + pos += uri_len; + + /* OSU Method list */ + if (pos + 1 > end) { + wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method " + "list length"); + return; + } + osu_method_len = pos[0]; + if (osu_method_len > end - pos - 1) { + wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method " + "list"); + return; + } + pos2 = pos + 1; + pos += 1 + osu_method_len; + while (pos2 < pos) { + if (*pos2 < 32) + prov->osu_methods |= BIT(*pos2); + pos2++; + } + + /* Icons Available Length */ + if (pos + 2 > end) { + wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons " + "Available Length"); + return; + } + len2 = WPA_GET_LE16(pos); + pos += 2; + if (len2 > end - pos) { + wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons " + "Available"); + return; + } + pos2 = pos; + pos += len2; + + /* Icons Available */ + while (pos2 < pos) { + struct osu_icon *icon = &prov->icon[prov->icon_count]; + u8 flen; + + if (pos2 + 2 + 2 + 3 + 1 + 1 > pos) { + wpa_printf(MSG_DEBUG, "HS 2.0: Invalid Icon Metadata"); + break; + } + + icon->width = WPA_GET_LE16(pos2); + pos2 += 2; + icon->height = WPA_GET_LE16(pos2); + pos2 += 2; + os_memcpy(icon->lang, pos2, 3); + pos2 += 3; + + flen = pos2[0]; + if (flen > pos - pos2 - 1) { + wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon Type"); + break; + } + os_memcpy(icon->icon_type, pos2 + 1, flen); + pos2 += 1 + flen; + + if (pos2 + 1 > pos) { + wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon " + "Filename length"); + break; + } + flen = pos2[0]; + if (flen > pos - pos2 - 1) { + wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon " + "Filename"); + break; + } + os_memcpy(icon->filename, pos2 + 1, flen); + pos2 += 1 + flen; + + prov->icon_count++; + } + + /* OSU_NAI */ + if (pos + 1 > end) { + wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI"); + return; + } + osu_nai_len = pos[0]; + if (osu_nai_len > end - pos - 1) { + wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI"); + return; + } + os_memcpy(prov->osu_nai, pos + 1, osu_nai_len); + pos += 1 + osu_nai_len; + + /* OSU Service Description Length */ + if (pos + 2 > end) { + wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU " + "Service Description Length"); + return; + } + len2 = WPA_GET_LE16(pos); + pos += 2; + if (len2 > end - pos) { + wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU " + "Service Description Duples"); + return; + } + pos2 = pos; + pos += len2; + + /* OSU Service Description Duples */ + while (pos2 + 4 <= pos && prov->serv_desc_count < OSU_MAX_ITEMS) { + struct osu_lang_string *f; + u8 descr_len; + + descr_len = pos2[0]; + if (descr_len > pos - pos2 - 1 || descr_len < 3) { + wpa_printf(MSG_DEBUG, "Invalid OSU Service " + "Description"); + break; + } + f = &prov->serv_desc[prov->serv_desc_count++]; + os_memcpy(f->lang, pos2 + 1, 3); + os_memcpy(f->text, pos2 + 1 + 3, descr_len - 3); + pos2 += 1 + descr_len; + } + + wpa_printf(MSG_DEBUG, "HS 2.0: Added OSU Provider through " MACSTR, + MAC2STR(bss->bssid)); + wpa_s->osu_prov_count++; +} + + +void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss *bss; + struct wpabuf *prov_anqp; + const u8 *pos, *end; + u16 len; + const u8 *osu_ssid; + u8 osu_ssid_len; + u8 num_providers; + + hs20_free_osu_prov(wpa_s); + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (bss->anqp == NULL) + continue; + prov_anqp = bss->anqp->hs20_osu_providers_list; + if (prov_anqp == NULL) + continue; + wpa_printf(MSG_DEBUG, "HS 2.0: Parsing OSU Providers list from " + MACSTR, MAC2STR(bss->bssid)); + wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers list", + prov_anqp); + pos = wpabuf_head(prov_anqp); + end = pos + wpabuf_len(prov_anqp); + + /* OSU SSID */ + if (pos + 1 > end) + continue; + if (pos + 1 + pos[0] > end) { + wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for " + "OSU SSID"); + continue; + } + osu_ssid_len = *pos++; + if (osu_ssid_len > 32) { + wpa_printf(MSG_DEBUG, "HS 2.0: Invalid OSU SSID " + "Length %u", osu_ssid_len); + continue; + } + osu_ssid = pos; + pos += osu_ssid_len; + + if (pos + 1 > end) { + wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for " + "Number of OSU Providers"); + continue; + } + num_providers = *pos++; + wpa_printf(MSG_DEBUG, "HS 2.0: Number of OSU Providers: %u", + num_providers); + + /* OSU Providers */ + while (pos + 2 < end && num_providers > 0) { + num_providers--; + len = WPA_GET_LE16(pos); + pos += 2; + if (len > (unsigned int) (end - pos)) + break; + hs20_osu_add_prov(wpa_s, bss, osu_ssid, + osu_ssid_len, pos, len); + pos += len; + } + + if (pos != end) { + wpa_printf(MSG_DEBUG, "HS 2.0: Ignored %d bytes of " + "extra data after OSU Providers", + (int) (end - pos)); + } + } + + wpa_s->fetch_osu_icon_in_progress = 1; + hs20_next_osu_icon(wpa_s); +} + + +static void hs20_osu_scan_res_handler(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + wpa_printf(MSG_DEBUG, "OSU provisioning fetch scan completed"); + if (!wpa_s->fetch_osu_waiting_scan) { + wpa_printf(MSG_DEBUG, "OSU fetch have been canceled"); + return; + } + wpa_s->network_select = 0; + wpa_s->fetch_all_anqp = 1; + wpa_s->fetch_osu_info = 1; + wpa_s->fetch_osu_icon_in_progress = 0; + + interworking_start_fetch_anqp(wpa_s); +} + + +int hs20_fetch_osu(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { + wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - " + "interface disabled"); + return -1; + } + + if (wpa_s->scanning) { + wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - " + "scanning"); + return -1; + } + + if (wpa_s->conf->osu_dir == NULL) { + wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - " + "osu_dir not configured"); + return -1; + } + + if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) { + wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - " + "fetch in progress (%d, %d)", + wpa_s->fetch_anqp_in_progress, + wpa_s->network_select); + return -1; + } + + wpa_msg(wpa_s, MSG_INFO, "Starting OSU provisioning information fetch"); + wpa_s->num_osu_scans = 0; + wpa_s->num_prov_found = 0; + hs20_start_osu_scan(wpa_s); + + return 0; +} + + +void hs20_start_osu_scan(struct wpa_supplicant *wpa_s) +{ + wpa_s->fetch_osu_waiting_scan = 1; + wpa_s->num_osu_scans++; + wpa_s->scan_req = MANUAL_SCAN_REQ; + wpa_s->scan_res_handler = hs20_osu_scan_res_handler; + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s) +{ + wpa_printf(MSG_DEBUG, "Cancel OSU fetch"); + interworking_stop_fetch_anqp(wpa_s); + wpa_s->fetch_osu_waiting_scan = 0; + wpa_s->network_select = 0; + wpa_s->fetch_osu_info = 0; + wpa_s->fetch_osu_icon_in_progress = 0; +} + + +void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s) +{ + hs20_osu_icon_fetch_result(wpa_s, -1); + eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL); + eloop_register_timeout(0, 0, hs20_continue_icon_fetch, wpa_s, NULL); +} + + +void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s, + const char *url, u8 osu_method) +{ + if (url) + wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION "%u %s", + osu_method, url); + else + wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION); +} + + +void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code, + u16 reauth_delay, const char *url) +{ + if (!wpa_sm_pmf_enabled(wpa_s->wpa)) { + wpa_printf(MSG_DEBUG, "HS 2.0: Ignore deauthentication imminent notice since PMF was not enabled"); + return; + } + + wpa_msg(wpa_s, MSG_INFO, HS20_DEAUTH_IMMINENT_NOTICE "%u %u %s", + code, reauth_delay, url); + + if (code == HS20_DEAUTH_REASON_CODE_BSS) { + wpa_printf(MSG_DEBUG, "HS 2.0: Add BSS to blacklist"); + wpa_blacklist_add(wpa_s, wpa_s->bssid); + /* TODO: For now, disable full ESS since some drivers may not + * support disabling per BSS. */ + if (wpa_s->current_ssid) { + struct os_reltime now; + os_get_reltime(&now); + if (now.sec + reauth_delay <= + wpa_s->current_ssid->disabled_until.sec) + return; + wpa_printf(MSG_DEBUG, "HS 2.0: Disable network for %u seconds (BSS)", + reauth_delay); + wpa_s->current_ssid->disabled_until.sec = + now.sec + reauth_delay; + } + } + + if (code == HS20_DEAUTH_REASON_CODE_ESS && wpa_s->current_ssid) { + struct os_reltime now; + os_get_reltime(&now); + if (now.sec + reauth_delay <= + wpa_s->current_ssid->disabled_until.sec) + return; + wpa_printf(MSG_DEBUG, "HS 2.0: Disable network for %u seconds", + reauth_delay); + wpa_s->current_ssid->disabled_until.sec = + now.sec + reauth_delay; + } +} + + +void hs20_deinit(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL); + hs20_free_osu_prov(wpa_s); +} diff --git a/wpa_supplicant/hs20_supplicant.h b/wpa_supplicant/hs20_supplicant.h index 6eb3926d34f8e..85b512012a975 100644 --- a/wpa_supplicant/hs20_supplicant.h +++ b/wpa_supplicant/hs20_supplicant.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -8,13 +8,34 @@ #ifndef HS20_SUPPLICANT_H #define HS20_SUPPLICANT_H -void wpas_hs20_add_indication(struct wpabuf *buf); +void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id); int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, const u8 *payload, size_t payload_len); struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload, size_t payload_len); +void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len, + struct wpabuf *buf); void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, - const u8 *sa, const u8 *data, size_t slen); + struct wpa_bss *bss, const u8 *sa, + const u8 *data, size_t slen); +int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + struct wpa_bss *bss); +int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +void hs20_notify_parse_done(struct wpa_supplicant *wpa_s); + +void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s, + const char *url, u8 osu_method); +void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code, + u16 reauth_delay, const char *url); + +void hs20_free_osu_prov(struct wpa_supplicant *wpa_s); +void hs20_next_osu_icon(struct wpa_supplicant *wpa_s); +void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s); +int hs20_fetch_osu(struct wpa_supplicant *wpa_s); +void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s); +void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s); +void hs20_start_osu_scan(struct wpa_supplicant *wpa_s); +void hs20_deinit(struct wpa_supplicant *wpa_s); #endif /* HS20_SUPPLICANT_H */ diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c index 046f181f031ec..d0ae135bdf725 100644 --- a/wpa_supplicant/ibss_rsn.c +++ b/wpa_supplicant/ibss_rsn.c @@ -1,6 +1,6 @@ /* * wpa_supplicant - IBSS RSN - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009-2013, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,15 +9,21 @@ #include "includes.h" #include "common.h" +#include "common/wpa_ctrl.h" +#include "utils/eloop.h" #include "l2_packet/l2_packet.h" #include "rsn_supp/wpa.h" #include "rsn_supp/wpa_ie.h" #include "ap/wpa_auth.h" #include "wpa_supplicant_i.h" #include "driver_i.h" +#include "common/ieee802_11_defs.h" #include "ibss_rsn.h" +static void ibss_rsn_auth_timeout(void *eloop_ctx, void *timeout_ctx); + + static struct ibss_rsn_peer * ibss_rsn_get_peer(struct ibss_rsn *ibss_rsn, const u8 *addr) { @@ -32,6 +38,7 @@ static struct ibss_rsn_peer * ibss_rsn_get_peer(struct ibss_rsn *ibss_rsn, static void ibss_rsn_free(struct ibss_rsn_peer *peer) { + eloop_cancel_timeout(ibss_rsn_auth_timeout, peer, NULL); wpa_auth_sta_deinit(peer->auth); wpa_sm_deinit(peer->supp); os_free(peer); @@ -65,7 +72,7 @@ static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf, if (wpa_s->l2) return l2_packet_send(wpa_s->l2, dest, proto, buf, len); - return wpa_drv_send_eapol(wpa_s, dest, proto, buf, len); + return -1; } @@ -113,6 +120,22 @@ static int supp_get_beacon_ie(void *ctx) } +static void ibss_check_rsn_completed(struct ibss_rsn_peer *peer) +{ + struct wpa_supplicant *wpa_s = peer->ibss_rsn->wpa_s; + + if ((peer->authentication_status & + (IBSS_RSN_SET_PTK_SUPP | IBSS_RSN_SET_PTK_AUTH)) != + (IBSS_RSN_SET_PTK_SUPP | IBSS_RSN_SET_PTK_AUTH)) + return; + if (peer->authentication_status & IBSS_RSN_REPORTED_PTK) + return; + peer->authentication_status |= IBSS_RSN_REPORTED_PTK; + wpa_msg(wpa_s, MSG_INFO, IBSS_RSN_COMPLETED MACSTR, + MAC2STR(peer->addr)); +} + + static int supp_set_key(void *ctx, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, @@ -127,6 +150,8 @@ static int supp_set_key(void *ctx, enum wpa_alg alg, wpa_hexdump_key(MSG_DEBUG, "SUPP: set_key - key", key, key_len); if (key_idx == 0) { + peer->authentication_status |= IBSS_RSN_SET_PTK_SUPP; + ibss_check_rsn_completed(peer); /* * In IBSS RSN, the pairwise key from the 4-way handshake * initiated by the peer with highest MAC address is used. @@ -205,7 +230,7 @@ static int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr, wpa_sm_set_param(peer->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP); wpa_sm_set_param(peer->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP); wpa_sm_set_param(peer->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK); - wpa_sm_set_pmk(peer->supp, psk, PMK_LEN); + wpa_sm_set_pmk(peer->supp, psk, PMK_LEN, NULL); peer->supp_ie_len = sizeof(peer->supp_ie); if (wpa_sm_set_assoc_wpa_ie_default(peer->supp, peer->supp_ie, @@ -232,7 +257,8 @@ static void auth_logger(void *ctx, const u8 *addr, logger_level level, } -static const u8 * auth_get_psk(void *ctx, const u8 *addr, const u8 *prev_psk) +static const u8 * auth_get_psk(void *ctx, const u8 *addr, + const u8 *p2p_dev_addr, const u8 *prev_psk) { struct ibss_rsn *ibss_rsn = ctx; wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)", @@ -257,7 +283,7 @@ static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data, return l2_packet_send(wpa_s->l2, addr, ETH_P_EAPOL, data, data_len); - return wpa_drv_send_eapol(wpa_s, addr, ETH_P_EAPOL, data, data_len); + return -1; } @@ -280,6 +306,15 @@ static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len); if (idx == 0) { + if (addr) { + struct ibss_rsn_peer *peer; + peer = ibss_rsn_get_peer(ibss_rsn, addr); + if (peer) { + peer->authentication_status |= + IBSS_RSN_SET_PTK_AUTH; + ibss_check_rsn_completed(peer); + } + } /* * In IBSS RSN, the pairwise key from the 4-way handshake * initiated by the peer with highest MAC address is used. @@ -296,6 +331,13 @@ static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, } +static void ibss_rsn_disconnect(void *ctx, const u8 *addr, u16 reason) +{ + struct ibss_rsn *ibss_rsn = ctx; + wpa_drv_sta_deauth(ibss_rsn->wpa_s, addr, reason); +} + + static int auth_for_each_sta(void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx), void *cb_ctx) @@ -386,6 +428,7 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn, cb.get_psk = auth_get_psk; cb.set_key = auth_set_key; cb.for_each_sta = auth_for_each_sta; + cb.disconnect = ibss_rsn_disconnect; ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb); if (ibss_rsn->auth_group == NULL) { @@ -402,7 +445,7 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn, static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn, struct ibss_rsn_peer *peer) { - peer->auth = wpa_auth_sta_init(ibss_rsn->auth_group, peer->addr); + peer->auth = wpa_auth_sta_init(ibss_rsn->auth_group, peer->addr, NULL); if (peer->auth == NULL) { wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed"); return -1; @@ -430,45 +473,152 @@ static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn, } -int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr) +static int ibss_rsn_send_auth(struct ibss_rsn *ibss_rsn, const u8 *da, int seq) { - struct ibss_rsn_peer *peer; + struct ieee80211_mgmt auth; + const size_t auth_length = IEEE80211_HDRLEN + sizeof(auth.u.auth); + struct wpa_supplicant *wpa_s = ibss_rsn->wpa_s; - if (ibss_rsn == NULL) + if (wpa_s->driver->send_frame == NULL) return -1; - if (ibss_rsn_get_peer(ibss_rsn, addr)) { - wpa_printf(MSG_DEBUG, "RSN: IBSS Authenticator and Supplicant " - "for peer " MACSTR " already running", - MAC2STR(addr)); - return 0; + os_memset(&auth, 0, sizeof(auth)); + + auth.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_AUTH); + os_memcpy(auth.da, da, ETH_ALEN); + os_memcpy(auth.sa, wpa_s->own_addr, ETH_ALEN); + os_memcpy(auth.bssid, wpa_s->bssid, ETH_ALEN); + + auth.u.auth.auth_alg = host_to_le16(WLAN_AUTH_OPEN); + auth.u.auth.auth_transaction = host_to_le16(seq); + auth.u.auth.status_code = host_to_le16(WLAN_STATUS_SUCCESS); + + wpa_printf(MSG_DEBUG, "RSN: IBSS TX Auth frame (SEQ %d) to " MACSTR, + seq, MAC2STR(da)); + + return wpa_s->driver->send_frame(wpa_s->drv_priv, (u8 *) &auth, + auth_length, 0); +} + + +static int ibss_rsn_is_auth_started(struct ibss_rsn_peer * peer) +{ + return peer->authentication_status & + (IBSS_RSN_AUTH_BY_US | IBSS_RSN_AUTH_EAPOL_BY_US); +} + + +static struct ibss_rsn_peer * +ibss_rsn_peer_init(struct ibss_rsn *ibss_rsn, const u8 *addr) +{ + struct ibss_rsn_peer *peer; + if (ibss_rsn == NULL) + return NULL; + + peer = ibss_rsn_get_peer(ibss_rsn, addr); + if (peer) { + wpa_printf(MSG_DEBUG, "RSN: IBSS Supplicant for peer "MACSTR + " already running", MAC2STR(addr)); + return peer; } - wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator and " - "Supplicant for peer " MACSTR, MAC2STR(addr)); + wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Supplicant for peer "MACSTR, + MAC2STR(addr)); peer = os_zalloc(sizeof(*peer)); - if (peer == NULL) - return -1; + if (peer == NULL) { + wpa_printf(MSG_DEBUG, "RSN: Could not allocate memory."); + return NULL; + } peer->ibss_rsn = ibss_rsn; os_memcpy(peer->addr, addr, ETH_ALEN); + peer->authentication_status = IBSS_RSN_AUTH_NOT_AUTHENTICATED; - if (ibss_rsn_supp_init(peer, ibss_rsn->wpa_s->own_addr, ibss_rsn->psk) - < 0) { + if (ibss_rsn_supp_init(peer, ibss_rsn->wpa_s->own_addr, + ibss_rsn->psk) < 0) { ibss_rsn_free(peer); + return NULL; + } + + peer->next = ibss_rsn->peers; + ibss_rsn->peers = peer; + + return peer; +} + + +static void ibss_rsn_auth_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct ibss_rsn_peer *peer = eloop_ctx; + + /* + * Assume peer does not support Authentication exchange or the frame was + * lost somewhere - start EAPOL Authenticator. + */ + wpa_printf(MSG_DEBUG, + "RSN: Timeout on waiting Authentication frame response from " + MACSTR " - start authenticator", MAC2STR(peer->addr)); + + peer->authentication_status |= IBSS_RSN_AUTH_BY_US; + ibss_rsn_auth_init(peer->ibss_rsn, peer); +} + + +int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr) +{ + struct ibss_rsn_peer *peer; + int res; + + /* if the peer already exists, exit immediately */ + peer = ibss_rsn_get_peer(ibss_rsn, addr); + if (peer) + return 0; + + peer = ibss_rsn_peer_init(ibss_rsn, addr); + if (peer == NULL) return -1; + + /* Open Authentication: send first Authentication frame */ + res = ibss_rsn_send_auth(ibss_rsn, addr, 1); + if (res) { + /* + * The driver may not support Authentication frame exchange in + * IBSS. Ignore authentication and go through EAPOL exchange. + */ + peer->authentication_status |= IBSS_RSN_AUTH_BY_US; + return ibss_rsn_auth_init(ibss_rsn, peer); + } else { + os_get_reltime(&peer->own_auth_tx); + eloop_register_timeout(1, 0, ibss_rsn_auth_timeout, peer, NULL); } - if (ibss_rsn_auth_init(ibss_rsn, peer) < 0) { - ibss_rsn_free(peer); + return 0; +} + + +static int ibss_rsn_peer_authenticated(struct ibss_rsn *ibss_rsn, + struct ibss_rsn_peer *peer, int reason) +{ + int already_started; + + if (ibss_rsn == NULL || peer == NULL) return -1; + + already_started = ibss_rsn_is_auth_started(peer); + peer->authentication_status |= reason; + + if (already_started) { + wpa_printf(MSG_DEBUG, "RSN: IBSS Authenticator already " + "started for peer " MACSTR, MAC2STR(peer->addr)); + return 0; } - peer->next = ibss_rsn->peers; - ibss_rsn->peers = peer; + wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator " + "for now-authenticated peer " MACSTR, MAC2STR(peer->addr)); - return 0; + return ibss_rsn_auth_init(ibss_rsn, peer); } @@ -609,10 +759,21 @@ static int ibss_rsn_process_rx_eapol(struct ibss_rsn *ibss_rsn, return -1; os_memcpy(tmp, buf, len); if (supp) { - wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant"); + peer->authentication_status |= IBSS_RSN_AUTH_EAPOL_BY_PEER; + wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant from " + MACSTR, MAC2STR(peer->addr)); wpa_sm_rx_eapol(peer->supp, peer->addr, tmp, len); } else { - wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Authenticator"); + if (ibss_rsn_is_auth_started(peer) == 0) { + wpa_printf(MSG_DEBUG, "RSN: IBSS EAPOL for " + "Authenticator dropped as " MACSTR " is not " + "authenticated", MAC2STR(peer->addr)); + os_free(tmp); + return -1; + } + + wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Authenticator " + "from "MACSTR, MAC2STR(peer->addr)); wpa_receive(ibss_rsn->auth_group, peer->auth, tmp, len); } os_free(tmp); @@ -638,8 +799,16 @@ int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr, * Create new IBSS peer based on an EAPOL message from the peer * Authenticator. */ - if (ibss_rsn_start(ibss_rsn, src_addr) < 0) + peer = ibss_rsn_peer_init(ibss_rsn, src_addr); + if (peer == NULL) return -1; + + /* assume the peer is authenticated already */ + wpa_printf(MSG_DEBUG, "RSN: IBSS Not using IBSS Auth for peer " + MACSTR, MAC2STR(src_addr)); + ibss_rsn_peer_authenticated(ibss_rsn, peer, + IBSS_RSN_AUTH_EAPOL_BY_US); + return ibss_rsn_process_rx_eapol(ibss_rsn, ibss_rsn->peers, buf, len); } @@ -647,10 +816,101 @@ int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr, return 0; } - 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); } + + +static void ibss_rsn_handle_auth_1_of_2(struct ibss_rsn *ibss_rsn, + struct ibss_rsn_peer *peer, + const u8* addr) +{ + wpa_printf(MSG_DEBUG, "RSN: IBSS RX Auth frame (SEQ 1) from " MACSTR, + MAC2STR(addr)); + + if (peer && + peer->authentication_status & IBSS_RSN_AUTH_EAPOL_BY_PEER) { + if (peer->own_auth_tx.sec) { + struct os_reltime now, diff; + os_get_reltime(&now); + os_reltime_sub(&now, &peer->own_auth_tx, &diff); + if (diff.sec == 0 && diff.usec < 500000) { + wpa_printf(MSG_DEBUG, "RSN: Skip IBSS reinit since only %u usec from own Auth frame TX", + (int) diff.usec); + goto skip_reinit; + } + } + /* + * A peer sent us an Authentication frame even though it already + * started an EAPOL session. We should reinit state machines + * here, but it's much more complicated than just deleting and + * recreating the state machine + */ + wpa_printf(MSG_DEBUG, "RSN: IBSS Reinitializing station " + MACSTR, MAC2STR(addr)); + + ibss_rsn_stop(ibss_rsn, addr); + peer = NULL; + } + + if (!peer) { + peer = ibss_rsn_peer_init(ibss_rsn, addr); + if (!peer) + return; + + wpa_printf(MSG_DEBUG, "RSN: IBSS Auth started by peer " MACSTR, + MAC2STR(addr)); + } + +skip_reinit: + /* reply with an Authentication frame now, before sending an EAPOL */ + ibss_rsn_send_auth(ibss_rsn, addr, 2); + /* no need to start another AUTH challenge in the other way.. */ + ibss_rsn_peer_authenticated(ibss_rsn, peer, IBSS_RSN_AUTH_EAPOL_BY_US); +} + + +void ibss_rsn_handle_auth(struct ibss_rsn *ibss_rsn, const u8 *auth_frame, + size_t len) +{ + const struct ieee80211_mgmt *header; + struct ibss_rsn_peer *peer; + size_t auth_length; + + header = (const struct ieee80211_mgmt *) auth_frame; + auth_length = IEEE80211_HDRLEN + sizeof(header->u.auth); + + if (ibss_rsn == NULL || len < auth_length) + return; + + if (le_to_host16(header->u.auth.auth_alg) != WLAN_AUTH_OPEN || + le_to_host16(header->u.auth.status_code) != WLAN_STATUS_SUCCESS) + return; + + peer = ibss_rsn_get_peer(ibss_rsn, header->sa); + + switch (le_to_host16(header->u.auth.auth_transaction)) { + case 1: + ibss_rsn_handle_auth_1_of_2(ibss_rsn, peer, header->sa); + break; + case 2: + wpa_printf(MSG_DEBUG, "RSN: IBSS RX Auth frame (SEQ 2) from " + MACSTR, MAC2STR(header->sa)); + if (!peer) { + wpa_printf(MSG_DEBUG, "RSN: Received Auth seq 2 from " + "unknown STA " MACSTR, MAC2STR(header->sa)); + break; + } + + /* authentication has been completed */ + eloop_cancel_timeout(ibss_rsn_auth_timeout, peer, NULL); + wpa_printf(MSG_DEBUG, "RSN: IBSS Auth completed with " MACSTR, + MAC2STR(header->sa)); + ibss_rsn_peer_authenticated(ibss_rsn, peer, + IBSS_RSN_AUTH_BY_US); + break; + } +} diff --git a/wpa_supplicant/ibss_rsn.h b/wpa_supplicant/ibss_rsn.h index 1da94ab8c9eb7..67fae2d14ab78 100644 --- a/wpa_supplicant/ibss_rsn.h +++ b/wpa_supplicant/ibss_rsn.h @@ -11,6 +11,21 @@ struct ibss_rsn; +/* not authenticated */ +#define IBSS_RSN_AUTH_NOT_AUTHENTICATED 0x00 +/* remote peer sent an EAPOL message */ +#define IBSS_RSN_AUTH_EAPOL_BY_PEER 0x01 +/* we sent an AUTH message with seq 1 */ +#define IBSS_RSN_AUTH_BY_US 0x02 +/* we sent an EAPOL message */ +#define IBSS_RSN_AUTH_EAPOL_BY_US 0x04 +/* PTK derived as supplicant */ +#define IBSS_RSN_SET_PTK_SUPP 0x08 +/* PTK derived as authenticator */ +#define IBSS_RSN_SET_PTK_AUTH 0x10 +/* PTK completion reported */ +#define IBSS_RSN_REPORTED_PTK 0x20 + struct ibss_rsn_peer { struct ibss_rsn_peer *next; struct ibss_rsn *ibss_rsn; @@ -23,6 +38,9 @@ struct ibss_rsn_peer { size_t supp_ie_len; struct wpa_state_machine *auth; + int authentication_status; + + struct os_reltime own_auth_tx; }; struct ibss_rsn { @@ -40,5 +58,7 @@ 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); +void ibss_rsn_handle_auth(struct ibss_rsn *ibss_rsn, const u8 *auth_frame, + size_t len); #endif /* IBSS_RSN_H */ diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c index b8a8bb2b6ffdd..4a396654487e6 100644 --- a/wpa_supplicant/interworking.c +++ b/wpa_supplicant/interworking.c @@ -1,6 +1,7 @@ /* * Interworking (IEEE 802.11u) - * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -18,12 +19,15 @@ #include "eap_common/eap_defs.h" #include "eap_peer/eap.h" #include "eap_peer/eap_methods.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "rsn_supp/wpa.h" #include "wpa_supplicant_i.h" #include "config.h" #include "config_ssid.h" #include "bss.h" #include "scan.h" #include "notify.h" +#include "driver_i.h" #include "gas_query.h" #include "hs20_supplicant.h" #include "interworking.h" @@ -42,29 +46,50 @@ #endif static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s); +static struct wpa_cred * interworking_credentials_available_realm( + struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw, + int *excluded); +static struct wpa_cred * interworking_credentials_available_3gpp( + struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw, + int *excluded); + + +static int cred_prio_cmp(const struct wpa_cred *a, const struct wpa_cred *b) +{ + if (a->priority > b->priority) + return 1; + if (a->priority < b->priority) + return -1; + if (a->provisioning_sp == NULL || b->provisioning_sp == NULL || + os_strcmp(a->provisioning_sp, b->provisioning_sp) != 0) + return 0; + if (a->sp_priority < b->sp_priority) + return 1; + if (a->sp_priority > b->sp_priority) + return -1; + return 0; +} static void interworking_reconnect(struct wpa_supplicant *wpa_s) { + unsigned int tried; + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) { wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); } wpa_s->disconnected = 0; wpa_s->reassociate = 1; + tried = wpa_s->interworking_fast_assoc_tried; + wpa_s->interworking_fast_assoc_tried = 1; - if (wpa_s->last_scan_res_used > 0) { - struct os_time now; - os_get_time(&now); - if (now.sec - wpa_s->last_scan.sec <= 5) { - wpa_printf(MSG_DEBUG, "Interworking: Old scan results " - "are fresh - connect without new scan"); - if (wpas_select_network_from_last_scan(wpa_s) >= 0) - return; - } - } + if (!tried && wpa_supplicant_fast_associate(wpa_s) >= 0) + return; + wpa_s->interworking_fast_assoc_tried = 0; wpa_supplicant_req_scan(wpa_s, 0, 0); } @@ -103,6 +128,9 @@ static void interworking_anqp_resp_cb(void *ctx, const u8 *dst, { struct wpa_supplicant *wpa_s = ctx; + wpa_printf(MSG_DEBUG, "ANQP: Response callback dst=" MACSTR + " dialog_token=%u result=%d status_code=%u", + MAC2STR(dst), dialog_token, result, status_code); anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp, status_code); interworking_next_anqp_fetch(wpa_s); @@ -116,6 +144,8 @@ static int cred_with_roaming_consortium(struct wpa_supplicant *wpa_s) for (cred = wpa_s->conf->cred; cred; cred = cred->next) { if (cred->roaming_consortium_len) return 1; + if (cred->required_roaming_consortium_len) + return 1; } return 0; } @@ -154,13 +184,45 @@ static int cred_with_domain(struct wpa_supplicant *wpa_s) struct wpa_cred *cred; for (cred = wpa_s->conf->cred; cred; cred = cred->next) { - if (cred->domain || cred->pcsc || cred->imsi) + if (cred->domain || cred->pcsc || cred->imsi || + cred->roaming_partner) return 1; } return 0; } +#ifdef CONFIG_HS20 + +static int cred_with_min_backhaul(struct wpa_supplicant *wpa_s) +{ + struct wpa_cred *cred; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + if (cred->min_dl_bandwidth_home || + cred->min_ul_bandwidth_home || + cred->min_dl_bandwidth_roaming || + cred->min_ul_bandwidth_roaming) + return 1; + } + return 0; +} + + +static int cred_with_conn_capab(struct wpa_supplicant *wpa_s) +{ + struct wpa_cred *cred; + + for (cred = wpa_s->conf->cred; cred; cred = cred->next) { + if (cred->num_req_conn_capab) + return 1; + } + return 0; +} + +#endif /* CONFIG_HS20 */ + + static int additional_roaming_consortiums(struct wpa_bss *bss) { const u8 *ie; @@ -189,8 +251,9 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s, struct wpabuf *extra = NULL; int all = wpa_s->fetch_all_anqp; - wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR, - MAC2STR(bss->bssid)); + wpa_msg(wpa_s, MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR, + MAC2STR(bss->bssid)); + wpa_s->interworking_gas_bss = bss; info_ids[num_info_ids++] = ANQP_CAPABILITY_LIST; if (all) { @@ -204,8 +267,10 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s, info_ids[num_info_ids++] = ANQP_IP_ADDR_TYPE_AVAILABILITY; if (all || cred_with_nai_realm(wpa_s)) info_ids[num_info_ids++] = ANQP_NAI_REALM; - if (all || cred_with_3gpp(wpa_s)) + if (all || cred_with_3gpp(wpa_s)) { info_ids[num_info_ids++] = ANQP_3GPP_CELLULAR_NETWORK; + wpa_supplicant_scard_init(wpa_s, NULL); + } if (all || cred_with_domain(wpa_s)) info_ids[num_info_ids++] = ANQP_DOMAIN_NAME; wpa_hexdump(MSG_DEBUG, "Interworking: ANQP Query info", @@ -225,13 +290,17 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s, wpabuf_put_u8(extra, HS20_STYPE_QUERY_LIST); wpabuf_put_u8(extra, 0); /* Reserved */ wpabuf_put_u8(extra, HS20_STYPE_CAPABILITY_LIST); - if (all) { + if (all) wpabuf_put_u8(extra, HS20_STYPE_OPERATOR_FRIENDLY_NAME); + if (all || cred_with_min_backhaul(wpa_s)) wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS); + if (all || cred_with_conn_capab(wpa_s)) wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY); + if (all) wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS); - } + if (all) + wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_LIST); gas_anqp_set_element_len(extra, len_pos); } #endif /* CONFIG_HS20 */ @@ -244,15 +313,15 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s, 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"); + wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request"); + wpabuf_free(buf); ret = -1; eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s, NULL); } else - wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " - "%u", res); + wpa_msg(wpa_s, MSG_DEBUG, + "ANQP: Query started with dialog token %u", res); - wpabuf_free(buf); return ret; } @@ -411,11 +480,9 @@ static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos, return NULL; } wpa_hexdump_ascii(MSG_DEBUG, "NAI Realm", pos, realm_len); - r->realm = os_malloc(realm_len + 1); + r->realm = dup_binstr(pos, realm_len); 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) { @@ -447,20 +514,25 @@ static struct nai_realm * nai_realm_parse(struct wpabuf *anqp, u16 *count) struct nai_realm *realm; const u8 *pos, *end; u16 i, num; + size_t left; - if (anqp == NULL || wpabuf_len(anqp) < 2) + if (anqp == NULL) + return NULL; + left = wpabuf_len(anqp); + if (left < 2) return NULL; pos = wpabuf_head_u8(anqp); - end = pos + wpabuf_len(anqp); + end = pos + left; num = WPA_GET_LE16(pos); wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num); pos += 2; + left -= 2; - if (num * 5 > end - pos) { + if (num > left / 5) { wpa_printf(MSG_DEBUG, "Invalid NAI Realm Count %u - not " "enough data (%u octets) for that many realms", - num, (unsigned int) (end - pos)); + num, (unsigned int) left); return NULL; } @@ -516,55 +588,91 @@ static int nai_realm_match(struct nai_realm *realm, const char *home_realm) } -static int nai_realm_cred_username(struct nai_realm_eap *eap) +static int nai_realm_cred_username(struct wpa_supplicant *wpa_s, + struct nai_realm_eap *eap) { - if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) + if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) { + wpa_msg(wpa_s, MSG_DEBUG, + "nai-realm-cred-username: EAP method not supported: %d", + eap->method); return 0; /* method not supported */ + } - if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP) { + if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP && + eap->method != EAP_TYPE_FAST) { /* Only tunneled methods with username/password supported */ + wpa_msg(wpa_s, MSG_DEBUG, + "nai-realm-cred-username: Method: %d is not TTLS, PEAP, or FAST", + eap->method); return 0; } - if (eap->method == EAP_TYPE_PEAP) { + if (eap->method == EAP_TYPE_PEAP || eap->method == EAP_TYPE_FAST) { if (eap->inner_method && - eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) + eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) { + wpa_msg(wpa_s, MSG_DEBUG, + "nai-realm-cred-username: PEAP/FAST: Inner method not supported: %d", + eap->inner_method); return 0; + } if (!eap->inner_method && - eap_get_name(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2) == NULL) + eap_get_name(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2) == NULL) { + wpa_msg(wpa_s, MSG_DEBUG, + "nai-realm-cred-username: MSCHAPv2 not supported"); return 0; + } } if (eap->method == EAP_TYPE_TTLS) { if (eap->inner_method == 0 && eap->inner_non_eap == 0) return 1; /* Assume TTLS/MSCHAPv2 is used */ if (eap->inner_method && - eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) + eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) { + wpa_msg(wpa_s, MSG_DEBUG, + "nai-realm-cred-username: TTLS, but inner not supported: %d", + eap->inner_method); 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) + eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAPV2) { + wpa_msg(wpa_s, MSG_DEBUG, + "nai-realm-cred-username: TTLS, inner-non-eap not supported: %d", + eap->inner_non_eap); return 0; + } } if (eap->inner_method && eap->inner_method != EAP_TYPE_GTC && - eap->inner_method != EAP_TYPE_MSCHAPV2) + eap->inner_method != EAP_TYPE_MSCHAPV2) { + wpa_msg(wpa_s, MSG_DEBUG, + "nai-realm-cred-username: inner-method not GTC or MSCHAPv2: %d", + eap->inner_method); return 0; + } return 1; } -static int nai_realm_cred_cert(struct nai_realm_eap *eap) +static int nai_realm_cred_cert(struct wpa_supplicant *wpa_s, + struct nai_realm_eap *eap) { - if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) + if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) { + wpa_msg(wpa_s, MSG_DEBUG, + "nai-realm-cred-cert: Method not supported: %d", + eap->method); return 0; /* method not supported */ + } if (eap->method != EAP_TYPE_TLS) { /* Only EAP-TLS supported for credential authentication */ + wpa_msg(wpa_s, MSG_DEBUG, + "nai-realm-cred-cert: Method not TLS: %d", + eap->method); return 0; } @@ -572,27 +680,33 @@ static int nai_realm_cred_cert(struct nai_realm_eap *eap) } -static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred, +static struct nai_realm_eap * nai_realm_find_eap(struct wpa_supplicant *wpa_s, + struct wpa_cred *cred, struct nai_realm *realm) { u8 e; - if (cred == NULL || - cred->username == NULL || + if (cred->username == NULL || cred->username[0] == '\0' || ((cred->password == NULL || cred->password[0] == '\0') && (cred->private_key == NULL || - cred->private_key[0] == '\0'))) + cred->private_key[0] == '\0'))) { + wpa_msg(wpa_s, MSG_DEBUG, + "nai-realm-find-eap: incomplete cred info: username: %s password: %s private_key: %s", + cred->username ? cred->username : "NULL", + cred->password ? cred->password : "NULL", + cred->private_key ? cred->private_key : "NULL"); return NULL; + } for (e = 0; e < realm->eap_count; e++) { struct nai_realm_eap *eap = &realm->eap[e]; if (cred->password && cred->password[0] && - nai_realm_cred_username(eap)) + nai_realm_cred_username(wpa_s, eap)) return eap; if (cred->private_key && cred->private_key[0] && - nai_realm_cred_cert(eap)) + nai_realm_cred_cert(wpa_s, eap)) return eap; } @@ -604,19 +718,29 @@ static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred, static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) { - u8 plmn[3]; + u8 plmn[3], plmn2[3]; const u8 *pos, *end; u8 udhl; - /* See Annex A of 3GPP TS 24.234 v8.1.0 for description */ + /* + * See Annex A of 3GPP TS 24.234 v8.1.0 for description. The network + * operator is allowed to include only two digits of the MNC, so allow + * matches based on both two and three digit MNC assumptions. Since some + * SIM/USIM cards may not expose MNC length conveniently, we may be + * provided the default MNC length 3 here and as such, checking with MNC + * length 2 is justifiable even though 3GPP TS 24.234 does not mention + * that case. Anyway, MCC/MNC pair where both 2 and 3 digit MNC is used + * with otherwise matching values would not be good idea in general, so + * this should not result in selecting incorrect networks. + */ + /* Match with 3 digit MNC */ plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4); - plmn[1] = imsi[2] - '0'; - /* default to MNC length 3 if unknown */ - if (mnc_len != 2) - plmn[1] |= (imsi[5] - '0') << 4; - else - plmn[1] |= 0xf0; + plmn[1] = (imsi[2] - '0') | ((imsi[5] - '0') << 4); plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4); + /* Match with 2 digit MNC */ + plmn2[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4); + plmn2[1] = (imsi[2] - '0') | 0xf0; + plmn2[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4); if (anqp == NULL) return 0; @@ -636,6 +760,10 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) } end = pos + udhl; + wpa_printf(MSG_DEBUG, "Interworking: Matching against MCC/MNC alternatives: %02x:%02x:%02x or %02x:%02x:%02x (IMSI %s, MNC length %d)", + plmn[0], plmn[1], plmn[2], plmn2[0], plmn2[1], plmn2[2], + imsi, mnc_len); + while (pos + 2 <= end) { u8 iei, len; const u8 *l_end; @@ -648,14 +776,20 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len) if (iei == 0 && len > 0) { /* PLMN List */ u8 num, i; + wpa_hexdump(MSG_DEBUG, "Interworking: PLMN List information element", + pos, len); num = *pos++; for (i = 0; i < num; i++) { - if (pos + 3 > end) + if (pos + 3 > l_end) break; - if (os_memcmp(pos, plmn, 3) == 0) + if (os_memcmp(pos, plmn, 3) == 0 || + os_memcmp(pos, plmn2, 3) == 0) return 1; /* Found matching PLMN */ pos += 3; } + } else { + wpa_hexdump(MSG_DEBUG, "Interworking: Unrecognized 3GPP information element", + pos, len); } pos = l_end; @@ -714,8 +848,8 @@ static int build_root_nai(char *nai, size_t nai_len, const char *imsi, *pos++ = imsi[4]; *pos++ = imsi[5]; } - pos += os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org", - imsi[0], imsi[1], imsi[2]); + os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org", + imsi[0], imsi[1], imsi[2]); return 0; } @@ -732,12 +866,86 @@ static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix) #endif /* INTERWORKING_3GPP */ +static int already_connected(struct wpa_supplicant *wpa_s, + struct wpa_cred *cred, struct wpa_bss *bss) +{ + struct wpa_ssid *ssid, *sel_ssid; + struct wpa_bss *selected; + + if (wpa_s->wpa_state < WPA_ASSOCIATED || wpa_s->current_ssid == NULL) + return 0; + + ssid = wpa_s->current_ssid; + if (ssid->parent_cred != cred) + return 0; + + if (ssid->ssid_len != bss->ssid_len || + os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) != 0) + return 0; + + sel_ssid = NULL; + selected = wpa_supplicant_pick_network(wpa_s, &sel_ssid); + if (selected && sel_ssid && sel_ssid->priority > ssid->priority) + return 0; /* higher priority network in scan results */ + + return 1; +} + + +static void remove_duplicate_network(struct wpa_supplicant *wpa_s, + struct wpa_cred *cred, + struct wpa_bss *bss) +{ + struct wpa_ssid *ssid; + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid->parent_cred != cred) + continue; + if (ssid->ssid_len != bss->ssid_len || + os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) != 0) + continue; + + break; + } + + if (ssid == NULL) + return; + + wpa_printf(MSG_DEBUG, "Interworking: Remove duplicate network entry for the same credential"); + + 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->own_disconnect_req = 1; + wpa_supplicant_deauthenticate(wpa_s, + WLAN_REASON_DEAUTH_LEAVING); + } + + wpas_notify_network_removed(wpa_s, ssid); + wpa_config_remove_network(wpa_s->conf, ssid->id); +} + + static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { - if (wpa_config_set(ssid, "key_mgmt", - wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ? - "WPA-EAP WPA-EAP-SHA256" : "WPA-EAP", 0) < 0) + const char *key_mgmt = NULL; +#ifdef CONFIG_IEEE80211R + int res; + struct wpa_driver_capa capa; + + res = wpa_drv_get_capa(wpa_s, &capa); + if (res == 0 && capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) { + key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ? + "WPA-EAP WPA-EAP-SHA256 FT-EAP" : + "WPA-EAP FT-EAP"; + } +#endif /* CONFIG_IEEE80211R */ + + if (!key_mgmt) + key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ? + "WPA-EAP WPA-EAP-SHA256" : "WPA-EAP"; + if (wpa_config_set(ssid, "key_mgmt", key_mgmt, 0) < 0) return -1; if (wpa_config_set(ssid, "proto", "RSN", 0) < 0) return -1; @@ -748,12 +956,11 @@ static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s, static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss) + struct wpa_cred *cred, + struct wpa_bss *bss, int only_add) { #ifdef INTERWORKING_3GPP - struct wpa_cred *cred; struct wpa_ssid *ssid; - const u8 *ie; int eap_type; int res; char prefix; @@ -761,45 +968,16 @@ static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s, if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL) return -1; - for (cred = wpa_s->conf->cred; cred; cred = cred->next) { - char *sep; - const char *imsi; - int mnc_len; + wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR + " (3GPP)", MAC2STR(bss->bssid)); -#ifdef PCSC_FUNCS - if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard && - wpa_s->imsi[0]) { - imsi = wpa_s->imsi; - mnc_len = wpa_s->mnc_len; - goto compare; - } -#endif /* PCSC_FUNCS */ - - if (cred->imsi == NULL || !cred->imsi[0] || - cred->milenage == NULL || !cred->milenage[0]) - continue; - - sep = os_strchr(cred->imsi, '-'); - if (sep == NULL || - (sep - cred->imsi != 5 && sep - cred->imsi != 6)) - continue; - mnc_len = sep - cred->imsi - 3; - imsi = cred->imsi; - -#ifdef PCSC_FUNCS - compare: -#endif /* PCSC_FUNCS */ - if (plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len)) - break; + if (already_connected(wpa_s, cred, bss)) { + wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR, + MAC2STR(bss->bssid)); + return wpa_s->current_ssid->id; } - if (cred == NULL) - return -1; - 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)); + remove_duplicate_network(wpa_s, cred, bss); ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) @@ -810,11 +988,12 @@ static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s, wpa_config_set_network_defaults(ssid); ssid->priority = cred->priority; ssid->temporary = 1; - ssid->ssid = os_zalloc(ie[1] + 1); + ssid->ssid = os_zalloc(bss->ssid_len + 1); if (ssid->ssid == NULL) goto fail; - os_memcpy(ssid->ssid, ie + 2, ie[1]); - ssid->ssid_len = ie[1]; + os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len); + ssid->ssid_len = bss->ssid_len; + ssid->eap.sim_num = cred->sim_num; if (interworking_set_hs20_params(wpa_s, ssid) < 0) goto fail; @@ -847,13 +1026,13 @@ static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s, break; } if (res < 0) { - wpa_printf(MSG_DEBUG, "Selected EAP method (%d) not supported", - eap_type); + wpa_msg(wpa_s, MSG_DEBUG, + "Selected EAP method (%d) not supported", eap_type); goto fail; } if (!cred->pcsc && set_root_nai(ssid, cred->imsi, prefix) < 0) { - wpa_printf(MSG_DEBUG, "Failed to set Root NAI"); + wpa_msg(wpa_s, MSG_DEBUG, "Failed to set Root NAI"); goto fail; } @@ -870,14 +1049,12 @@ static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s, goto fail; } - if (cred->password && cred->password[0] && - wpa_config_set_quoted(ssid, "password", cred->password) < 0) - goto fail; - + wpa_s->next_ssid = ssid; wpa_config_update_prio_list(wpa_s->conf); - interworking_reconnect(wpa_s); + if (!only_add) + interworking_reconnect(wpa_s); - return 0; + return ssid->id; fail: wpas_notify_network_removed(wpa_s, ssid); @@ -963,6 +1140,27 @@ static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp, } +static int cred_no_required_oi_match(struct wpa_cred *cred, struct wpa_bss *bss) +{ + const u8 *ie; + + if (cred->required_roaming_consortium_len == 0) + return 0; + + ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM); + + if (ie == NULL && + (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL)) + return 1; + + return !roaming_consortium_match(ie, + bss->anqp ? + bss->anqp->roaming_consortium : NULL, + cred->required_roaming_consortium, + cred->required_roaming_consortium_len); +} + + static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss) { size_t i; @@ -981,11 +1179,164 @@ static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss) } +static int cred_below_min_backhaul(struct wpa_supplicant *wpa_s, + struct wpa_cred *cred, struct wpa_bss *bss) +{ + int res; + unsigned int dl_bandwidth, ul_bandwidth; + const u8 *wan; + u8 wan_info, dl_load, ul_load; + u16 lmd; + u32 ul_speed, dl_speed; + + if (!cred->min_dl_bandwidth_home && + !cred->min_ul_bandwidth_home && + !cred->min_dl_bandwidth_roaming && + !cred->min_ul_bandwidth_roaming) + return 0; /* No bandwidth constraint specified */ + + if (bss->anqp == NULL || bss->anqp->hs20_wan_metrics == NULL) + return 0; /* No WAN Metrics known - ignore constraint */ + + wan = wpabuf_head(bss->anqp->hs20_wan_metrics); + wan_info = wan[0]; + if (wan_info & BIT(3)) + return 1; /* WAN link at capacity */ + lmd = WPA_GET_LE16(wan + 11); + if (lmd == 0) + return 0; /* Downlink/Uplink Load was not measured */ + dl_speed = WPA_GET_LE32(wan + 1); + ul_speed = WPA_GET_LE32(wan + 5); + dl_load = wan[9]; + ul_load = wan[10]; + + if (dl_speed >= 0xffffff) + dl_bandwidth = dl_speed / 255 * (255 - dl_load); + else + dl_bandwidth = dl_speed * (255 - dl_load) / 255; + + if (ul_speed >= 0xffffff) + ul_bandwidth = ul_speed / 255 * (255 - ul_load); + else + ul_bandwidth = ul_speed * (255 - ul_load) / 255; + + res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ? + bss->anqp->domain_name : NULL); + if (res > 0) { + if (cred->min_dl_bandwidth_home > dl_bandwidth) + return 1; + if (cred->min_ul_bandwidth_home > ul_bandwidth) + return 1; + } else { + if (cred->min_dl_bandwidth_roaming > dl_bandwidth) + return 1; + if (cred->min_ul_bandwidth_roaming > ul_bandwidth) + return 1; + } + + return 0; +} + + +static int cred_over_max_bss_load(struct wpa_supplicant *wpa_s, + struct wpa_cred *cred, struct wpa_bss *bss) +{ + const u8 *ie; + int res; + + if (!cred->max_bss_load) + return 0; /* No BSS Load constraint specified */ + + ie = wpa_bss_get_ie(bss, WLAN_EID_BSS_LOAD); + if (ie == NULL || ie[1] < 3) + return 0; /* No BSS Load advertised */ + + res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ? + bss->anqp->domain_name : NULL); + if (res <= 0) + return 0; /* Not a home network */ + + return ie[4] > cred->max_bss_load; +} + + +static int has_proto_match(const u8 *pos, const u8 *end, u8 proto) +{ + while (pos + 4 <= end) { + if (pos[0] == proto && pos[3] == 1 /* Open */) + return 1; + pos += 4; + } + + return 0; +} + + +static int has_proto_port_match(const u8 *pos, const u8 *end, u8 proto, + u16 port) +{ + while (pos + 4 <= end) { + if (pos[0] == proto && WPA_GET_LE16(&pos[1]) == port && + pos[3] == 1 /* Open */) + return 1; + pos += 4; + } + + return 0; +} + + +static int cred_conn_capab_missing(struct wpa_supplicant *wpa_s, + struct wpa_cred *cred, struct wpa_bss *bss) +{ + int res; + const u8 *capab, *end; + unsigned int i, j; + int *ports; + + if (!cred->num_req_conn_capab) + return 0; /* No connection capability constraint specified */ + + if (bss->anqp == NULL || bss->anqp->hs20_connection_capability == NULL) + return 0; /* No Connection Capability known - ignore constraint + */ + + res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ? + bss->anqp->domain_name : NULL); + if (res > 0) + return 0; /* No constraint in home network */ + + capab = wpabuf_head(bss->anqp->hs20_connection_capability); + end = capab + wpabuf_len(bss->anqp->hs20_connection_capability); + + for (i = 0; i < cred->num_req_conn_capab; i++) { + ports = cred->req_conn_capab_port[i]; + if (!ports) { + if (!has_proto_match(capab, end, + cred->req_conn_capab_proto[i])) + return 1; + } else { + for (j = 0; ports[j] > -1; j++) { + if (!has_proto_port_match( + capab, end, + cred->req_conn_capab_proto[i], + ports[j])) + return 1; + } + } + } + + return 0; +} + + static struct wpa_cred * interworking_credentials_available_roaming_consortium( - struct wpa_supplicant *wpa_s, struct wpa_bss *bss) + struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw, + int *excluded) { struct wpa_cred *cred, *selected = NULL; const u8 *ie; + int is_excluded = 0; ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM); @@ -1008,14 +1359,33 @@ static struct wpa_cred * interworking_credentials_available_roaming_consortium( cred->roaming_consortium_len)) continue; - if (cred_excluded_ssid(cred, bss)) + if (cred_no_required_oi_match(cred, bss)) continue; - - if (selected == NULL || - selected->priority < cred->priority) - selected = cred; + if (!ignore_bw && cred_below_min_backhaul(wpa_s, cred, bss)) + continue; + if (!ignore_bw && cred_over_max_bss_load(wpa_s, cred, bss)) + continue; + if (!ignore_bw && cred_conn_capab_missing(wpa_s, cred, bss)) + continue; + if (cred_excluded_ssid(cred, bss)) { + if (excluded == NULL) + continue; + if (selected == NULL) { + selected = cred; + is_excluded = 1; + } + } else { + if (selected == NULL || is_excluded || + cred_prio_cmp(selected, cred) < 0) { + selected = cred; + is_excluded = 0; + } + } } + if (excluded) + *excluded = is_excluded; + return selected; } @@ -1119,18 +1489,33 @@ static int interworking_set_eap_params(struct wpa_ssid *ssid, wpa_config_set_quoted(ssid, "ca_cert", cred->ca_cert) < 0) return -1; + if (cred->domain_suffix_match && cred->domain_suffix_match[0] && + wpa_config_set_quoted(ssid, "domain_suffix_match", + cred->domain_suffix_match) < 0) + return -1; + + ssid->eap.ocsp = cred->ocsp; + return 0; } static int interworking_connect_roaming_consortium( struct wpa_supplicant *wpa_s, struct wpa_cred *cred, - struct wpa_bss *bss, const u8 *ssid_ie) + struct wpa_bss *bss, int only_add) { struct wpa_ssid *ssid; - wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " based on " - "roaming consortium match", MAC2STR(bss->bssid)); + wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR + " based on roaming consortium match", MAC2STR(bss->bssid)); + + if (already_connected(wpa_s, cred, bss)) { + wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR, + MAC2STR(bss->bssid)); + return wpa_s->current_ssid->id; + } + + remove_duplicate_network(wpa_s, cred, bss); ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) @@ -1140,18 +1525,18 @@ static int interworking_connect_roaming_consortium( wpa_config_set_network_defaults(ssid); ssid->priority = cred->priority; ssid->temporary = 1; - ssid->ssid = os_zalloc(ssid_ie[1] + 1); + ssid->ssid = os_zalloc(bss->ssid_len + 1); if (ssid->ssid == NULL) goto fail; - os_memcpy(ssid->ssid, ssid_ie + 2, ssid_ie[1]); - ssid->ssid_len = ssid_ie[1]; + os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len); + ssid->ssid_len = bss->ssid_len; if (interworking_set_hs20_params(wpa_s, ssid) < 0) goto fail; if (cred->eap_method == NULL) { - wpa_printf(MSG_DEBUG, "Interworking: No EAP method set for " - "credential using roaming consortium"); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: No EAP method set for credential using roaming consortium"); goto fail; } @@ -1161,10 +1546,12 @@ static int interworking_connect_roaming_consortium( cred->eap_method->method == EAP_TYPE_TTLS) < 0) goto fail; + wpa_s->next_ssid = ssid; wpa_config_update_prio_list(wpa_s->conf); - interworking_reconnect(wpa_s); + if (!only_add) + interworking_reconnect(wpa_s); - return 0; + return ssid->id; fail: wpas_notify_network_removed(wpa_s, ssid); @@ -1173,77 +1560,161 @@ fail: } -int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +static int interworking_connect_helper(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, int allow_excluded, + int only_add) { - struct wpa_cred *cred; + struct wpa_cred *cred, *cred_rc, *cred_3gpp; struct wpa_ssid *ssid; struct nai_realm *realm; struct nai_realm_eap *eap = NULL; u16 count, i; char buf[100]; - const u8 *ie; + int excluded = 0, *excl = allow_excluded ? &excluded : NULL; + const char *name; if (wpa_s->conf->cred == NULL || 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)); + if (disallowed_bssid(wpa_s, bss->bssid) || + disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) { + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Reject connection to disallowed BSS " + MACSTR, MAC2STR(bss->bssid)); return -1; } + wpa_printf(MSG_DEBUG, "Interworking: Considering BSS " MACSTR + " for connection (allow_excluded=%d)", + MAC2STR(bss->bssid), allow_excluded); + if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) { /* * We currently support only HS 2.0 networks and those are * required to use WPA2-Enterprise. */ - wpa_printf(MSG_DEBUG, "Interworking: Network does not use " - "RSN"); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Network does not use RSN"); return -1; } - cred = interworking_credentials_available_roaming_consortium(wpa_s, - bss); - if (cred) - return interworking_connect_roaming_consortium(wpa_s, cred, - bss, ie); + cred_rc = interworking_credentials_available_roaming_consortium( + wpa_s, bss, 0, excl); + if (cred_rc) { + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Highest roaming consortium matching credential priority %d sp_priority %d", + cred_rc->priority, cred_rc->sp_priority); + if (allow_excluded && excl && !(*excl)) + excl = NULL; + } + + cred = interworking_credentials_available_realm(wpa_s, bss, 0, excl); + if (cred) { + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d", + cred->priority, cred->sp_priority); + if (allow_excluded && excl && !(*excl)) + excl = NULL; + } + + cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss, 0, + excl); + if (cred_3gpp) { + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Highest 3GPP matching credential priority %d sp_priority %d", + cred_3gpp->priority, cred_3gpp->sp_priority); + if (allow_excluded && excl && !(*excl)) + excl = NULL; + } + + if (!cred_rc && !cred && !cred_3gpp) { + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: No full credential matches - consider options without BW(etc.) limits"); + cred_rc = interworking_credentials_available_roaming_consortium( + wpa_s, bss, 1, excl); + if (cred_rc) { + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Highest roaming consortium matching credential priority %d sp_priority %d (ignore BW)", + cred_rc->priority, cred_rc->sp_priority); + if (allow_excluded && excl && !(*excl)) + excl = NULL; + } + + cred = interworking_credentials_available_realm(wpa_s, bss, 1, + excl); + if (cred) { + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d (ignore BW)", + cred->priority, cred->sp_priority); + if (allow_excluded && excl && !(*excl)) + excl = NULL; + } + + cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss, + 1, excl); + if (cred_3gpp) { + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Highest 3GPP matching credential priority %d sp_priority %d (ignore BW)", + cred_3gpp->priority, cred_3gpp->sp_priority); + if (allow_excluded && excl && !(*excl)) + excl = NULL; + } + } + + if (cred_rc && + (cred == NULL || cred_prio_cmp(cred_rc, cred) >= 0) && + (cred_3gpp == NULL || cred_prio_cmp(cred_rc, cred_3gpp) >= 0)) + return interworking_connect_roaming_consortium(wpa_s, cred_rc, + bss, only_add); + + if (cred_3gpp && + (cred == NULL || cred_prio_cmp(cred_3gpp, cred) >= 0)) { + return interworking_connect_3gpp(wpa_s, cred_3gpp, bss, + only_add); + } + + if (cred == NULL) { + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: No matching credentials found for " + MACSTR, MAC2STR(bss->bssid)); + return -1; + } realm = nai_realm_parse(bss->anqp ? bss->anqp->nai_realm : NULL, &count); if (realm == NULL) { - wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI " - "Realm list from " MACSTR, MAC2STR(bss->bssid)); - count = 0; + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Could not parse NAI Realm list from " + MACSTR, MAC2STR(bss->bssid)); + return -1; } - for (cred = wpa_s->conf->cred; cred; cred = cred->next) { - for (i = 0; i < count; i++) { - if (!nai_realm_match(&realm[i], cred->realm)) - continue; - eap = nai_realm_find_eap(cred, &realm[i]); - if (eap) - break; - } + for (i = 0; i < count; i++) { + if (!nai_realm_match(&realm[i], cred->realm)) + continue; + eap = nai_realm_find_eap(wpa_s, cred, &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)); + wpa_msg(wpa_s, 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)); + wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR, + MAC2STR(bss->bssid)); + + if (already_connected(wpa_s, cred, bss)) { + wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR, + MAC2STR(bss->bssid)); + nai_realm_free(realm, count); + return 0; + } + + remove_duplicate_network(wpa_s, cred, bss); ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) { @@ -1255,11 +1726,11 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) wpa_config_set_network_defaults(ssid); ssid->priority = cred->priority; ssid->temporary = 1; - ssid->ssid = os_zalloc(ie[1] + 1); + ssid->ssid = os_zalloc(bss->ssid_len + 1); if (ssid->ssid == NULL) goto fail; - os_memcpy(ssid->ssid, ie + 2, ie[1]); - ssid->ssid_len = ie[1]; + os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len); + ssid->ssid_len = bss->ssid_len; if (interworking_set_hs20_params(wpa_s, ssid) < 0) goto fail; @@ -1308,11 +1779,19 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) } break; case EAP_TYPE_PEAP: - os_snprintf(buf, sizeof(buf), "\"auth=%s\"", - eap_get_name(EAP_VENDOR_IETF, - eap->inner_method ? - eap->inner_method : - EAP_TYPE_MSCHAPV2)); + case EAP_TYPE_FAST: + if (wpa_config_set(ssid, "phase1", "\"fast_provisioning=2\"", + 0) < 0) + goto fail; + if (wpa_config_set(ssid, "pac_file", + "\"blob://pac_interworking\"", 0) < 0) + goto fail; + name = eap_get_name(EAP_VENDOR_IETF, + eap->inner_method ? eap->inner_method : + EAP_TYPE_MSCHAPV2); + if (name == NULL) + goto fail; + os_snprintf(buf, sizeof(buf), "\"auth=%s\"", name); if (wpa_config_set(ssid, "phase2", buf, 0) < 0) goto fail; break; @@ -1326,10 +1805,12 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) nai_realm_free(realm, count); + wpa_s->next_ssid = ssid; wpa_config_update_prio_list(wpa_s->conf); - interworking_reconnect(wpa_s); + if (!only_add) + interworking_reconnect(wpa_s); - return 0; + return ssid->id; fail: wpas_notify_network_removed(wpa_s, ssid); @@ -1339,32 +1820,102 @@ fail: } +int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + int only_add) +{ + return interworking_connect_helper(wpa_s, bss, 1, only_add); +} + + +#ifdef PCSC_FUNCS +static int interworking_pcsc_read_imsi(struct wpa_supplicant *wpa_s) +{ + size_t len; + + if (wpa_s->imsi[0] && wpa_s->mnc_len) + return 0; + + len = sizeof(wpa_s->imsi) - 1; + if (scard_get_imsi(wpa_s->scard, wpa_s->imsi, &len)) { + scard_deinit(wpa_s->scard); + wpa_s->scard = NULL; + wpa_msg(wpa_s, MSG_ERROR, "Could not read IMSI"); + return -1; + } + wpa_s->imsi[len] = '\0'; + wpa_s->mnc_len = scard_get_mnc_len(wpa_s->scard); + wpa_printf(MSG_DEBUG, "SCARD: IMSI %s (MNC length %d)", + wpa_s->imsi, wpa_s->mnc_len); + + return 0; +} +#endif /* PCSC_FUNCS */ + + static struct wpa_cred * interworking_credentials_available_3gpp( - struct wpa_supplicant *wpa_s, struct wpa_bss *bss) + struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw, + int *excluded) { - struct wpa_cred *cred, *selected = NULL; + struct wpa_cred *selected = NULL; +#ifdef INTERWORKING_3GPP + struct wpa_cred *cred; int ret; + int is_excluded = 0; -#ifdef INTERWORKING_3GPP - if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL) + if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL) { + wpa_msg(wpa_s, MSG_DEBUG, + "interworking-avail-3gpp: not avail, anqp: %p anqp_3gpp: %p", + bss->anqp, bss->anqp ? bss->anqp->anqp_3gpp : NULL); return NULL; + } + +#ifdef CONFIG_EAP_PROXY + if (!wpa_s->imsi[0]) { + size_t len; + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: IMSI not available - try to read again through eap_proxy"); + wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, + wpa_s->imsi, + &len); + if (wpa_s->mnc_len > 0) { + wpa_s->imsi[len] = '\0'; + wpa_msg(wpa_s, MSG_DEBUG, + "eap_proxy: IMSI %s (MNC length %d)", + wpa_s->imsi, wpa_s->mnc_len); + } else { + wpa_msg(wpa_s, MSG_DEBUG, + "eap_proxy: IMSI not available"); + } + } +#endif /* CONFIG_EAP_PROXY */ for (cred = wpa_s->conf->cred; cred; cred = cred->next) { char *sep; const char *imsi; int mnc_len; + char imsi_buf[16]; + size_t msin_len; #ifdef PCSC_FUNCS - if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard && - wpa_s->imsi[0]) { + if (cred->pcsc && wpa_s->scard) { + if (interworking_pcsc_read_imsi(wpa_s) < 0) + continue; imsi = wpa_s->imsi; mnc_len = wpa_s->mnc_len; goto compare; } #endif /* PCSC_FUNCS */ +#ifdef CONFIG_EAP_PROXY + if (cred->pcsc && wpa_s->mnc_len > 0 && wpa_s->imsi[0]) { + imsi = wpa_s->imsi; + mnc_len = wpa_s->mnc_len; + goto compare; + } +#endif /* CONFIG_EAP_PROXY */ if (cred->imsi == NULL || !cred->imsi[0] || - cred->milenage == NULL || !cred->milenage[0]) + (!wpa_s->conf->external_sim && + (cred->milenage == NULL || !cred->milenage[0]))) continue; sep = os_strchr(cred->imsi, '-'); @@ -1372,34 +1923,68 @@ static struct wpa_cred * interworking_credentials_available_3gpp( (sep - cred->imsi != 5 && sep - cred->imsi != 6)) continue; mnc_len = sep - cred->imsi - 3; - imsi = cred->imsi; - -#ifdef PCSC_FUNCS + os_memcpy(imsi_buf, cred->imsi, 3 + mnc_len); + sep++; + msin_len = os_strlen(cred->imsi); + if (3 + mnc_len + msin_len >= sizeof(imsi_buf) - 1) + msin_len = sizeof(imsi_buf) - 3 - mnc_len - 1; + os_memcpy(&imsi_buf[3 + mnc_len], sep, msin_len); + imsi_buf[3 + mnc_len + msin_len] = '\0'; + imsi = imsi_buf; + +#if defined(PCSC_FUNCS) || defined(CONFIG_EAP_PROXY) compare: -#endif /* PCSC_FUNCS */ - wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from " - MACSTR, MAC2STR(bss->bssid)); +#endif /* PCSC_FUNCS || CONFIG_EAP_PROXY */ + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Parsing 3GPP info from " MACSTR, + MAC2STR(bss->bssid)); ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len); - wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not "); + wpa_msg(wpa_s, MSG_DEBUG, "PLMN match %sfound", + ret ? "" : "not "); if (ret) { - if (cred_excluded_ssid(cred, bss)) + if (cred_no_required_oi_match(cred, bss)) continue; - if (selected == NULL || - selected->priority < cred->priority) - selected = cred; + if (!ignore_bw && + cred_below_min_backhaul(wpa_s, cred, bss)) + continue; + if (!ignore_bw && + cred_over_max_bss_load(wpa_s, cred, bss)) + continue; + if (!ignore_bw && + cred_conn_capab_missing(wpa_s, cred, bss)) + continue; + if (cred_excluded_ssid(cred, bss)) { + if (excluded == NULL) + continue; + if (selected == NULL) { + selected = cred; + is_excluded = 1; + } + } else { + if (selected == NULL || is_excluded || + cred_prio_cmp(selected, cred) < 0) { + selected = cred; + is_excluded = 0; + } + } } } + + if (excluded) + *excluded = is_excluded; #endif /* INTERWORKING_3GPP */ return selected; } static struct wpa_cred * interworking_credentials_available_realm( - struct wpa_supplicant *wpa_s, struct wpa_bss *bss) + struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw, + int *excluded) { struct wpa_cred *cred, *selected = NULL; struct nai_realm *realm; u16 count, i; + int is_excluded = 0; if (bss->anqp == NULL || bss->anqp->nai_realm == NULL) return NULL; @@ -1407,12 +1992,13 @@ static struct wpa_cred * interworking_credentials_available_realm( if (wpa_s->conf->cred == NULL) return NULL; - wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from " - MACSTR, MAC2STR(bss->bssid)); + wpa_msg(wpa_s, 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)); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Could not parse NAI Realm list from " + MACSTR, MAC2STR(bss->bssid)); return NULL; } @@ -1423,48 +2009,114 @@ static struct wpa_cred * interworking_credentials_available_realm( for (i = 0; i < count; i++) { if (!nai_realm_match(&realm[i], cred->realm)) continue; - if (nai_realm_find_eap(cred, &realm[i])) { - if (cred_excluded_ssid(cred, bss)) + if (nai_realm_find_eap(wpa_s, cred, &realm[i])) { + if (cred_no_required_oi_match(cred, bss)) continue; - if (selected == NULL || - selected->priority < cred->priority) - selected = cred; + if (!ignore_bw && + cred_below_min_backhaul(wpa_s, cred, bss)) + continue; + if (!ignore_bw && + cred_over_max_bss_load(wpa_s, cred, bss)) + continue; + if (!ignore_bw && + cred_conn_capab_missing(wpa_s, cred, bss)) + continue; + if (cred_excluded_ssid(cred, bss)) { + if (excluded == NULL) + continue; + if (selected == NULL) { + selected = cred; + is_excluded = 1; + } + } else { + if (selected == NULL || is_excluded || + cred_prio_cmp(selected, cred) < 0) + { + selected = cred; + is_excluded = 0; + } + } break; + } else { + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: realm-find-eap returned false"); } } } nai_realm_free(realm, count); + if (excluded) + *excluded = is_excluded; + return selected; } -static struct wpa_cred * interworking_credentials_available( - struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +static struct wpa_cred * interworking_credentials_available_helper( + struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw, + int *excluded) { struct wpa_cred *cred, *cred2; + int excluded1, excluded2; - cred = interworking_credentials_available_realm(wpa_s, bss); - cred2 = interworking_credentials_available_3gpp(wpa_s, bss); - if (cred && cred2 && cred2->priority >= cred->priority) + if (disallowed_bssid(wpa_s, bss->bssid) || + disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) { + wpa_printf(MSG_DEBUG, "Interworking: Ignore disallowed BSS " + MACSTR, MAC2STR(bss->bssid)); + return NULL; + } + + cred = interworking_credentials_available_realm(wpa_s, bss, ignore_bw, + &excluded1); + cred2 = interworking_credentials_available_3gpp(wpa_s, bss, ignore_bw, + &excluded2); + if (cred && cred2 && + (cred_prio_cmp(cred2, cred) >= 0 || (!excluded2 && excluded1))) { cred = cred2; - if (!cred) + excluded1 = excluded2; + } + if (!cred) { cred = cred2; + excluded1 = excluded2; + } - cred2 = interworking_credentials_available_roaming_consortium(wpa_s, - bss); - if (cred && cred2 && cred2->priority >= cred->priority) + cred2 = interworking_credentials_available_roaming_consortium( + wpa_s, bss, ignore_bw, &excluded2); + if (cred && cred2 && + (cred_prio_cmp(cred2, cred) >= 0 || (!excluded2 && excluded1))) { cred = cred2; - if (!cred) + excluded1 = excluded2; + } + if (!cred) { cred = cred2; + excluded1 = excluded2; + } + if (excluded) + *excluded = excluded1; return cred; } -static int domain_name_list_contains(struct wpabuf *domain_names, - const char *domain) +static struct wpa_cred * interworking_credentials_available( + struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int *excluded) +{ + struct wpa_cred *cred; + + if (excluded) + *excluded = 0; + cred = interworking_credentials_available_helper(wpa_s, bss, 0, + excluded); + if (cred) + return cred; + return interworking_credentials_available_helper(wpa_s, bss, 1, + excluded); +} + + +int domain_name_list_contains(struct wpabuf *domain_names, + const char *domain, int exact_match) { const u8 *pos, *end; size_t len; @@ -1482,6 +2134,12 @@ static int domain_name_list_contains(struct wpabuf *domain_names, if (pos[0] == len && os_strncasecmp(domain, (const char *) (pos + 1), len) == 0) return 1; + if (!exact_match && pos[0] > len && pos[pos[0] - len] == '.') { + const char *ap = (const char *) (pos + 1); + int offset = pos[0] - len; + if (os_strncasecmp(domain, ap + offset, len) == 0) + return 1; + } pos += 1 + pos[0]; } @@ -1494,6 +2152,8 @@ int interworking_home_sp_cred(struct wpa_supplicant *wpa_s, struct wpa_cred *cred, struct wpabuf *domain_names) { + size_t i; + int ret = -1; #ifdef INTERWORKING_3GPP char nai[100], *realm; @@ -1501,33 +2161,46 @@ int interworking_home_sp_cred(struct wpa_supplicant *wpa_s, int mnc_len = 0; if (cred->imsi) imsi = cred->imsi; -#ifdef CONFIG_PCSC - else if (cred->pcsc && wpa_s->conf->pcsc_reader && - wpa_s->scard && wpa_s->imsi[0]) { +#ifdef PCSC_FUNCS + else if (cred->pcsc && wpa_s->scard) { + if (interworking_pcsc_read_imsi(wpa_s) < 0) + return -1; imsi = wpa_s->imsi; mnc_len = wpa_s->mnc_len; } -#endif /* CONFIG_PCSC */ +#endif /* PCSC_FUNCS */ +#ifdef CONFIG_EAP_PROXY + else if (cred->pcsc && wpa_s->mnc_len > 0 && wpa_s->imsi[0]) { + imsi = wpa_s->imsi; + mnc_len = wpa_s->mnc_len; + } +#endif /* CONFIG_EAP_PROXY */ if (domain_names && imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0) == 0) { realm = os_strchr(nai, '@'); if (realm) realm++; - wpa_printf(MSG_DEBUG, "Interworking: Search for match " - "with SIM/USIM domain %s", realm); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Search for match with SIM/USIM domain %s", + realm); if (realm && - domain_name_list_contains(domain_names, realm)) + domain_name_list_contains(domain_names, realm, 1)) return 1; + if (realm) + ret = 0; } #endif /* INTERWORKING_3GPP */ if (domain_names == NULL || cred->domain == NULL) - return 0; + return ret; - wpa_printf(MSG_DEBUG, "Interworking: Search for match with " - "home SP FQDN %s", cred->domain); - if (domain_name_list_contains(domain_names, cred->domain)) - return 1; + for (i = 0; i < cred->num_domain; i++) { + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Search for match with home SP FQDN %s", + cred->domain[i]); + if (domain_name_list_contains(domain_names, cred->domain[i], 1)) + return 1; + } return 0; } @@ -1577,32 +2250,143 @@ static int interworking_find_network_match(struct wpa_supplicant *wpa_s) } +static int roaming_partner_match(struct wpa_supplicant *wpa_s, + struct roaming_partner *partner, + struct wpabuf *domain_names) +{ + wpa_printf(MSG_DEBUG, "Interworking: Comparing roaming_partner info fqdn='%s' exact_match=%d priority=%u country='%s'", + partner->fqdn, partner->exact_match, partner->priority, + partner->country); + wpa_hexdump_ascii(MSG_DEBUG, "Interworking: Domain names", + wpabuf_head(domain_names), + wpabuf_len(domain_names)); + if (!domain_name_list_contains(domain_names, partner->fqdn, + partner->exact_match)) + return 0; + /* TODO: match Country */ + return 1; +} + + +static u8 roaming_prio(struct wpa_supplicant *wpa_s, struct wpa_cred *cred, + struct wpa_bss *bss) +{ + size_t i; + + if (bss->anqp == NULL || bss->anqp->domain_name == NULL) { + wpa_printf(MSG_DEBUG, "Interworking: No ANQP domain name info -> use default roaming partner priority 128"); + return 128; /* cannot check preference with domain name */ + } + + if (interworking_home_sp_cred(wpa_s, cred, bss->anqp->domain_name) > 0) + { + wpa_printf(MSG_DEBUG, "Interworking: Determined to be home SP -> use maximum preference 0 as roaming partner priority"); + return 0; /* max preference for home SP network */ + } + + for (i = 0; i < cred->num_roaming_partner; i++) { + if (roaming_partner_match(wpa_s, &cred->roaming_partner[i], + bss->anqp->domain_name)) { + wpa_printf(MSG_DEBUG, "Interworking: Roaming partner preference match - priority %u", + cred->roaming_partner[i].priority); + return cred->roaming_partner[i].priority; + } + } + + wpa_printf(MSG_DEBUG, "Interworking: No roaming partner preference match - use default roaming partner priority 128"); + return 128; +} + + +static struct wpa_bss * pick_best_roaming_partner(struct wpa_supplicant *wpa_s, + struct wpa_bss *selected, + struct wpa_cred *cred) +{ + struct wpa_bss *bss; + u8 best_prio, prio; + struct wpa_cred *cred2; + + /* + * Check if any other BSS is operated by a more preferred roaming + * partner. + */ + + best_prio = roaming_prio(wpa_s, cred, selected); + wpa_printf(MSG_DEBUG, "Interworking: roaming_prio=%u for selected BSS " + MACSTR " (cred=%d)", best_prio, MAC2STR(selected->bssid), + cred->id); + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (bss == selected) + continue; + cred2 = interworking_credentials_available(wpa_s, bss, NULL); + if (!cred2) + continue; + if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) + continue; + prio = roaming_prio(wpa_s, cred2, bss); + wpa_printf(MSG_DEBUG, "Interworking: roaming_prio=%u for BSS " + MACSTR " (cred=%d)", prio, MAC2STR(bss->bssid), + cred2->id); + if (prio < best_prio) { + int bh1, bh2, load1, load2, conn1, conn2; + bh1 = cred_below_min_backhaul(wpa_s, cred, selected); + load1 = cred_over_max_bss_load(wpa_s, cred, selected); + conn1 = cred_conn_capab_missing(wpa_s, cred, selected); + bh2 = cred_below_min_backhaul(wpa_s, cred2, bss); + load2 = cred_over_max_bss_load(wpa_s, cred2, bss); + conn2 = cred_conn_capab_missing(wpa_s, cred2, bss); + wpa_printf(MSG_DEBUG, "Interworking: old: %d %d %d new: %d %d %d", + bh1, load1, conn1, bh2, load2, conn2); + if (bh1 || load1 || conn1 || !(bh2 || load2 || conn2)) { + wpa_printf(MSG_DEBUG, "Interworking: Better roaming partner " MACSTR " selected", MAC2STR(bss->bssid)); + best_prio = prio; + selected = bss; + } + } + } + + return selected; +} + + static void interworking_select_network(struct wpa_supplicant *wpa_s) { struct wpa_bss *bss, *selected = NULL, *selected_home = NULL; - int selected_prio = -999999, selected_home_prio = -999999; + struct wpa_bss *selected2 = NULL, *selected2_home = NULL; unsigned int count = 0; const char *type; int res; - struct wpa_cred *cred; + struct wpa_cred *cred, *selected_cred = NULL; + struct wpa_cred *selected_home_cred = NULL; + struct wpa_cred *selected2_cred = NULL; + struct wpa_cred *selected2_home_cred = NULL; wpa_s->network_select = 0; + wpa_printf(MSG_DEBUG, "Interworking: Select network (auto_select=%d)", + wpa_s->auto_select); dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { - cred = interworking_credentials_available(wpa_s, bss); + int excluded = 0; + int bh, bss_load, conn_capab; + cred = interworking_credentials_available(wpa_s, bss, + &excluded); if (!cred) continue; + if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) { /* * We currently support only HS 2.0 networks and those * are required to use WPA2-Enterprise. */ - wpa_printf(MSG_DEBUG, "Interworking: Credential match " - "with " MACSTR " but network does not use " - "RSN", MAC2STR(bss->bssid)); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Credential match with " MACSTR + " but network does not use RSN", + MAC2STR(bss->bssid)); continue; } - count++; + if (!excluded) + count++; res = interworking_home_sp(wpa_s, bss->anqp ? bss->anqp->domain_name : NULL); if (res > 0) @@ -1611,29 +2395,75 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s) type = "roaming"; else type = "unknown"; - wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s", - MAC2STR(bss->bssid), type); + bh = cred_below_min_backhaul(wpa_s, cred, bss); + bss_load = cred_over_max_bss_load(wpa_s, cred, bss); + conn_capab = cred_conn_capab_missing(wpa_s, cred, bss); + wpa_msg(wpa_s, MSG_INFO, "%s" MACSTR " type=%s%s%s%s id=%d priority=%d sp_priority=%d", + excluded ? INTERWORKING_BLACKLISTED : INTERWORKING_AP, + MAC2STR(bss->bssid), type, + bh ? " below_min_backhaul=1" : "", + bss_load ? " over_max_bss_load=1" : "", + conn_capab ? " conn_capab_missing=1" : "", + cred->id, cred->priority, cred->sp_priority); + if (excluded) + continue; if (wpa_s->auto_select || (wpa_s->conf->auto_interworking && wpa_s->auto_network_select)) { - if (selected == NULL || - cred->priority > selected_prio) { - selected = bss; - selected_prio = cred->priority; - } - if (res > 0 && - (selected_home == NULL || - cred->priority > selected_home_prio)) { - selected_home = bss; - selected_home_prio = cred->priority; + if (bh || bss_load || conn_capab) { + if (selected2_cred == NULL || + cred_prio_cmp(cred, selected2_cred) > 0) { + wpa_printf(MSG_DEBUG, "Interworking: Mark as selected2"); + selected2 = bss; + selected2_cred = cred; + } + if (res > 0 && + (selected2_home_cred == NULL || + cred_prio_cmp(cred, selected2_home_cred) > + 0)) { + wpa_printf(MSG_DEBUG, "Interworking: Mark as selected2_home"); + selected2_home = bss; + selected2_home_cred = cred; + } + } else { + if (selected_cred == NULL || + cred_prio_cmp(cred, selected_cred) > 0) { + wpa_printf(MSG_DEBUG, "Interworking: Mark as selected"); + selected = bss; + selected_cred = cred; + } + if (res > 0 && + (selected_home_cred == NULL || + cred_prio_cmp(cred, selected_home_cred) > + 0)) { + wpa_printf(MSG_DEBUG, "Interworking: Mark as selected_home"); + selected_home = bss; + selected_home_cred = cred; + } } } } if (selected_home && selected_home != selected && - selected_home_prio >= selected_prio) { + selected_home_cred && + (selected_cred == NULL || + cred_prio_cmp(selected_home_cred, selected_cred) >= 0)) { /* Prefer network operated by the Home SP */ + wpa_printf(MSG_DEBUG, "Interworking: Overrided selected with selected_home"); selected = selected_home; + selected_cred = selected_home_cred; + } + + if (!selected) { + if (selected2_home) { + wpa_printf(MSG_DEBUG, "Interworking: Use home BSS with BW limit mismatch since no other network could be selected"); + selected = selected2_home; + selected_cred = selected2_home_cred; + } else if (selected2) { + wpa_printf(MSG_DEBUG, "Interworking: Use visited BSS with BW limit mismatch since no other network could be selected"); + selected = selected2; + selected_cred = selected2_cred; + } } if (count == 0) { @@ -1643,16 +2473,17 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s) * have matching APs. */ if (interworking_find_network_match(wpa_s)) { - wpa_printf(MSG_DEBUG, "Interworking: Possible BSS " - "match for enabled network configurations"); - if (wpa_s->auto_select) + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Possible BSS match for enabled network configurations"); + if (wpa_s->auto_select) { interworking_reconnect(wpa_s); - return; + return; + } } if (wpa_s->auto_network_select) { - wpa_printf(MSG_DEBUG, "Interworking: Continue " - "scanning after ANQP fetch"); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Continue scanning after ANQP fetch"); wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval, 0); return; @@ -1660,10 +2491,22 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s) wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network " "with matching credentials found"); + if (wpa_s->wpa_state == WPA_SCANNING) + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + } + + if (selected) { + wpa_printf(MSG_DEBUG, "Interworking: Selected " MACSTR, + MAC2STR(selected->bssid)); + selected = pick_best_roaming_partner(wpa_s, selected, + selected_cred); + wpa_printf(MSG_DEBUG, "Interworking: Selected " MACSTR + " (after best roaming partner selection)", + MAC2STR(selected->bssid)); + wpa_msg(wpa_s, MSG_INFO, INTERWORKING_SELECTED MACSTR, + MAC2STR(selected->bssid)); + interworking_connect(wpa_s, selected, 0); } - - if (selected) - interworking_connect(wpa_s, selected); } @@ -1693,9 +2536,10 @@ interworking_match_anqp_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) os_memcmp(bss->ssid, other->ssid, bss->ssid_len) != 0) continue; - wpa_printf(MSG_DEBUG, "Interworking: Share ANQP data with " - "already fetched BSSID " MACSTR " and " MACSTR, - MAC2STR(other->bssid), MAC2STR(bss->bssid)); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Share ANQP data with already fetched BSSID " + MACSTR " and " MACSTR, + MAC2STR(other->bssid), MAC2STR(bss->bssid)); other->anqp->users++; return other->anqp; } @@ -1710,8 +2554,21 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) int found = 0; const u8 *ie; - if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress) + wpa_printf(MSG_DEBUG, "Interworking: next_anqp_fetch - " + "fetch_anqp_in_progress=%d fetch_osu_icon_in_progress=%d", + wpa_s->fetch_anqp_in_progress, + wpa_s->fetch_osu_icon_in_progress); + + if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress) { + wpa_printf(MSG_DEBUG, "Interworking: Stop next-ANQP-fetch"); return; + } + + if (wpa_s->fetch_osu_icon_in_progress) { + wpa_printf(MSG_DEBUG, "Interworking: Next icon (in progress)"); + hs20_next_osu_icon(wpa_s); + return; + } dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { if (!(bss->caps & IEEE80211_CAP_ESS)) @@ -1719,6 +2576,9 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) 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 (disallowed_bssid(wpa_s, bss->bssid) || + disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) + continue; /* Disallowed BSS */ if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) { if (bss->anqp == NULL) { @@ -1742,6 +2602,18 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s) } if (found == 0) { + if (wpa_s->fetch_osu_info) { + if (wpa_s->num_prov_found == 0 && + wpa_s->fetch_osu_waiting_scan && + wpa_s->num_osu_scans < 3) { + wpa_printf(MSG_DEBUG, "HS 2.0: No OSU providers seen - try to scan again"); + hs20_start_osu_scan(wpa_s); + return; + } + wpa_printf(MSG_DEBUG, "Interworking: Next icon"); + hs20_osu_icon_fetch(wpa_s); + return; + } wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed"); wpa_s->fetch_anqp_in_progress = 0; if (wpa_s->network_select) @@ -1758,7 +2630,12 @@ void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s) bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED; wpa_s->fetch_anqp_in_progress = 1; - interworking_next_anqp_fetch(wpa_s); + + /* + * Start actual ANQP operation from eloop call to make sure the loop + * does not end up using excessive recursion. + */ + eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s, NULL); } @@ -1769,6 +2646,7 @@ int interworking_fetch_anqp(struct wpa_supplicant *wpa_s) wpa_s->network_select = 0; wpa_s->fetch_all_anqp = 1; + wpa_s->fetch_osu_info = 0; interworking_start_fetch_anqp(wpa_s); @@ -1786,9 +2664,10 @@ void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s) int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, - u16 info_ids[], size_t num_ids) + u16 info_ids[], size_t num_ids, u32 subtypes) { struct wpabuf *buf; + struct wpabuf *hs20_buf = NULL; int ret = 0; int freq; struct wpa_bss *bss; @@ -1803,32 +2682,44 @@ int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, if (freq <= 0) return -1; - wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)", - MAC2STR(dst), (unsigned int) num_ids); + wpa_msg(wpa_s, MSG_DEBUG, + "ANQP: Query Request to " MACSTR " for %u id(s)", + MAC2STR(dst), (unsigned int) num_ids); + +#ifdef CONFIG_HS20 + if (subtypes != 0) { + hs20_buf = wpabuf_alloc(100); + if (hs20_buf == NULL) + return -1; + hs20_put_anqp_req(subtypes, NULL, 0, hs20_buf); + } +#endif /* CONFIG_HS20 */ - buf = anqp_build_req(info_ids, num_ids, NULL); + buf = anqp_build_req(info_ids, num_ids, hs20_buf); + wpabuf_free(hs20_buf); 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"); + wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request"); + wpabuf_free(buf); ret = -1; - } else - wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token " - "%u", res); + } else { + wpa_msg(wpa_s, 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, + struct wpa_bss *bss, 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); struct wpa_bss_anqp *anqp = NULL; #ifdef CONFIG_HS20 u8 type; @@ -1841,6 +2732,12 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, case ANQP_CAPABILITY_LIST: wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR " ANQP Capability list", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Capability list", + pos, slen); + if (anqp) { + wpabuf_free(anqp->capability_list); + anqp->capability_list = wpabuf_alloc_copy(pos, slen); + } break; case ANQP_VENUE_NAME: wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR @@ -1929,26 +2826,27 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, switch (type) { case HS20_ANQP_OUI_TYPE: - hs20_parse_rx_hs20_anqp_resp(wpa_s, sa, pos, - slen); + hs20_parse_rx_hs20_anqp_resp(wpa_s, bss, sa, + pos, slen); break; default: - wpa_printf(MSG_DEBUG, "HS20: Unsupported ANQP " - "vendor type %u", type); + wpa_msg(wpa_s, MSG_DEBUG, + "HS20: Unsupported ANQP vendor type %u", + type); break; } break; #endif /* CONFIG_HS20 */ default: - wpa_printf(MSG_DEBUG, "Interworking: Unsupported " - "vendor-specific ANQP OUI %06x", - WPA_GET_BE24(pos)); + wpa_msg(wpa_s, 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); + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Unsupported ANQP Info ID %u", info_id); break; } } @@ -1964,62 +2862,108 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, const u8 *end; u16 info_id; u16 slen; + struct wpa_bss *bss = NULL, *tmp; + const char *anqp_result = "SUCCESS"; - if (result != GAS_QUERY_SUCCESS) - return; + wpa_printf(MSG_DEBUG, "Interworking: anqp_resp_cb dst=" MACSTR + " dialog_token=%u result=%d status_code=%u", + MAC2STR(dst), dialog_token, result, status_code); + if (result != GAS_QUERY_SUCCESS) { + if (wpa_s->fetch_osu_icon_in_progress) + hs20_icon_fetch_failed(wpa_s); + anqp_result = "FAILURE"; + goto out; + } 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; + wpa_msg(wpa_s, MSG_DEBUG, + "ANQP: Unexpected Advertisement Protocol in response"); + if (wpa_s->fetch_osu_icon_in_progress) + hs20_icon_fetch_failed(wpa_s); + anqp_result = "INVALID_FRAME"; + goto out; } + /* + * If possible, select the BSS entry based on which BSS entry was used + * for the request. This can help in cases where multiple BSS entries + * may exist for the same AP. + */ + dl_list_for_each_reverse(tmp, &wpa_s->bss, struct wpa_bss, list) { + if (tmp == wpa_s->interworking_gas_bss && + os_memcmp(tmp->bssid, dst, ETH_ALEN) == 0) { + bss = tmp; + break; + } + } + if (bss == NULL) + bss = wpa_bss_get_bssid(wpa_s, dst); + pos = wpabuf_head(resp); end = pos + wpabuf_len(resp); while (pos < end) { - if (pos + 4 > end) { - wpa_printf(MSG_DEBUG, "ANQP: Invalid element"); - break; + unsigned int left = end - pos; + + if (left < 4) { + wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Invalid element"); + anqp_result = "INVALID_FRAME"; + goto out_parse_done; } 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; + left -= 4; + if (left < slen) { + wpa_msg(wpa_s, MSG_DEBUG, + "ANQP: Invalid element length for Info ID %u", + info_id); + anqp_result = "INVALID_FRAME"; + goto out_parse_done; } - interworking_parse_rx_anqp_resp(wpa_s, dst, info_id, pos, + interworking_parse_rx_anqp_resp(wpa_s, bss, dst, info_id, pos, slen); pos += slen; } + +out_parse_done: + hs20_notify_parse_done(wpa_s); +out: + wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s", + MAC2STR(dst), anqp_result); } 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"); + wpa_msg(wpa_s, 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) +int interworking_select(struct wpa_supplicant *wpa_s, int auto_select, + int *freqs) { interworking_stop_fetch_anqp(wpa_s); wpa_s->network_select = 1; wpa_s->auto_network_select = 0; wpa_s->auto_select = !!auto_select; wpa_s->fetch_all_anqp = 0; - wpa_printf(MSG_DEBUG, "Interworking: Start scan for network " - "selection"); + wpa_s->fetch_osu_info = 0; + wpa_msg(wpa_s, MSG_DEBUG, + "Interworking: Start scan for network selection"); wpa_s->scan_res_handler = interworking_scan_res_handler; + wpa_s->normal_scans = 0; wpa_s->scan_req = MANUAL_SCAN_REQ; + os_free(wpa_s->manual_scan_freqs); + wpa_s->manual_scan_freqs = freqs; + wpa_s->after_wps = 0; + wpa_s->known_wps_freq = 0; wpa_supplicant_req_scan(wpa_s, 0, 0); return 0; @@ -2032,6 +2976,7 @@ static void gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, const struct wpabuf *resp, u16 status_code) { struct wpa_supplicant *wpa_s = ctx; + struct wpabuf *n; wpa_msg(wpa_s, MSG_INFO, GAS_RESPONSE_INFO "addr=" MACSTR " dialog_token=%d status_code=%d resp_len=%d", @@ -2040,10 +2985,14 @@ static void gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, if (!resp) return; - wpabuf_free(wpa_s->last_gas_resp); - wpa_s->last_gas_resp = wpabuf_dup(resp); - if (wpa_s->last_gas_resp == NULL) + n = wpabuf_dup(resp); + if (n == NULL) return; + wpabuf_free(wpa_s->prev_gas_resp); + wpa_s->prev_gas_resp = wpa_s->last_gas_resp; + os_memcpy(wpa_s->prev_gas_addr, wpa_s->last_gas_addr, ETH_ALEN); + wpa_s->prev_gas_dialog_token = wpa_s->last_gas_dialog_token; + wpa_s->last_gas_resp = n; os_memcpy(wpa_s->last_gas_addr, addr, ETH_ALEN); wpa_s->last_gas_dialog_token = dialog_token; } @@ -2059,7 +3008,7 @@ int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst, struct wpa_bss *bss; int res; size_t len; - u8 query_resp_len_limit = 0, pame_bi = 0; + u8 query_resp_len_limit = 0; freq = wpa_s->assoc_freq; bss = wpa_bss_get_bssid(wpa_s, dst); @@ -2068,8 +3017,8 @@ int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst, if (freq <= 0) return -1; - wpa_printf(MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)", - MAC2STR(dst), freq); + wpa_msg(wpa_s, MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)", + MAC2STR(dst), freq); wpa_hexdump_buf(MSG_DEBUG, "Advertisement Protocol ID", adv_proto); wpa_hexdump_buf(MSG_DEBUG, "GAS Query", query); @@ -2083,8 +3032,7 @@ int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst, /* Advertisement Protocol IE */ wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); wpabuf_put_u8(buf, 1 + wpabuf_len(adv_proto)); /* Length */ - wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) | - (pame_bi ? 0x80 : 0)); + wpabuf_put_u8(buf, query_resp_len_limit & 0x7f); wpabuf_put_buf(buf, adv_proto); /* GAS Query */ @@ -2096,12 +3044,12 @@ int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst, res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s); if (res < 0) { - wpa_printf(MSG_DEBUG, "GAS: Failed to send Query Request"); + wpa_msg(wpa_s, MSG_DEBUG, "GAS: Failed to send Query Request"); + wpabuf_free(buf); ret = -1; } else - wpa_printf(MSG_DEBUG, "GAS: Query started with dialog token " - "%u", res); + wpa_msg(wpa_s, MSG_DEBUG, + "GAS: Query started with dialog token %u", res); - wpabuf_free(buf); return ret; } diff --git a/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h index 4a4af827bf5b8..3743dc00e905a 100644 --- a/wpa_supplicant/interworking.h +++ b/wpa_supplicant/interworking.h @@ -12,7 +12,7 @@ enum gas_query_result; int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, - u16 info_ids[], size_t num_ids); + u16 info_ids[], size_t num_ids, u32 subtypes); void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, enum gas_query_result result, const struct wpabuf *adv_proto, @@ -22,11 +22,15 @@ int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst, const struct wpabuf *query); 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); +int interworking_select(struct wpa_supplicant *wpa_s, int auto_select, + int *freqs); +int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + int only_add); void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s); int interworking_home_sp_cred(struct wpa_supplicant *wpa_s, struct wpa_cred *cred, struct wpabuf *domain_names); +int domain_name_list_contains(struct wpabuf *domain_names, + const char *domain, int exact_match); #endif /* INTERWORKING_H */ diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c index 19f7ce6679edd..22827479c6435 100644 --- a/wpa_supplicant/main.c +++ b/wpa_supplicant/main.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / main() function for UNIX like OSes and MinGW - * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,8 +14,7 @@ #include "common.h" #include "wpa_supplicant_i.h" #include "driver_i.h" - -extern struct wpa_driver_ops *wpa_drivers[]; +#include "p2p_supplicant.h" static void usage(void) @@ -23,16 +22,32 @@ static void usage(void) int i; printf("%s\n\n%s\n" "usage:\n" - " wpa_supplicant [-BddhKLqqstuvW] [-P<pid file>] " + " wpa_supplicant [-BddhKLqq" +#ifdef CONFIG_DEBUG_SYSLOG + "s" +#endif /* CONFIG_DEBUG_SYSLOG */ + "t" +#ifdef CONFIG_DBUS + "u" +#endif /* CONFIG_DBUS */ + "vW] [-P<pid file>] " "[-g<global ctrl>] \\\n" + " [-G<group>] \\\n" " -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] " "[-p<driver_param>] \\\n" - " [-b<br_ifname>] [-f<debug file>] [-e<entropy file>] " - "\\\n" + " [-b<br_ifname>] [-e<entropy file>]" +#ifdef CONFIG_DEBUG_FILE + " [-f<debug file>]" +#endif /* CONFIG_DEBUG_FILE */ + " \\\n" " [-o<override driver>] [-O<override ctrl>] \\\n" " [-N -i<ifname> -c<conf> [-C<ctrl>] " "[-D<driver>] \\\n" - " [-p<driver_param>] [-b<br_ifname>] ...]\n" +#ifdef CONFIG_P2P + " [-m<P2P Device config file>] \\\n" +#endif /* CONFIG_P2P */ + " [-p<driver_param>] [-b<br_ifname>] [-I<config file>] " + "...]\n" "\n" "drivers:\n", wpa_supplicant_version, wpa_supplicant_license); @@ -50,6 +65,7 @@ static void usage(void) " -c = Configuration file\n" " -C = ctrl_interface parameter (only used if -c is not)\n" " -i = interface name\n" + " -I = additional configuration file\n" " -d = increase debugging verbosity (-dd even more)\n" " -D = driver name (can be multiple drivers: nl80211,wext)\n" " -e = entropy file\n"); @@ -57,6 +73,7 @@ static void usage(void) printf(" -f = log output to debug file instead of stdout\n"); #endif /* CONFIG_DEBUG_FILE */ printf(" -g = global ctrl_interface\n" + " -G = global ctrl_interface group\n" " -K = include keys (passwords, etc.) in debug output\n"); #ifdef CONFIG_DEBUG_SYSLOG printf(" -s = log output to syslog instead of stdout\n"); @@ -78,11 +95,14 @@ static void usage(void) #endif /* CONFIG_DBUS */ printf(" -v = show version\n" " -W = wait for a control interface monitor before starting\n" +#ifdef CONFIG_P2P + " -m = Configuration file for the P2P Device interface\n" +#endif /* CONFIG_P2P */ " -N = start describing new interface\n"); printf("example:\n" " wpa_supplicant -D%s -iwlan0 -c/etc/wpa_supplicant.conf\n", - wpa_drivers[i] ? wpa_drivers[i]->name : "wext"); + wpa_drivers[0] ? wpa_drivers[0]->name : "nl80211"); #endif /* CONFIG_NO_STDOUT_DEBUG */ } @@ -155,7 +175,7 @@ int main(int argc, char *argv[]) for (;;) { c = getopt(argc, argv, - "b:Bc:C:D:de:f:g:hi:KLNo:O:p:P:qsTtuvW"); + "b:Bc:C:D:de:f:g:G:hi:I:KLm:No:O:p:P:qsTtuvW"); if (c < 0) break; switch (c) { @@ -195,6 +215,9 @@ int main(int argc, char *argv[]) case 'g': params.ctrl_interface = optarg; break; + case 'G': + params.ctrl_interface_group = optarg; + break; case 'h': usage(); exitcode = 0; @@ -202,6 +225,9 @@ int main(int argc, char *argv[]) case 'i': iface->ifname = optarg; break; + case 'I': + iface->confanother = optarg; + break; case 'K': params.wpa_debug_show_keys++; break; @@ -209,6 +235,11 @@ int main(int argc, char *argv[]) license(); exitcode = 0; goto out; +#ifdef CONFIG_P2P + case 'm': + iface->conf_p2p_dev = optarg; + break; +#endif /* CONFIG_P2P */ case 'o': params.override_driver = optarg; break; @@ -279,6 +310,8 @@ int main(int argc, char *argv[]) } for (i = 0; exitcode == 0 && i < iface_count; i++) { + struct wpa_supplicant *wpa_s; + if ((ifaces[i].confname == NULL && ifaces[i].ctrl_interface == NULL) || ifaces[i].ifname == NULL) { @@ -289,8 +322,11 @@ int main(int argc, char *argv[]) exitcode = -1; break; } - if (wpa_supplicant_add_iface(global, &ifaces[i]) == NULL) + wpa_s = wpa_supplicant_add_iface(global, &ifaces[i], NULL); + if (wpa_s == NULL) { exitcode = -1; + break; + } } if (exitcode == 0) diff --git a/wpa_supplicant/main_none.c b/wpa_supplicant/main_none.c index 010c30a3037df..4d3caf2a4da38 100644 --- a/wpa_supplicant/main_none.c +++ b/wpa_supplicant/main_none.c @@ -28,7 +28,7 @@ int main(int argc, char *argv[]) memset(&iface, 0, sizeof(iface)); /* TODO: set interface parameters */ - if (wpa_supplicant_add_iface(global, &iface) == NULL) + if (wpa_supplicant_add_iface(global, &iface, NULL) == NULL) exitcode = -1; if (exitcode == 0) diff --git a/wpa_supplicant/main_winmain.c b/wpa_supplicant/main_winmain.c index 93a68f17bc3d8..e1dded0c349a7 100644 --- a/wpa_supplicant/main_winmain.c +++ b/wpa_supplicant/main_winmain.c @@ -61,7 +61,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, exitcode = -1; break; } - if (wpa_supplicant_add_iface(global, &ifaces[i]) == NULL) + if (wpa_supplicant_add_iface(global, &ifaces[i], NULL) == NULL) exitcode = -1; } diff --git a/wpa_supplicant/main_winsvc.c b/wpa_supplicant/main_winsvc.c index 0b7d5ce069625..9950aa99ae7a8 100644 --- a/wpa_supplicant/main_winsvc.c +++ b/wpa_supplicant/main_winsvc.c @@ -119,7 +119,7 @@ static int read_interface(struct wpa_global *global, HKEY _hk, RegCloseKey(hk); - if (wpa_supplicant_add_iface(global, &iface) == NULL) { + if (wpa_supplicant_add_iface(global, &iface, NULL) == NULL) { if (skip_on_error) wpa_printf(MSG_DEBUG, "Skipped interface '%s' due to " "initialization failure", iface.ifname); diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c new file mode 100644 index 0000000000000..33b4af38faa86 --- /dev/null +++ b/wpa_supplicant/mesh.c @@ -0,0 +1,540 @@ +/* + * WPA Supplicant - Basic mesh mode routines + * Copyright (c) 2013-2014, cozybit, Inc. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/uuid.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" +#include "ap/sta_info.h" +#include "ap/hostapd.h" +#include "ap/ieee802_11.h" +#include "config_ssid.h" +#include "config.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "notify.h" +#include "ap.h" +#include "mesh_mpm.h" +#include "mesh_rsn.h" +#include "mesh.h" + + +static void wpa_supplicant_mesh_deinit(struct wpa_supplicant *wpa_s) +{ + wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh); + wpa_s->ifmsh = NULL; + wpa_s->current_ssid = NULL; + os_free(wpa_s->mesh_rsn); + wpa_s->mesh_rsn = NULL; + /* TODO: leave mesh (stop beacon). This will happen on link down + * anyway, so it's not urgent */ +} + + +void wpa_supplicant_mesh_iface_deinit(struct wpa_supplicant *wpa_s, + struct hostapd_iface *ifmsh) +{ + if (!ifmsh) + return; + + if (ifmsh->mconf) { + mesh_mpm_deinit(wpa_s, ifmsh); + if (ifmsh->mconf->ies) { + ifmsh->mconf->ies = NULL; + /* We cannot free this struct + * because wpa_authenticator on + * hostapd side is also using it + * for now just set to NULL and + * let hostapd code free it. + */ + } + os_free(ifmsh->mconf); + ifmsh->mconf = NULL; + } + + /* take care of shared data */ + hostapd_interface_deinit(ifmsh); + hostapd_interface_free(ifmsh); +} + + +static struct mesh_conf * mesh_config_create(struct wpa_ssid *ssid) +{ + struct mesh_conf *conf; + + conf = os_zalloc(sizeof(struct mesh_conf)); + if (!conf) + return NULL; + + os_memcpy(conf->meshid, ssid->ssid, ssid->ssid_len); + conf->meshid_len = ssid->ssid_len; + + if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) + conf->security |= MESH_CONF_SEC_AUTH | + MESH_CONF_SEC_AMPE; + else + conf->security |= MESH_CONF_SEC_NONE; + + /* defaults */ + conf->mesh_pp_id = MESH_PATH_PROTOCOL_HWMP; + conf->mesh_pm_id = MESH_PATH_METRIC_AIRTIME; + conf->mesh_cc_id = 0; + conf->mesh_sp_id = MESH_SYNC_METHOD_NEIGHBOR_OFFSET; + conf->mesh_auth_id = (conf->security & MESH_CONF_SEC_AUTH) ? 1 : 0; + conf->dot11MeshMaxRetries = ssid->dot11MeshMaxRetries; + conf->dot11MeshRetryTimeout = ssid->dot11MeshRetryTimeout; + conf->dot11MeshConfirmTimeout = ssid->dot11MeshConfirmTimeout; + conf->dot11MeshHoldingTimeout = ssid->dot11MeshHoldingTimeout; + + return conf; +} + + +static void wpas_mesh_copy_groups(struct hostapd_data *bss, + struct wpa_supplicant *wpa_s) +{ + int num_groups; + size_t groups_size; + + for (num_groups = 0; wpa_s->conf->sae_groups[num_groups] > 0; + num_groups++) + ; + + groups_size = (num_groups + 1) * sizeof(wpa_s->conf->sae_groups[0]); + bss->conf->sae_groups = os_malloc(groups_size); + if (bss->conf->sae_groups) + os_memcpy(bss->conf->sae_groups, wpa_s->conf->sae_groups, + groups_size); +} + + +static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct hostapd_iface *ifmsh; + struct hostapd_data *bss; + struct hostapd_config *conf; + struct mesh_conf *mconf; + int basic_rates_erp[] = { 10, 20, 55, 60, 110, 120, 240, -1 }; + static int default_groups[] = { 19, 20, 21, 25, 26, -1 }; + size_t len; + int rate_len; + + if (!wpa_s->conf->user_mpm) { + /* not much for us to do here */ + wpa_msg(wpa_s, MSG_WARNING, + "user_mpm is not enabled in configuration"); + return 0; + } + + wpa_s->ifmsh = ifmsh = os_zalloc(sizeof(*wpa_s->ifmsh)); + if (!ifmsh) + return -ENOMEM; + + ifmsh->drv_flags = wpa_s->drv_flags; + ifmsh->num_bss = 1; + ifmsh->bss = os_calloc(wpa_s->ifmsh->num_bss, + sizeof(struct hostapd_data *)); + if (!ifmsh->bss) + goto out_free; + + ifmsh->bss[0] = bss = os_zalloc(sizeof(struct hostapd_data)); + if (!bss) + goto out_free; + + os_memcpy(bss->own_addr, wpa_s->own_addr, ETH_ALEN); + bss->driver = wpa_s->driver; + bss->drv_priv = wpa_s->drv_priv; + bss->iface = ifmsh; + bss->mesh_sta_free_cb = mesh_mpm_free_sta; + wpa_s->assoc_freq = ssid->frequency; + wpa_s->current_ssid = ssid; + + /* setup an AP config for auth processing */ + conf = hostapd_config_defaults(); + if (!conf) + goto out_free; + + bss->conf = *conf->bss; + bss->conf->start_disabled = 1; + bss->conf->mesh = MESH_ENABLED; + bss->conf->ap_max_inactivity = wpa_s->conf->mesh_max_inactivity; + bss->iconf = conf; + ifmsh->conf = conf; + + ifmsh->bss[0]->max_plinks = wpa_s->conf->max_peer_links; + os_strlcpy(bss->conf->iface, wpa_s->ifname, sizeof(bss->conf->iface)); + + mconf = mesh_config_create(ssid); + if (!mconf) + goto out_free; + ifmsh->mconf = mconf; + + /* need conf->hw_mode for supported rates. */ + if (ssid->frequency == 0) { + conf->hw_mode = HOSTAPD_MODE_IEEE80211G; + conf->channel = 1; + } else { + conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, + &conf->channel); + } + if (conf->hw_mode == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_ERROR, "Unsupported mesh mode frequency: %d MHz", + ssid->frequency); + goto out_free; + } + + if (ssid->mesh_basic_rates == NULL) { + /* + * XXX: Hack! This is so an MPM which correctly sets the ERP + * mandatory rates as BSSBasicRateSet doesn't reject us. We + * could add a new hw_mode HOSTAPD_MODE_IEEE80211G_ERP, but + * this is way easier. This also makes our BSSBasicRateSet + * advertised in beacons match the one in peering frames, sigh. + */ + if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) { + conf->basic_rates = os_malloc(sizeof(basic_rates_erp)); + if (!conf->basic_rates) + goto out_free; + os_memcpy(conf->basic_rates, basic_rates_erp, + sizeof(basic_rates_erp)); + } + } else { + rate_len = 0; + while (1) { + if (ssid->mesh_basic_rates[rate_len] < 1) + break; + rate_len++; + } + conf->basic_rates = os_calloc(rate_len + 1, sizeof(int)); + if (conf->basic_rates == NULL) + goto out_free; + os_memcpy(conf->basic_rates, ssid->mesh_basic_rates, + rate_len * sizeof(int)); + conf->basic_rates[rate_len] = -1; + } + + if (hostapd_setup_interface(ifmsh)) { + wpa_printf(MSG_ERROR, + "Failed to initialize hostapd interface for mesh"); + return -1; + } + + if (wpa_drv_init_mesh(wpa_s)) { + wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh in driver"); + return -1; + } + + if (mconf->security != MESH_CONF_SEC_NONE) { + if (ssid->passphrase == NULL) { + wpa_printf(MSG_ERROR, + "mesh: Passphrase for SAE not configured"); + goto out_free; + } + + bss->conf->wpa = ssid->proto; + bss->conf->wpa_key_mgmt = ssid->key_mgmt; + + if (wpa_s->conf->sae_groups && + wpa_s->conf->sae_groups[0] > 0) { + wpas_mesh_copy_groups(bss, wpa_s); + } else { + bss->conf->sae_groups = + os_malloc(sizeof(default_groups)); + if (!bss->conf->sae_groups) + goto out_free; + os_memcpy(bss->conf->sae_groups, default_groups, + sizeof(default_groups)); + } + + len = os_strlen(ssid->passphrase); + bss->conf->ssid.wpa_passphrase = + dup_binstr(ssid->passphrase, len); + + wpa_s->mesh_rsn = mesh_rsn_auth_init(wpa_s, mconf); + if (!wpa_s->mesh_rsn) + goto out_free; + } + + wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf); + + return 0; +out_free: + wpa_supplicant_mesh_deinit(wpa_s); + return -ENOMEM; +} + + +void wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s, const u8 *addr, + const u8 *ies, size_t ie_len) +{ + struct ieee802_11_elems elems; + + wpa_msg(wpa_s, MSG_INFO, + "new peer notification for " MACSTR, MAC2STR(addr)); + + if (ieee802_11_parse_elems(ies, ie_len, &elems, 0) == ParseFailed) { + wpa_msg(wpa_s, MSG_INFO, "Could not parse beacon from " MACSTR, + MAC2STR(addr)); + return; + } + wpa_mesh_new_mesh_peer(wpa_s, addr, &elems); +} + + +void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s, + struct wpabuf **extra_ie) +{ + /* EID + 0-length (wildcard) mesh-id */ + size_t ielen = 2; + + if (wpabuf_resize(extra_ie, ielen) == 0) { + wpabuf_put_u8(*extra_ie, WLAN_EID_MESH_ID); + wpabuf_put_u8(*extra_ie, 0); + } +} + + +int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct wpa_driver_mesh_join_params params; + int ret = 0; + + if (!ssid || !ssid->ssid || !ssid->ssid_len || !ssid->frequency) { + ret = -ENOENT; + goto out; + } + + wpa_supplicant_mesh_deinit(wpa_s); + + os_memset(¶ms, 0, sizeof(params)); + params.meshid = ssid->ssid; + params.meshid_len = ssid->ssid_len; + ibss_mesh_setup_freq(wpa_s, ssid, ¶ms.freq); + wpa_s->mesh_ht_enabled = !!params.freq.ht_enabled; + if (ssid->beacon_int > 0) + params.beacon_int = ssid->beacon_int; + else if (wpa_s->conf->beacon_int > 0) + params.beacon_int = wpa_s->conf->beacon_int; + params.max_peer_links = wpa_s->conf->max_peer_links; + + if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { + params.flags |= WPA_DRIVER_MESH_FLAG_SAE_AUTH; + params.flags |= WPA_DRIVER_MESH_FLAG_AMPE; + wpa_s->conf->user_mpm = 1; + } + + if (wpa_s->conf->user_mpm) { + params.flags |= WPA_DRIVER_MESH_FLAG_USER_MPM; + params.conf.flags &= ~WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS; + } else { + params.flags |= WPA_DRIVER_MESH_FLAG_DRIVER_MPM; + params.conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS; + } + params.conf.peer_link_timeout = wpa_s->conf->mesh_max_inactivity; + + if (wpa_supplicant_mesh_init(wpa_s, ssid)) { + wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh"); + wpa_drv_leave_mesh(wpa_s); + ret = -1; + goto out; + } + + if (wpa_s->ifmsh) { + params.ies = wpa_s->ifmsh->mconf->ies; + params.ie_len = wpa_s->ifmsh->mconf->ie_len; + params.basic_rates = wpa_s->ifmsh->basic_rates; + } + + wpa_msg(wpa_s, MSG_INFO, "joining mesh %s", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + ret = wpa_drv_join_mesh(wpa_s, ¶ms); + if (ret) + wpa_msg(wpa_s, MSG_ERROR, "mesh join error=%d\n", ret); + + /* hostapd sets the interface down until we associate */ + wpa_drv_set_operstate(wpa_s, 1); + +out: + return ret; +} + + +int wpa_supplicant_leave_mesh(struct wpa_supplicant *wpa_s) +{ + int ret = 0; + + wpa_msg(wpa_s, MSG_INFO, "leaving mesh"); + + /* Need to send peering close messages first */ + wpa_supplicant_mesh_deinit(wpa_s); + + ret = wpa_drv_leave_mesh(wpa_s); + if (ret) + wpa_msg(wpa_s, MSG_ERROR, "mesh leave error=%d", ret); + + wpa_drv_set_operstate(wpa_s, 1); + + return ret; +} + + +static int mesh_attr_text(const u8 *ies, size_t ies_len, char *buf, char *end) +{ + struct ieee802_11_elems elems; + char *mesh_id, *pos = buf; + u8 *bss_basic_rate_set; + int bss_basic_rate_set_len, ret, i; + + if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) == ParseFailed) + return -1; + + if (elems.mesh_id_len < 1) + return 0; + + mesh_id = os_malloc(elems.mesh_id_len + 1); + if (mesh_id == NULL) + return -1; + + os_memcpy(mesh_id, elems.mesh_id, elems.mesh_id_len); + mesh_id[elems.mesh_id_len] = '\0'; + ret = os_snprintf(pos, end - pos, "mesh_id=%s\n", mesh_id); + os_free(mesh_id); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + if (elems.mesh_config_len > 6) { + ret = os_snprintf(pos, end - pos, + "active_path_selection_protocol_id=0x%02x\n" + "active_path_selection_metric_id=0x%02x\n" + "congestion_control_mode_id=0x%02x\n" + "synchronization_method_id=0x%02x\n" + "authentication_protocol_id=0x%02x\n" + "mesh_formation_info=0x%02x\n" + "mesh_capability=0x%02x\n", + elems.mesh_config[0], elems.mesh_config[1], + elems.mesh_config[2], elems.mesh_config[3], + elems.mesh_config[4], elems.mesh_config[5], + elems.mesh_config[6]); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + bss_basic_rate_set = os_malloc(elems.supp_rates_len + + elems.ext_supp_rates_len); + if (bss_basic_rate_set == NULL) + return -1; + + bss_basic_rate_set_len = 0; + for (i = 0; i < elems.supp_rates_len; i++) { + if (elems.supp_rates[i] & 0x80) { + bss_basic_rate_set[bss_basic_rate_set_len++] = + (elems.supp_rates[i] & 0x7f) * 5; + } + } + for (i = 0; i < elems.ext_supp_rates_len; i++) { + if (elems.ext_supp_rates[i] & 0x80) { + bss_basic_rate_set[bss_basic_rate_set_len++] = + (elems.ext_supp_rates[i] & 0x7f) * 5; + } + } + if (bss_basic_rate_set_len > 0) { + ret = os_snprintf(pos, end - pos, "bss_basic_rate_set=%d", + bss_basic_rate_set[0]); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + for (i = 1; i < bss_basic_rate_set_len; i++) { + ret = os_snprintf(pos, end - pos, " %d", + bss_basic_rate_set[i]); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "\n"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + os_free(bss_basic_rate_set); + + return pos - buf; +} + + +int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf, + char *end) +{ + return mesh_attr_text(ies, ies_len, buf, end); +} + + +static int wpas_mesh_get_ifname(struct wpa_supplicant *wpa_s, char *ifname, + size_t len) +{ + char *ifname_ptr = wpa_s->ifname; + int res; + + res = os_snprintf(ifname, len, "mesh-%s-%d", ifname_ptr, + wpa_s->mesh_if_idx); + if (os_snprintf_error(len, res) || + (os_strlen(ifname) >= IFNAMSIZ && + os_strlen(wpa_s->ifname) < IFNAMSIZ)) { + /* Try to avoid going over the IFNAMSIZ length limit */ + res = os_snprintf(ifname, len, "mesh-%d", wpa_s->mesh_if_idx); + if (os_snprintf_error(len, res)) + return -1; + } + wpa_s->mesh_if_idx++; + return 0; +} + + +int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname, + size_t len) +{ + struct wpa_interface iface; + struct wpa_supplicant *mesh_wpa_s; + u8 addr[ETH_ALEN]; + + if (ifname[0] == '\0' && wpas_mesh_get_ifname(wpa_s, ifname, len) < 0) + return -1; + + if (wpa_drv_if_add(wpa_s, WPA_IF_MESH, ifname, NULL, NULL, NULL, addr, + NULL) < 0) { + wpa_printf(MSG_ERROR, + "mesh: Failed to create new mesh interface"); + return -1; + } + wpa_printf(MSG_INFO, "mesh: Created virtual interface %s addr " + MACSTR, ifname, MAC2STR(addr)); + + os_memset(&iface, 0, sizeof(iface)); + iface.ifname = ifname; + iface.driver = wpa_s->driver->name; + iface.driver_param = wpa_s->conf->driver_param; + iface.ctrl_interface = wpa_s->conf->ctrl_interface; + + mesh_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s); + if (!mesh_wpa_s) { + wpa_printf(MSG_ERROR, + "mesh: Failed to create new wpa_supplicant interface"); + wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0); + return -1; + } + mesh_wpa_s->mesh_if_created = 1; + return 0; +} diff --git a/wpa_supplicant/mesh.h b/wpa_supplicant/mesh.h new file mode 100644 index 0000000000000..3cb7f1b1364f7 --- /dev/null +++ b/wpa_supplicant/mesh.h @@ -0,0 +1,44 @@ +/* + * WPA Supplicant - Basic mesh mode routines + * Copyright (c) 2013-2014, cozybit, Inc. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef MESH_H +#define MESH_H + +int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +int wpa_supplicant_leave_mesh(struct wpa_supplicant *wpa_s); +void wpa_supplicant_mesh_iface_deinit(struct wpa_supplicant *wpa_s, + struct hostapd_iface *ifmsh); +int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf, + char *end); +int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname, + size_t len); + +#ifdef CONFIG_MESH + +void wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s, const u8 *addr, + const u8 *ies, size_t ie_len); +void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s, + struct wpabuf **extra_ie); + +#else /* CONFIG_MESH */ + +static inline void wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s, + const u8 *addr, + const u8 *ies, size_t ie_len) +{ +} + +static inline void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s, + struct wpabuf **extra_ie) +{ +} + +#endif /* CONFIG_MESH */ + +#endif /* MESH_H */ diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c new file mode 100644 index 0000000000000..1d6f2be2b50bb --- /dev/null +++ b/wpa_supplicant/mesh_mpm.c @@ -0,0 +1,1059 @@ +/* + * WPA Supplicant - Basic mesh peer management + * Copyright (c) 2013-2014, cozybit, Inc. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "ap/hostapd.h" +#include "ap/sta_info.h" +#include "ap/ieee802_11.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "mesh_mpm.h" +#include "mesh_rsn.h" + +struct mesh_peer_mgmt_ie { + const u8 *proto_id; + const u8 *llid; + const u8 *plid; + const u8 *reason; + const u8 *pmk; +}; + +static void plink_timer(void *eloop_ctx, void *user_data); + + +enum plink_event { + PLINK_UNDEFINED, + OPN_ACPT, + OPN_RJCT, + OPN_IGNR, + CNF_ACPT, + CNF_RJCT, + CNF_IGNR, + CLS_ACPT, + CLS_IGNR +}; + +static const char * const mplstate[] = { + [PLINK_LISTEN] = "LISTEN", + [PLINK_OPEN_SENT] = "OPEN_SENT", + [PLINK_OPEN_RCVD] = "OPEN_RCVD", + [PLINK_CNF_RCVD] = "CNF_RCVD", + [PLINK_ESTAB] = "ESTAB", + [PLINK_HOLDING] = "HOLDING", + [PLINK_BLOCKED] = "BLOCKED" +}; + +static const char * const mplevent[] = { + [PLINK_UNDEFINED] = "UNDEFINED", + [OPN_ACPT] = "OPN_ACPT", + [OPN_RJCT] = "OPN_RJCT", + [OPN_IGNR] = "OPN_IGNR", + [CNF_ACPT] = "CNF_ACPT", + [CNF_RJCT] = "CNF_RJCT", + [CNF_IGNR] = "CNF_IGNR", + [CLS_ACPT] = "CLS_ACPT", + [CLS_IGNR] = "CLS_IGNR" +}; + + +static int mesh_mpm_parse_peer_mgmt(struct wpa_supplicant *wpa_s, + u8 action_field, + const u8 *ie, size_t len, + struct mesh_peer_mgmt_ie *mpm_ie) +{ + os_memset(mpm_ie, 0, sizeof(*mpm_ie)); + + /* remove optional PMK at end */ + if (len >= 16) { + len -= 16; + mpm_ie->pmk = ie + len - 16; + } + + if ((action_field == PLINK_OPEN && len != 4) || + (action_field == PLINK_CONFIRM && len != 6) || + (action_field == PLINK_CLOSE && len != 6 && len != 8)) { + wpa_msg(wpa_s, MSG_DEBUG, "MPM: Invalid peer mgmt ie"); + return -1; + } + + /* required fields */ + if (len < 4) + return -1; + mpm_ie->proto_id = ie; + mpm_ie->llid = ie + 2; + ie += 4; + len -= 4; + + /* close reason is always present at end for close */ + if (action_field == PLINK_CLOSE) { + if (len < 2) + return -1; + mpm_ie->reason = ie + len - 2; + len -= 2; + } + + /* plid, present for confirm, and possibly close */ + if (len) + mpm_ie->plid = ie; + + return 0; +} + + +static int plink_free_count(struct hostapd_data *hapd) +{ + if (hapd->max_plinks > hapd->num_plinks) + return hapd->max_plinks - hapd->num_plinks; + return 0; +} + + +static u16 copy_supp_rates(struct wpa_supplicant *wpa_s, + struct sta_info *sta, + struct ieee802_11_elems *elems) +{ + if (!elems->supp_rates) { + wpa_msg(wpa_s, MSG_ERROR, "no supported rates from " MACSTR, + MAC2STR(sta->addr)); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (elems->supp_rates_len + elems->ext_supp_rates_len > + sizeof(sta->supported_rates)) { + wpa_msg(wpa_s, MSG_ERROR, + "Invalid supported rates element length " MACSTR + " %d+%d", MAC2STR(sta->addr), elems->supp_rates_len, + elems->ext_supp_rates_len); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + sta->supported_rates_len = merge_byte_arrays( + sta->supported_rates, sizeof(sta->supported_rates), + elems->supp_rates, elems->supp_rates_len, + elems->ext_supp_rates, elems->ext_supp_rates_len); + + return WLAN_STATUS_SUCCESS; +} + + +/* return true if elems from a neighbor match this MBSS */ +static Boolean matches_local(struct wpa_supplicant *wpa_s, + struct ieee802_11_elems *elems) +{ + struct mesh_conf *mconf = wpa_s->ifmsh->mconf; + + if (elems->mesh_config_len < 5) + return FALSE; + + return (mconf->meshid_len == elems->mesh_id_len && + os_memcmp(mconf->meshid, elems->mesh_id, + elems->mesh_id_len) == 0 && + mconf->mesh_pp_id == elems->mesh_config[0] && + mconf->mesh_pm_id == elems->mesh_config[1] && + mconf->mesh_cc_id == elems->mesh_config[2] && + mconf->mesh_sp_id == elems->mesh_config[3] && + mconf->mesh_auth_id == elems->mesh_config[4]); +} + + +/* check if local link id is already used with another peer */ +static Boolean llid_in_use(struct wpa_supplicant *wpa_s, u16 llid) +{ + struct sta_info *sta; + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (sta->my_lid == llid) + return TRUE; + } + + return FALSE; +} + + +/* generate an llid for a link and set to initial state */ +static void mesh_mpm_init_link(struct wpa_supplicant *wpa_s, + struct sta_info *sta) +{ + u16 llid; + + do { + if (os_get_random((u8 *) &llid, sizeof(llid)) < 0) + continue; + } while (!llid || llid_in_use(wpa_s, llid)); + + sta->my_lid = llid; + sta->peer_lid = 0; + + /* + * We do not use wpa_mesh_set_plink_state() here because there is no + * entry in kernel yet. + */ + sta->plink_state = PLINK_LISTEN; +} + + +static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, + struct sta_info *sta, + enum plink_action_field type, + u16 close_reason) +{ + struct wpabuf *buf; + struct hostapd_iface *ifmsh = wpa_s->ifmsh; + struct hostapd_data *bss = ifmsh->bss[0]; + struct mesh_conf *conf = ifmsh->mconf; + u8 supp_rates[2 + 2 + 32]; +#ifdef CONFIG_IEEE80211N + u8 ht_capa_oper[2 + 26 + 2 + 22]; +#endif /* CONFIG_IEEE80211N */ + u8 *pos, *cat; + u8 ie_len, add_plid = 0; + int ret; + int ampe = conf->security & MESH_CONF_SEC_AMPE; + size_t buf_len; + + if (!sta) + return; + + buf_len = 2 + /* capability info */ + 2 + /* AID */ + 2 + 8 + /* supported rates */ + 2 + (32 - 8) + + 2 + 32 + /* mesh ID */ + 2 + 7 + /* mesh config */ + 2 + 23 + /* peering management */ + 2 + 96 + /* AMPE */ + 2 + 16; /* MIC */ +#ifdef CONFIG_IEEE80211N + if (type != PLINK_CLOSE && wpa_s->mesh_ht_enabled) { + buf_len += 2 + 26 + /* HT capabilities */ + 2 + 22; /* HT operation */ + } +#endif /* CONFIG_IEEE80211N */ + buf = wpabuf_alloc(buf_len); + if (!buf) + return; + + cat = wpabuf_mhead_u8(buf); + wpabuf_put_u8(buf, WLAN_ACTION_SELF_PROTECTED); + wpabuf_put_u8(buf, type); + + if (type != PLINK_CLOSE) { + u8 info; + + /* capability info */ + wpabuf_put_le16(buf, ampe ? IEEE80211_CAP_PRIVACY : 0); + + /* aid */ + if (type == PLINK_CONFIRM) + wpabuf_put_le16(buf, sta->peer_lid); + + /* IE: supp + ext. supp rates */ + pos = hostapd_eid_supp_rates(bss, supp_rates); + pos = hostapd_eid_ext_supp_rates(bss, pos); + wpabuf_put_data(buf, supp_rates, pos - supp_rates); + + /* IE: Mesh ID */ + wpabuf_put_u8(buf, WLAN_EID_MESH_ID); + wpabuf_put_u8(buf, conf->meshid_len); + wpabuf_put_data(buf, conf->meshid, conf->meshid_len); + + /* IE: mesh conf */ + wpabuf_put_u8(buf, WLAN_EID_MESH_CONFIG); + wpabuf_put_u8(buf, 7); + wpabuf_put_u8(buf, conf->mesh_pp_id); + wpabuf_put_u8(buf, conf->mesh_pm_id); + wpabuf_put_u8(buf, conf->mesh_cc_id); + wpabuf_put_u8(buf, conf->mesh_sp_id); + wpabuf_put_u8(buf, conf->mesh_auth_id); + info = (bss->num_plinks > 63 ? 63 : bss->num_plinks) << 1; + /* TODO: Add Connected to Mesh Gate/AS subfields */ + wpabuf_put_u8(buf, info); + /* always forwarding & accepting plinks for now */ + wpabuf_put_u8(buf, 0x1 | 0x8); + } else { /* Peer closing frame */ + /* IE: Mesh ID */ + wpabuf_put_u8(buf, WLAN_EID_MESH_ID); + wpabuf_put_u8(buf, conf->meshid_len); + wpabuf_put_data(buf, conf->meshid, conf->meshid_len); + } + + /* IE: Mesh Peering Management element */ + ie_len = 4; + if (ampe) + ie_len += PMKID_LEN; + switch (type) { + case PLINK_OPEN: + break; + case PLINK_CONFIRM: + ie_len += 2; + add_plid = 1; + break; + case PLINK_CLOSE: + ie_len += 2; + add_plid = 1; + ie_len += 2; /* reason code */ + break; + } + + wpabuf_put_u8(buf, WLAN_EID_PEER_MGMT); + wpabuf_put_u8(buf, ie_len); + /* peering protocol */ + if (ampe) + wpabuf_put_le16(buf, 1); + else + wpabuf_put_le16(buf, 0); + wpabuf_put_le16(buf, sta->my_lid); + if (add_plid) + wpabuf_put_le16(buf, sta->peer_lid); + if (type == PLINK_CLOSE) + wpabuf_put_le16(buf, close_reason); + if (ampe) { + if (sta->sae == NULL) { + wpa_msg(wpa_s, MSG_INFO, "Mesh MPM: no SAE session"); + goto fail; + } + mesh_rsn_get_pmkid(wpa_s->mesh_rsn, sta, + wpabuf_put(buf, PMKID_LEN)); + } + +#ifdef CONFIG_IEEE80211N + if (type != PLINK_CLOSE && wpa_s->mesh_ht_enabled) { + pos = hostapd_eid_ht_capabilities(bss, ht_capa_oper); + pos = hostapd_eid_ht_operation(bss, pos); + wpabuf_put_data(buf, ht_capa_oper, pos - ht_capa_oper); + } +#endif /* CONFIG_IEEE80211N */ + + if (ampe && mesh_rsn_protect_frame(wpa_s->mesh_rsn, sta, cat, buf)) { + wpa_msg(wpa_s, MSG_INFO, + "Mesh MPM: failed to add AMPE and MIC IE"); + goto fail; + } + + ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, + sta->addr, wpa_s->own_addr, wpa_s->own_addr, + wpabuf_head(buf), wpabuf_len(buf), 0); + if (ret < 0) + wpa_msg(wpa_s, MSG_INFO, + "Mesh MPM: failed to send peering frame"); + +fail: + wpabuf_free(buf); +} + + +/* configure peering state in ours and driver's station entry */ +void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s, + struct sta_info *sta, + enum mesh_plink_state state) +{ + struct hostapd_sta_add_params params; + int ret; + + sta->plink_state = state; + + os_memset(¶ms, 0, sizeof(params)); + params.addr = sta->addr; + params.plink_state = state; + params.set = 1; + + wpa_msg(wpa_s, MSG_DEBUG, "MPM set " MACSTR " into %s", + MAC2STR(sta->addr), mplstate[state]); + ret = wpa_drv_sta_add(wpa_s, ¶ms); + if (ret) { + wpa_msg(wpa_s, MSG_ERROR, "Driver failed to set " MACSTR + ": %d", MAC2STR(sta->addr), ret); + } +} + + +static void mesh_mpm_fsm_restart(struct wpa_supplicant *wpa_s, + struct sta_info *sta) +{ + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; + + eloop_cancel_timeout(plink_timer, wpa_s, sta); + + ap_free_sta(hapd, sta); +} + + +static void plink_timer(void *eloop_ctx, void *user_data) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct sta_info *sta = user_data; + u16 reason = 0; + struct mesh_conf *conf = wpa_s->ifmsh->mconf; + + switch (sta->plink_state) { + case PLINK_OPEN_RCVD: + case PLINK_OPEN_SENT: + /* retry timer */ + if (sta->mpm_retries < conf->dot11MeshMaxRetries) { + eloop_register_timeout( + conf->dot11MeshRetryTimeout / 1000, + (conf->dot11MeshRetryTimeout % 1000) * 1000, + plink_timer, wpa_s, sta); + mesh_mpm_send_plink_action(wpa_s, sta, PLINK_OPEN, 0); + sta->mpm_retries++; + break; + } + reason = WLAN_REASON_MESH_MAX_RETRIES; + /* fall through on else */ + + case PLINK_CNF_RCVD: + /* confirm timer */ + if (!reason) + reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT; + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); + eloop_register_timeout(conf->dot11MeshHoldingTimeout / 1000, + (conf->dot11MeshHoldingTimeout % 1000) * 1000, + plink_timer, wpa_s, sta); + mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CLOSE, reason); + break; + case PLINK_HOLDING: + /* holding timer */ + mesh_mpm_fsm_restart(wpa_s, sta); + break; + default: + break; + } +} + + +/* initiate peering with station */ +static void +mesh_mpm_plink_open(struct wpa_supplicant *wpa_s, struct sta_info *sta, + enum mesh_plink_state next_state) +{ + struct mesh_conf *conf = wpa_s->ifmsh->mconf; + + eloop_cancel_timeout(plink_timer, wpa_s, sta); + eloop_register_timeout(conf->dot11MeshRetryTimeout / 1000, + (conf->dot11MeshRetryTimeout % 1000) * 1000, + plink_timer, wpa_s, sta); + mesh_mpm_send_plink_action(wpa_s, sta, PLINK_OPEN, 0); + wpa_mesh_set_plink_state(wpa_s, sta, next_state); +} + + +int mesh_mpm_plink_close(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + int reason = WLAN_REASON_MESH_PEERING_CANCELLED; + + if (sta) { + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); + mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CLOSE, reason); + wpa_printf(MSG_DEBUG, "MPM closing plink sta=" MACSTR, + MAC2STR(sta->addr)); + eloop_cancel_timeout(plink_timer, wpa_s, sta); + return 0; + } + + return 1; +} + + +void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh) +{ + struct hostapd_data *hapd = ifmsh->bss[0]; + + /* notify peers we're leaving */ + ap_for_each_sta(hapd, mesh_mpm_plink_close, wpa_s); + + hapd->num_plinks = 0; + hostapd_free_stas(hapd); +} + + +/* for mesh_rsn to indicate this peer has completed authentication, and we're + * ready to start AMPE */ +void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr) +{ + struct hostapd_data *data = wpa_s->ifmsh->bss[0]; + struct hostapd_sta_add_params params; + struct sta_info *sta; + int ret; + + sta = ap_get_sta(data, addr); + if (!sta) { + wpa_msg(wpa_s, MSG_DEBUG, "no such mesh peer"); + return; + } + + /* TODO: Should do nothing if this STA is already authenticated, but + * the AP code already sets this flag. */ + sta->flags |= WLAN_STA_AUTH; + + mesh_rsn_init_ampe_sta(wpa_s, sta); + + os_memset(¶ms, 0, sizeof(params)); + params.addr = sta->addr; + params.flags = WPA_STA_AUTHENTICATED | WPA_STA_AUTHORIZED; + params.set = 1; + + wpa_msg(wpa_s, MSG_DEBUG, "MPM authenticating " MACSTR, + MAC2STR(sta->addr)); + ret = wpa_drv_sta_add(wpa_s, ¶ms); + if (ret) { + wpa_msg(wpa_s, MSG_ERROR, + "Driver failed to set " MACSTR ": %d", + MAC2STR(sta->addr), ret); + } + + if (!sta->my_lid) + mesh_mpm_init_link(wpa_s, sta); + + mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT); +} + +/* + * Initialize a sta_info structure for a peer and upload it into the driver + * in preparation for beginning authentication or peering. This is done when a + * Beacon (secure or open mesh) or a peering open frame (for open mesh) is + * received from the peer for the first time. + */ +static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s, + const u8 *addr, + struct ieee802_11_elems *elems) +{ + struct hostapd_sta_add_params params; + struct mesh_conf *conf = wpa_s->ifmsh->mconf; + struct hostapd_data *data = wpa_s->ifmsh->bss[0]; + struct sta_info *sta; + int ret; + + sta = ap_get_sta(data, addr); + if (!sta) { + sta = ap_sta_add(data, addr); + if (!sta) + return NULL; + } + + /* initialize sta */ + if (copy_supp_rates(wpa_s, sta, elems)) { + ap_free_sta(data, sta); + return NULL; + } + + mesh_mpm_init_link(wpa_s, sta); + +#ifdef CONFIG_IEEE80211N + copy_sta_ht_capab(data, sta, elems->ht_capabilities, + elems->ht_capabilities_len); + update_ht_state(data, sta); +#endif /* CONFIG_IEEE80211N */ + + /* insert into driver */ + os_memset(¶ms, 0, sizeof(params)); + params.supp_rates = sta->supported_rates; + params.supp_rates_len = sta->supported_rates_len; + params.addr = addr; + params.plink_state = sta->plink_state; + params.aid = sta->peer_lid; + params.listen_interval = 100; + params.ht_capabilities = sta->ht_capabilities; + params.flags |= WPA_STA_WMM; + params.flags_mask |= WPA_STA_AUTHENTICATED; + if (conf->security == MESH_CONF_SEC_NONE) { + params.flags |= WPA_STA_AUTHORIZED; + params.flags |= WPA_STA_AUTHENTICATED; + } else { + sta->flags |= WLAN_STA_MFP; + params.flags |= WPA_STA_MFP; + } + + ret = wpa_drv_sta_add(wpa_s, ¶ms); + if (ret) { + wpa_msg(wpa_s, MSG_ERROR, + "Driver failed to insert " MACSTR ": %d", + MAC2STR(addr), ret); + ap_free_sta(data, sta); + return NULL; + } + + return sta; +} + + +void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr, + struct ieee802_11_elems *elems) +{ + struct mesh_conf *conf = wpa_s->ifmsh->mconf; + struct hostapd_data *data = wpa_s->ifmsh->bss[0]; + struct sta_info *sta; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + sta = mesh_mpm_add_peer(wpa_s, addr, elems); + if (!sta) + return; + + if (ssid && ssid->no_auto_peer) { + wpa_msg(wpa_s, MSG_INFO, "will not initiate new peer link with " + MACSTR " because of no_auto_peer", MAC2STR(addr)); + if (data->mesh_pending_auth) { + struct os_reltime age; + const struct ieee80211_mgmt *mgmt; + struct hostapd_frame_info fi; + + mgmt = wpabuf_head(data->mesh_pending_auth); + os_reltime_age(&data->mesh_pending_auth_time, &age); + if (age.sec < 2 && + os_memcmp(mgmt->sa, addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, + "mesh: Process pending Authentication frame from %u.%06u seconds ago", + (unsigned int) age.sec, + (unsigned int) age.usec); + os_memset(&fi, 0, sizeof(fi)); + ieee802_11_mgmt( + data, + wpabuf_head(data->mesh_pending_auth), + wpabuf_len(data->mesh_pending_auth), + &fi); + } + wpabuf_free(data->mesh_pending_auth); + data->mesh_pending_auth = NULL; + } + return; + } + + if (conf->security == MESH_CONF_SEC_NONE) + mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT); + else + mesh_rsn_auth_sae_sta(wpa_s, sta); +} + + +void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s, struct rx_mgmt *rx_mgmt) +{ + struct hostapd_frame_info fi; + + os_memset(&fi, 0, sizeof(fi)); + fi.datarate = rx_mgmt->datarate; + fi.ssi_signal = rx_mgmt->ssi_signal; + ieee802_11_mgmt(wpa_s->ifmsh->bss[0], rx_mgmt->frame, + rx_mgmt->frame_len, &fi); +} + + +static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s, + struct sta_info *sta) +{ + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; + struct mesh_conf *conf = wpa_s->ifmsh->mconf; + u8 seq[6] = {}; + + wpa_msg(wpa_s, MSG_INFO, "mesh plink with " MACSTR " established", + MAC2STR(sta->addr)); + + if (conf->security & MESH_CONF_SEC_AMPE) { + wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 0, 0, + seq, sizeof(seq), sta->mtk, sizeof(sta->mtk)); + wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 1, 0, + seq, sizeof(seq), + sta->mgtk, sizeof(sta->mgtk)); + wpa_drv_set_key(wpa_s, WPA_ALG_IGTK, sta->addr, 4, 0, + seq, sizeof(seq), + sta->mgtk, sizeof(sta->mgtk)); + + wpa_hexdump_key(MSG_DEBUG, "mtk:", sta->mtk, sizeof(sta->mtk)); + wpa_hexdump_key(MSG_DEBUG, "mgtk:", + sta->mgtk, sizeof(sta->mgtk)); + } + + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_ESTAB); + hapd->num_plinks++; + + sta->flags |= WLAN_STA_ASSOC; + + eloop_cancel_timeout(plink_timer, wpa_s, sta); + + /* Send ctrl event */ + wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_PEER_CONNECTED MACSTR, + MAC2STR(sta->addr)); +} + + +static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta, + enum plink_event event) +{ + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; + struct mesh_conf *conf = wpa_s->ifmsh->mconf; + u16 reason = 0; + + wpa_msg(wpa_s, MSG_DEBUG, "MPM " MACSTR " state %s event %s", + MAC2STR(sta->addr), mplstate[sta->plink_state], + mplevent[event]); + + switch (sta->plink_state) { + case PLINK_LISTEN: + switch (event) { + case CLS_ACPT: + mesh_mpm_fsm_restart(wpa_s, sta); + break; + case OPN_ACPT: + mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_RCVD); + mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CONFIRM, + 0); + break; + default: + break; + } + break; + case PLINK_OPEN_SENT: + switch (event) { + case OPN_RJCT: + case CNF_RJCT: + reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; + /* fall-through */ + case CLS_ACPT: + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); + if (!reason) + reason = WLAN_REASON_MESH_CLOSE_RCVD; + eloop_register_timeout( + conf->dot11MeshHoldingTimeout / 1000, + (conf->dot11MeshHoldingTimeout % 1000) * 1000, + plink_timer, wpa_s, sta); + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CLOSE, reason); + break; + case OPN_ACPT: + /* retry timer is left untouched */ + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_OPEN_RCVD); + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CONFIRM, 0); + break; + case CNF_ACPT: + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_CNF_RCVD); + eloop_register_timeout( + conf->dot11MeshConfirmTimeout / 1000, + (conf->dot11MeshConfirmTimeout % 1000) * 1000, + plink_timer, wpa_s, sta); + break; + default: + break; + } + break; + case PLINK_OPEN_RCVD: + switch (event) { + case OPN_RJCT: + case CNF_RJCT: + reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; + /* fall-through */ + case CLS_ACPT: + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); + if (!reason) + reason = WLAN_REASON_MESH_CLOSE_RCVD; + eloop_register_timeout( + conf->dot11MeshHoldingTimeout / 1000, + (conf->dot11MeshHoldingTimeout % 1000) * 1000, + plink_timer, wpa_s, sta); + sta->mpm_close_reason = reason; + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CLOSE, reason); + break; + case OPN_ACPT: + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CONFIRM, 0); + break; + case CNF_ACPT: + if (conf->security & MESH_CONF_SEC_AMPE) + mesh_rsn_derive_mtk(wpa_s, sta); + mesh_mpm_plink_estab(wpa_s, sta); + break; + default: + break; + } + break; + case PLINK_CNF_RCVD: + switch (event) { + case OPN_RJCT: + case CNF_RJCT: + reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION; + /* fall-through */ + case CLS_ACPT: + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); + if (!reason) + reason = WLAN_REASON_MESH_CLOSE_RCVD; + eloop_register_timeout( + conf->dot11MeshHoldingTimeout / 1000, + (conf->dot11MeshHoldingTimeout % 1000) * 1000, + plink_timer, wpa_s, sta); + sta->mpm_close_reason = reason; + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CLOSE, reason); + break; + case OPN_ACPT: + mesh_mpm_plink_estab(wpa_s, sta); + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CONFIRM, 0); + break; + default: + break; + } + break; + case PLINK_ESTAB: + switch (event) { + case CLS_ACPT: + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING); + reason = WLAN_REASON_MESH_CLOSE_RCVD; + + eloop_register_timeout( + conf->dot11MeshHoldingTimeout / 1000, + (conf->dot11MeshHoldingTimeout % 1000) * 1000, + plink_timer, wpa_s, sta); + sta->mpm_close_reason = reason; + + wpa_msg(wpa_s, MSG_INFO, "mesh plink with " MACSTR + " closed with reason %d", + MAC2STR(sta->addr), reason); + + wpa_msg_ctrl(wpa_s, MSG_INFO, + MESH_PEER_DISCONNECTED MACSTR, + MAC2STR(sta->addr)); + + hapd->num_plinks--; + + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CLOSE, reason); + break; + case OPN_ACPT: + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CONFIRM, 0); + break; + default: + break; + } + break; + case PLINK_HOLDING: + switch (event) { + case CLS_ACPT: + mesh_mpm_fsm_restart(wpa_s, sta); + break; + case OPN_ACPT: + case CNF_ACPT: + case OPN_RJCT: + case CNF_RJCT: + reason = sta->mpm_close_reason; + mesh_mpm_send_plink_action(wpa_s, sta, + PLINK_CLOSE, reason); + break; + default: + break; + } + break; + default: + wpa_msg(wpa_s, MSG_DEBUG, + "Unsupported MPM event %s for state %s", + mplevent[event], mplstate[sta->plink_state]); + break; + } +} + + +void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + u8 action_field; + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; + struct mesh_conf *mconf = wpa_s->ifmsh->mconf; + struct sta_info *sta; + u16 plid = 0, llid = 0; + enum plink_event event; + struct ieee802_11_elems elems; + struct mesh_peer_mgmt_ie peer_mgmt_ie; + const u8 *ies; + size_t ie_len; + int ret; + + if (mgmt->u.action.category != WLAN_ACTION_SELF_PROTECTED) + return; + + action_field = mgmt->u.action.u.slf_prot_action.action; + if (action_field != PLINK_OPEN && + action_field != PLINK_CONFIRM && + action_field != PLINK_CLOSE) + return; + + ies = mgmt->u.action.u.slf_prot_action.variable; + ie_len = (const u8 *) mgmt + len - + mgmt->u.action.u.slf_prot_action.variable; + + /* at least expect mesh id and peering mgmt */ + if (ie_len < 2 + 2) { + wpa_printf(MSG_DEBUG, + "MPM: Ignore too short action frame %u ie_len %u", + action_field, (unsigned int) ie_len); + return; + } + wpa_printf(MSG_DEBUG, "MPM: Received PLINK action %u", action_field); + + if (action_field == PLINK_OPEN || action_field == PLINK_CONFIRM) { + wpa_printf(MSG_DEBUG, "MPM: Capability 0x%x", + WPA_GET_LE16(ies)); + ies += 2; /* capability */ + ie_len -= 2; + } + if (action_field == PLINK_CONFIRM) { + wpa_printf(MSG_DEBUG, "MPM: AID 0x%x", WPA_GET_LE16(ies)); + ies += 2; /* aid */ + ie_len -= 2; + } + + /* check for mesh peering, mesh id and mesh config IEs */ + if (ieee802_11_parse_elems(ies, ie_len, &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, "MPM: Failed to parse PLINK IEs"); + return; + } + if (!elems.peer_mgmt) { + wpa_printf(MSG_DEBUG, + "MPM: No Mesh Peering Management element"); + return; + } + if (action_field != PLINK_CLOSE) { + if (!elems.mesh_id || !elems.mesh_config) { + wpa_printf(MSG_DEBUG, + "MPM: No Mesh ID or Mesh Configuration element"); + return; + } + + if (!matches_local(wpa_s, &elems)) { + wpa_printf(MSG_DEBUG, + "MPM: Mesh ID or Mesh Configuration element do not match local MBSS"); + return; + } + } + + ret = mesh_mpm_parse_peer_mgmt(wpa_s, action_field, + elems.peer_mgmt, + elems.peer_mgmt_len, + &peer_mgmt_ie); + if (ret) { + wpa_printf(MSG_DEBUG, "MPM: Mesh parsing rejected frame"); + return; + } + + /* the sender's llid is our plid and vice-versa */ + plid = WPA_GET_LE16(peer_mgmt_ie.llid); + if (peer_mgmt_ie.plid) + llid = WPA_GET_LE16(peer_mgmt_ie.plid); + wpa_printf(MSG_DEBUG, "MPM: plid=0x%x llid=0x%x", plid, llid); + + sta = ap_get_sta(hapd, mgmt->sa); + + /* + * If this is an open frame from an unknown STA, and this is an + * open mesh, then go ahead and add the peer before proceeding. + */ + if (!sta && action_field == PLINK_OPEN && + !(mconf->security & MESH_CONF_SEC_AMPE)) + sta = mesh_mpm_add_peer(wpa_s, mgmt->sa, &elems); + + if (!sta) { + wpa_printf(MSG_DEBUG, "MPM: No STA entry for peer"); + return; + } + +#ifdef CONFIG_SAE + /* peer is in sae_accepted? */ + if (sta->sae && sta->sae->state != SAE_ACCEPTED) { + wpa_printf(MSG_DEBUG, "MPM: SAE not yet accepted for peer"); + return; + } +#endif /* CONFIG_SAE */ + + if (!sta->my_lid) + mesh_mpm_init_link(wpa_s, sta); + + if ((mconf->security & MESH_CONF_SEC_AMPE) && + mesh_rsn_process_ampe(wpa_s, sta, &elems, + &mgmt->u.action.category, + ies, ie_len)) { + wpa_printf(MSG_DEBUG, "MPM: RSN process rejected frame"); + return; + } + + if (sta->plink_state == PLINK_BLOCKED) { + wpa_printf(MSG_DEBUG, "MPM: PLINK_BLOCKED"); + return; + } + + /* Now we will figure out the appropriate event... */ + switch (action_field) { + case PLINK_OPEN: + if (plink_free_count(hapd) == 0) { + event = OPN_IGNR; + wpa_printf(MSG_INFO, + "MPM: Peer link num over quota(%d)", + hapd->max_plinks); + } else if (sta->peer_lid && sta->peer_lid != plid) { + event = OPN_IGNR; + } else { + sta->peer_lid = plid; + event = OPN_ACPT; + } + break; + case PLINK_CONFIRM: + if (plink_free_count(hapd) == 0) { + event = CNF_IGNR; + wpa_printf(MSG_INFO, + "MPM: Peer link num over quota(%d)", + hapd->max_plinks); + } else if (sta->my_lid != llid || + (sta->peer_lid && sta->peer_lid != plid)) { + event = CNF_IGNR; + } else { + if (!sta->peer_lid) + sta->peer_lid = plid; + event = CNF_ACPT; + } + break; + case PLINK_CLOSE: + if (sta->plink_state == PLINK_ESTAB) + /* Do not check for llid or plid. This does not + * follow the standard but since multiple plinks + * per cand are not supported, it is necessary in + * order to avoid a livelock when MP A sees an + * establish peer link to MP B but MP B does not + * see it. This can be caused by a timeout in + * B's peer link establishment or B being + * restarted. + */ + event = CLS_ACPT; + else if (sta->peer_lid != plid) + event = CLS_IGNR; + else if (peer_mgmt_ie.plid && sta->my_lid != llid) + event = CLS_IGNR; + else + event = CLS_ACPT; + break; + default: + /* + * This cannot be hit due to the action_field check above, but + * compilers may not be able to figure that out and can warn + * about uninitialized event below. + */ + return; + } + mesh_mpm_fsm(wpa_s, sta, event); +} + + +/* called by ap_free_sta */ +void mesh_mpm_free_sta(struct sta_info *sta) +{ + eloop_cancel_timeout(plink_timer, ELOOP_ALL_CTX, sta); + eloop_cancel_timeout(mesh_auth_timer, ELOOP_ALL_CTX, sta); +} diff --git a/wpa_supplicant/mesh_mpm.h b/wpa_supplicant/mesh_mpm.h new file mode 100644 index 0000000000000..7ebaef0cd087d --- /dev/null +++ b/wpa_supplicant/mesh_mpm.h @@ -0,0 +1,43 @@ +/* + * WPA Supplicant - Basic mesh peer management + * Copyright (c) 2013-2014, cozybit, Inc. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef MESH_MPM_H +#define MESH_MPM_H + +/* notify MPM of new mesh peer to be inserted in MPM and driver */ +void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr, + struct ieee802_11_elems *elems); +void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh); +void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr); +void mesh_mpm_free_sta(struct sta_info *sta); +void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s, + struct sta_info *sta, + enum mesh_plink_state state); + +#ifdef CONFIG_MESH + +void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, size_t len); +void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s, struct rx_mgmt *rx_mgmt); + +#else /* CONFIG_MESH */ + +static inline void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, + const struct ieee80211_mgmt *mgmt, + size_t len) +{ +} + +static inline void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s, + struct rx_mgmt *rx_mgmt) +{ +} + +#endif /* CONFIG_MESH */ + +#endif /* MESH_MPM_H */ diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c new file mode 100644 index 0000000000000..936002d954ec1 --- /dev/null +++ b/wpa_supplicant/mesh_rsn.c @@ -0,0 +1,574 @@ +/* + * WPA Supplicant - Mesh RSN routines + * Copyright (c) 2013-2014, cozybit, Inc. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "crypto/sha256.h" +#include "crypto/random.h" +#include "crypto/aes.h" +#include "crypto/aes_siv.h" +#include "rsn_supp/wpa.h" +#include "ap/hostapd.h" +#include "ap/wpa_auth.h" +#include "ap/sta_info.h" +#include "ap/ieee802_11.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "wpas_glue.h" +#include "mesh_mpm.h" +#include "mesh_rsn.h" + +#define MESH_AUTH_TIMEOUT 10 +#define MESH_AUTH_RETRY 3 +#define MESH_AUTH_BLOCK_DURATION 3600 + +void mesh_auth_timer(void *eloop_ctx, void *user_data) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct sta_info *sta = user_data; + + if (sta->sae->state != SAE_ACCEPTED) { + wpa_printf(MSG_DEBUG, "AUTH: Re-authenticate with " MACSTR + " (attempt %d) ", + MAC2STR(sta->addr), sta->sae_auth_retry); + wpa_msg(wpa_s, MSG_INFO, MESH_SAE_AUTH_FAILURE "addr=" MACSTR, + MAC2STR(sta->addr)); + if (sta->sae_auth_retry < MESH_AUTH_RETRY) { + mesh_rsn_auth_sae_sta(wpa_s, sta); + } else { + if (sta->sae_auth_retry > MESH_AUTH_RETRY) { + ap_free_sta(wpa_s->ifmsh->bss[0], sta); + return; + } + + /* block the STA if exceeded the number of attempts */ + wpa_mesh_set_plink_state(wpa_s, sta, PLINK_BLOCKED); + sta->sae->state = SAE_NOTHING; + if (wpa_s->mesh_auth_block_duration < + MESH_AUTH_BLOCK_DURATION) + wpa_s->mesh_auth_block_duration += 60; + eloop_register_timeout(wpa_s->mesh_auth_block_duration, + 0, mesh_auth_timer, wpa_s, sta); + wpa_msg(wpa_s, MSG_INFO, MESH_SAE_AUTH_BLOCKED "addr=" + MACSTR " duration=%d", + MAC2STR(sta->addr), + wpa_s->mesh_auth_block_duration); + } + sta->sae_auth_retry++; + } +} + + +static void auth_logger(void *ctx, const u8 *addr, logger_level level, + const char *txt) +{ + if (addr) + wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s", + MAC2STR(addr), txt); + else + wpa_printf(MSG_DEBUG, "AUTH: %s", txt); +} + + +static const u8 *auth_get_psk(void *ctx, const u8 *addr, + const u8 *p2p_dev_addr, const u8 *prev_psk) +{ + struct mesh_rsn *mesh_rsn = ctx; + struct hostapd_data *hapd = mesh_rsn->wpa_s->ifmsh->bss[0]; + struct sta_info *sta = ap_get_sta(hapd, addr); + + wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)", + __func__, MAC2STR(addr), prev_psk); + + if (sta && sta->auth_alg == WLAN_AUTH_SAE) { + if (!sta->sae || prev_psk) + return NULL; + return sta->sae->pmk; + } + + return NULL; +} + + +static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, + const u8 *addr, int idx, u8 *key, size_t key_len) +{ + struct mesh_rsn *mesh_rsn = ctx; + u8 seq[6]; + + os_memset(seq, 0, sizeof(seq)); + + if (addr) { + wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d addr=" MACSTR + " key_idx=%d)", + __func__, alg, MAC2STR(addr), idx); + } else { + wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d key_idx=%d)", + __func__, alg, idx); + } + wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len); + + return wpa_drv_set_key(mesh_rsn->wpa_s, alg, addr, idx, + 1, seq, 6, key, key_len); +} + + +static int auth_start_ampe(void *ctx, const u8 *addr) +{ + struct mesh_rsn *mesh_rsn = ctx; + struct hostapd_data *hapd; + struct sta_info *sta; + + if (mesh_rsn->wpa_s->current_ssid->mode != WPAS_MODE_MESH) + return -1; + + hapd = mesh_rsn->wpa_s->ifmsh->bss[0]; + sta = ap_get_sta(hapd, addr); + if (sta) + eloop_cancel_timeout(mesh_auth_timer, mesh_rsn->wpa_s, sta); + + mesh_mpm_auth_peer(mesh_rsn->wpa_s, addr); + return 0; +} + + +static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr) +{ + struct wpa_auth_config conf; + struct wpa_auth_callbacks cb; + u8 seq[6] = {}; + + wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine"); + + os_memset(&conf, 0, sizeof(conf)); + conf.wpa = 2; + conf.wpa_key_mgmt = WPA_KEY_MGMT_SAE; + conf.wpa_pairwise = WPA_CIPHER_CCMP; + conf.rsn_pairwise = WPA_CIPHER_CCMP; + conf.wpa_group = WPA_CIPHER_CCMP; + conf.eapol_version = 0; + conf.wpa_group_rekey = -1; + + os_memset(&cb, 0, sizeof(cb)); + cb.ctx = rsn; + cb.logger = auth_logger; + cb.get_psk = auth_get_psk; + cb.set_key = auth_set_key; + cb.start_ampe = auth_start_ampe; + + rsn->auth = wpa_init(addr, &conf, &cb); + if (rsn->auth == NULL) { + wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed"); + return -1; + } + + /* TODO: support rekeying */ + if (random_get_bytes(rsn->mgtk, 16) < 0) { + wpa_deinit(rsn->auth); + return -1; + } + + /* group mgmt */ + wpa_drv_set_key(rsn->wpa_s, WPA_ALG_IGTK, NULL, 4, 1, + seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk)); + + /* group privacy / data frames */ + wpa_drv_set_key(rsn->wpa_s, WPA_ALG_CCMP, NULL, 1, 1, + seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk)); + + return 0; +} + + +static void mesh_rsn_deinit(struct mesh_rsn *rsn) +{ + os_memset(rsn->mgtk, 0, sizeof(rsn->mgtk)); + wpa_deinit(rsn->auth); +} + + +struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s, + struct mesh_conf *conf) +{ + struct mesh_rsn *mesh_rsn; + struct hostapd_data *bss = wpa_s->ifmsh->bss[0]; + const u8 *ie; + size_t ie_len; + + mesh_rsn = os_zalloc(sizeof(*mesh_rsn)); + if (mesh_rsn == NULL) + return NULL; + mesh_rsn->wpa_s = wpa_s; + + if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr) < 0) { + mesh_rsn_deinit(mesh_rsn); + return NULL; + } + + bss->wpa_auth = mesh_rsn->auth; + + ie = wpa_auth_get_wpa_ie(mesh_rsn->auth, &ie_len); + conf->ies = (u8 *) ie; + conf->ie_len = ie_len; + + wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); + + return mesh_rsn; +} + + +static int index_within_array(const int *array, int idx) +{ + int i; + + for (i = 0; i < idx; i++) { + if (array[i] == -1) + return 0; + } + + return 1; +} + + +static int mesh_rsn_sae_group(struct wpa_supplicant *wpa_s, + struct sae_data *sae) +{ + int *groups = wpa_s->ifmsh->bss[0]->conf->sae_groups; + + /* Configuration may have changed, so validate current index */ + if (!index_within_array(groups, wpa_s->mesh_rsn->sae_group_index)) + return -1; + + for (;;) { + int group = groups[wpa_s->mesh_rsn->sae_group_index]; + + if (group <= 0) + break; + if (sae_set_group(sae, group) == 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d", + sae->group); + return 0; + } + wpa_s->mesh_rsn->sae_group_index++; + } + + return -1; +} + + +static int mesh_rsn_build_sae_commit(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + struct sta_info *sta) +{ + if (ssid->passphrase == NULL) { + wpa_msg(wpa_s, MSG_DEBUG, "SAE: No password available"); + return -1; + } + + if (mesh_rsn_sae_group(wpa_s, sta->sae) < 0) { + wpa_msg(wpa_s, MSG_DEBUG, "SAE: Failed to select group"); + return -1; + } + + return sae_prepare_commit(wpa_s->own_addr, sta->addr, + (u8 *) ssid->passphrase, + os_strlen(ssid->passphrase), sta->sae); +} + + +/* initiate new SAE authentication with sta */ +int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, + struct sta_info *sta) +{ + struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; + struct wpa_ssid *ssid = wpa_s->current_ssid; + unsigned int rnd; + int ret; + + if (!ssid) { + wpa_msg(wpa_s, MSG_DEBUG, + "AUTH: No current_ssid known to initiate new SAE"); + return -1; + } + + if (!sta->sae) { + sta->sae = os_zalloc(sizeof(*sta->sae)); + if (sta->sae == NULL) + return -1; + } + + if (mesh_rsn_build_sae_commit(wpa_s, ssid, sta)) + return -1; + + wpa_msg(wpa_s, MSG_DEBUG, + "AUTH: started authentication with SAE peer: " MACSTR, + MAC2STR(sta->addr)); + + wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING); + ret = auth_sae_init_committed(hapd, sta); + if (ret) + return ret; + + eloop_cancel_timeout(mesh_auth_timer, wpa_s, sta); + rnd = rand() % MESH_AUTH_TIMEOUT; + eloop_register_timeout(MESH_AUTH_TIMEOUT + rnd, 0, mesh_auth_timer, + wpa_s, sta); + return 0; +} + + +void mesh_rsn_get_pmkid(struct mesh_rsn *rsn, struct sta_info *sta, u8 *pmkid) +{ + /* don't expect wpa auth to cache the pmkid for now */ + rsn_pmkid(sta->sae->pmk, PMK_LEN, rsn->wpa_s->own_addr, + sta->addr, pmkid, + wpa_key_mgmt_sha256(wpa_auth_sta_key_mgmt(sta->wpa_sm))); +} + + +static void +mesh_rsn_derive_aek(struct mesh_rsn *rsn, struct sta_info *sta) +{ + u8 *myaddr = rsn->wpa_s->own_addr; + u8 *peer = sta->addr; + u8 *addr1 = peer, *addr2 = myaddr; + u8 context[AES_BLOCK_SIZE]; + + /* SAE */ + RSN_SELECTOR_PUT(context, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP)); + + if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) { + addr1 = myaddr; + addr2 = peer; + } + os_memcpy(context + 4, addr1, ETH_ALEN); + os_memcpy(context + 10, addr2, ETH_ALEN); + + sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk), "AEK Derivation", + context, sizeof(context), sta->aek, sizeof(sta->aek)); +} + + +/* derive mesh temporal key from pmk */ +int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta) +{ + u8 *ptr; + u8 *min, *max; + u16 min_lid, max_lid; + size_t nonce_len = sizeof(sta->my_nonce); + size_t lid_len = sizeof(sta->my_lid); + u8 *myaddr = wpa_s->own_addr; + u8 *peer = sta->addr; + /* 2 nonces, 2 linkids, akm suite, 2 mac addrs */ + u8 context[64 + 4 + 4 + 12]; + + ptr = context; + if (os_memcmp(sta->my_nonce, sta->peer_nonce, nonce_len) < 0) { + min = sta->my_nonce; + max = sta->peer_nonce; + } else { + min = sta->peer_nonce; + max = sta->my_nonce; + } + os_memcpy(ptr, min, nonce_len); + os_memcpy(ptr + nonce_len, max, nonce_len); + ptr += 2 * nonce_len; + + if (sta->my_lid < sta->peer_lid) { + min_lid = host_to_le16(sta->my_lid); + max_lid = host_to_le16(sta->peer_lid); + } else { + min_lid = host_to_le16(sta->peer_lid); + max_lid = host_to_le16(sta->my_lid); + } + os_memcpy(ptr, &min_lid, lid_len); + os_memcpy(ptr + lid_len, &max_lid, lid_len); + ptr += 2 * lid_len; + + /* SAE */ + RSN_SELECTOR_PUT(ptr, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP)); + ptr += 4; + + if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) { + min = myaddr; + max = peer; + } else { + min = peer; + max = myaddr; + } + os_memcpy(ptr, min, ETH_ALEN); + os_memcpy(ptr + ETH_ALEN, max, ETH_ALEN); + + sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk), + "Temporal Key Derivation", context, sizeof(context), + sta->mtk, sizeof(sta->mtk)); + return 0; +} + + +void mesh_rsn_init_ampe_sta(struct wpa_supplicant *wpa_s, struct sta_info *sta) +{ + if (random_get_bytes(sta->my_nonce, 32) < 0) { + wpa_printf(MSG_INFO, "mesh: Failed to derive random nonce"); + /* TODO: How to handle this more cleanly? */ + } + os_memset(sta->peer_nonce, 0, 32); + mesh_rsn_derive_aek(wpa_s->mesh_rsn, sta); +} + + +/* insert AMPE and encrypted MIC at @ie. + * @mesh_rsn: mesh RSN context + * @sta: STA we're sending to + * @cat: pointer to category code in frame header. + * @buf: wpabuf to add encrypted AMPE and MIC to. + * */ +int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta, + const u8 *cat, struct wpabuf *buf) +{ + struct ieee80211_ampe_ie *ampe; + u8 const *ie = wpabuf_head_u8(buf) + wpabuf_len(buf); + u8 *ampe_ie = NULL, *mic_ie = NULL, *mic_payload; + const u8 *aad[] = { rsn->wpa_s->own_addr, sta->addr, cat }; + const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, ie - cat }; + int ret = 0; + + if (AES_BLOCK_SIZE + 2 + sizeof(*ampe) + 2 > wpabuf_tailroom(buf)) { + wpa_printf(MSG_ERROR, "protect frame: buffer too small"); + return -EINVAL; + } + + ampe_ie = os_zalloc(2 + sizeof(*ampe)); + if (!ampe_ie) { + wpa_printf(MSG_ERROR, "protect frame: out of memory"); + return -ENOMEM; + } + + mic_ie = os_zalloc(2 + AES_BLOCK_SIZE); + if (!mic_ie) { + wpa_printf(MSG_ERROR, "protect frame: out of memory"); + ret = -ENOMEM; + goto free; + } + + /* IE: AMPE */ + ampe_ie[0] = WLAN_EID_AMPE; + ampe_ie[1] = sizeof(*ampe); + ampe = (struct ieee80211_ampe_ie *) (ampe_ie + 2); + + RSN_SELECTOR_PUT(ampe->selected_pairwise_suite, + wpa_cipher_to_suite(WPA_PROTO_RSN, WPA_CIPHER_CCMP)); + os_memcpy(ampe->local_nonce, sta->my_nonce, 32); + os_memcpy(ampe->peer_nonce, sta->peer_nonce, 32); + /* incomplete: see 13.5.4 */ + /* TODO: static mgtk for now since we don't support rekeying! */ + os_memcpy(ampe->mgtk, rsn->mgtk, 16); + /* TODO: Populate Key RSC */ + /* expire in 13 decades or so */ + os_memset(ampe->key_expiration, 0xff, 4); + + /* IE: MIC */ + mic_ie[0] = WLAN_EID_MIC; + mic_ie[1] = AES_BLOCK_SIZE; + wpabuf_put_data(buf, mic_ie, 2); + /* MIC field is output ciphertext */ + + /* encrypt after MIC */ + mic_payload = (u8 *) wpabuf_put(buf, 2 + sizeof(*ampe) + + AES_BLOCK_SIZE); + + if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + sizeof(*ampe), 3, + aad, aad_len, mic_payload)) { + wpa_printf(MSG_ERROR, "protect frame: failed to encrypt"); + ret = -ENOMEM; + goto free; + } + +free: + os_free(ampe_ie); + os_free(mic_ie); + + return ret; +} + + +int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, + struct ieee802_11_elems *elems, const u8 *cat, + const u8 *start, size_t elems_len) +{ + int ret = 0; + struct ieee80211_ampe_ie *ampe; + u8 null_nonce[32] = {}; + u8 ampe_eid; + u8 ampe_ie_len; + u8 *ampe_buf, *crypt = NULL; + size_t crypt_len; + const u8 *aad[] = { sta->addr, wpa_s->own_addr, cat }; + const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, + (elems->mic - 2) - cat }; + + if (!elems->mic || elems->mic_len < AES_BLOCK_SIZE) { + wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing mic ie"); + return -1; + } + + ampe_buf = (u8 *) elems->mic + elems->mic_len; + if ((int) elems_len < ampe_buf - start) + return -1; + + crypt_len = elems_len - (elems->mic - start); + if (crypt_len < 2) { + wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing ampe ie"); + return -1; + } + + /* crypt is modified by siv_decrypt */ + crypt = os_zalloc(crypt_len); + if (!crypt) { + wpa_printf(MSG_ERROR, "Mesh RSN: out of memory"); + ret = -ENOMEM; + goto free; + } + + os_memcpy(crypt, elems->mic, crypt_len); + + if (aes_siv_decrypt(sta->aek, crypt, crypt_len, 3, + aad, aad_len, ampe_buf)) { + wpa_printf(MSG_ERROR, "Mesh RSN: frame verification failed!"); + ret = -1; + goto free; + } + + ampe_eid = *ampe_buf++; + ampe_ie_len = *ampe_buf++; + + if (ampe_eid != WLAN_EID_AMPE || + ampe_ie_len < sizeof(struct ieee80211_ampe_ie)) { + wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid ampe ie"); + ret = -1; + goto free; + } + + ampe = (struct ieee80211_ampe_ie *) ampe_buf; + if (os_memcmp(ampe->peer_nonce, null_nonce, 32) != 0 && + os_memcmp(ampe->peer_nonce, sta->my_nonce, 32) != 0) { + wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid peer nonce"); + ret = -1; + goto free; + } + os_memcpy(sta->peer_nonce, ampe->local_nonce, + sizeof(ampe->local_nonce)); + os_memcpy(sta->mgtk, ampe->mgtk, sizeof(ampe->mgtk)); + + /* todo parse mgtk expiration */ +free: + os_free(crypt); + return ret; +} diff --git a/wpa_supplicant/mesh_rsn.h b/wpa_supplicant/mesh_rsn.h new file mode 100644 index 0000000000000..b1471b2de8ae9 --- /dev/null +++ b/wpa_supplicant/mesh_rsn.h @@ -0,0 +1,36 @@ +/* + * WPA Supplicant - Mesh RSN routines + * Copyright (c) 2013-2014, cozybit, Inc. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef MESH_RSN_H +#define MESH_RSN_H + +struct mesh_rsn { + struct wpa_supplicant *wpa_s; + struct wpa_authenticator *auth; + u8 mgtk[16]; +#ifdef CONFIG_SAE + struct wpabuf *sae_token; + int sae_group_index; +#endif /* CONFIG_SAE */ +}; + +struct mesh_rsn * mesh_rsn_auth_init(struct wpa_supplicant *wpa_s, + struct mesh_conf *conf); +int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, struct sta_info *sta); +int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta); +void mesh_rsn_get_pmkid(struct mesh_rsn *rsn, struct sta_info *sta, u8 *pmkid); +void mesh_rsn_init_ampe_sta(struct wpa_supplicant *wpa_s, + struct sta_info *sta); +int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta, + const u8 *cat, struct wpabuf *buf); +int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, + struct ieee802_11_elems *elems, const u8 *cat, + const u8 *start, size_t elems_len); +void mesh_auth_timer(void *eloop_ctx, void *user_data); + +#endif /* MESH_RSN_H */ diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c index 9251f62e27eae..ea7dbdb15bf24 100644 --- a/wpa_supplicant/notify.c +++ b/wpa_supplicant/notify.c @@ -48,6 +48,9 @@ void wpas_notify_supplicant_deinitialized(struct wpa_global *global) int wpas_notify_iface_added(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return 0; + if (wpas_dbus_register_iface(wpa_s)) return -1; @@ -60,6 +63,9 @@ int wpas_notify_iface_added(struct wpa_supplicant *wpa_s) void wpas_notify_iface_removed(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return; + /* unregister interface in old DBus ctrl iface */ wpas_dbus_unregister_iface(wpa_s); @@ -72,6 +78,9 @@ void wpas_notify_state_changed(struct wpa_supplicant *wpa_s, enum wpa_states new_state, enum wpa_states old_state) { + if (wpa_s->p2p_mgmt) + return; + /* notify the old DBus API */ wpa_supplicant_dbus_notify_state_change(wpa_s, new_state, old_state); @@ -79,50 +88,67 @@ 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, + "id=%d state=%d BSSID=" MACSTR " SSID=%s", wpa_s->current_ssid ? wpa_s->current_ssid->id : -1, - new_state, MAC2STR(wpa_s->pending_bssid)); + new_state, + MAC2STR(wpa_s->bssid), + wpa_s->current_ssid && wpa_s->current_ssid->ssid ? + wpa_ssid_txt(wpa_s->current_ssid->ssid, + wpa_s->current_ssid->ssid_len) : ""); #endif /* ANDROID */ } void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_DISCONNECT_REASON); } void wpas_notify_network_changed(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_NETWORK); } void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_AP_SCAN); } void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_BSS); } void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_AUTH_MODE); } @@ -130,6 +156,9 @@ 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) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_network_enabled_changed(wpa_s, ssid); } @@ -137,6 +166,9 @@ void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s, void wpas_notify_network_selected(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_network_selected(wpa_s, ssid->id); } @@ -146,12 +178,18 @@ void wpas_notify_network_request(struct wpa_supplicant *wpa_s, enum wpa_ctrl_req_type rtype, const char *default_txt) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_network_request(wpa_s, ssid, rtype, default_txt); } void wpas_notify_scanning(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return; + /* notify the old DBus API */ wpa_supplicant_dbus_notify_scanning(wpa_s); @@ -162,12 +200,18 @@ void wpas_notify_scanning(struct wpa_supplicant *wpa_s) void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_scan_done(wpa_s, success); } void wpas_notify_scan_results(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return; + /* notify the old DBus API */ wpa_supplicant_dbus_notify_scan_results(wpa_s); @@ -178,6 +222,9 @@ void wpas_notify_scan_results(struct wpa_supplicant *wpa_s) void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s, const struct wps_credential *cred) { + if (wpa_s->p2p_mgmt) + return; + #ifdef CONFIG_WPS /* notify the old DBus API */ wpa_supplicant_dbus_notify_wps_cred(wpa_s, cred); @@ -190,6 +237,9 @@ void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s, void wpas_notify_wps_event_m2d(struct wpa_supplicant *wpa_s, struct wps_event_m2d *m2d) { + if (wpa_s->p2p_mgmt) + return; + #ifdef CONFIG_WPS wpas_dbus_signal_wps_event_m2d(wpa_s, m2d); #endif /* CONFIG_WPS */ @@ -199,6 +249,9 @@ void wpas_notify_wps_event_m2d(struct wpa_supplicant *wpa_s, void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail) { + if (wpa_s->p2p_mgmt) + return; + #ifdef CONFIG_WPS wpas_dbus_signal_wps_event_fail(wpa_s, fail); #endif /* CONFIG_WPS */ @@ -207,6 +260,9 @@ void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s, void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s) { + if (wpa_s->p2p_mgmt) + return; + #ifdef CONFIG_WPS wpas_dbus_signal_wps_event_success(wpa_s); #endif /* CONFIG_WPS */ @@ -216,13 +272,16 @@ 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) { + if (wpa_s->p2p_mgmt) + return; + /* * 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) + if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s) wpas_dbus_register_network(wpa_s, ssid); } @@ -248,19 +307,28 @@ void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s, void wpas_notify_network_removed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { + if (wpa_s->p2p_mgmt) + return; + + if (wpa_s->next_ssid == ssid) + wpa_s->next_ssid = NULL; if (wpa_s->wpa) wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); - if (wpa_s->global->p2p_group_formation != wpa_s) + if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s) wpas_dbus_unregister_network(wpa_s, ssid->id); -#ifdef CONFIG_P2P + if (network_is_persistent_group(ssid)) + wpas_notify_persistent_group_removed(wpa_s, ssid); + wpas_p2p_network_removed(wpa_s, ssid); -#endif /* CONFIG_P2P */ } void wpas_notify_bss_added(struct wpa_supplicant *wpa_s, u8 bssid[], unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_register_bss(wpa_s, bssid, id); wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_BSS_ADDED "%u " MACSTR, id, MAC2STR(bssid)); @@ -270,6 +338,9 @@ void wpas_notify_bss_added(struct wpa_supplicant *wpa_s, void wpas_notify_bss_removed(struct wpa_supplicant *wpa_s, u8 bssid[], unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_unregister_bss(wpa_s, bssid, id); wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_BSS_REMOVED "%u " MACSTR, id, MAC2STR(bssid)); @@ -279,6 +350,9 @@ void wpas_notify_bss_removed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_freq_changed(struct wpa_supplicant *wpa_s, unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_FREQ, id); } @@ -286,6 +360,9 @@ void wpas_notify_bss_freq_changed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_signal_changed(struct wpa_supplicant *wpa_s, unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_SIGNAL, id); } @@ -294,6 +371,9 @@ void wpas_notify_bss_signal_changed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_privacy_changed(struct wpa_supplicant *wpa_s, unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_PRIVACY, id); } @@ -302,6 +382,9 @@ void wpas_notify_bss_privacy_changed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_mode_changed(struct wpa_supplicant *wpa_s, unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_MODE, id); } @@ -309,6 +392,9 @@ void wpas_notify_bss_mode_changed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_wpaie_changed(struct wpa_supplicant *wpa_s, unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_WPA, id); } @@ -316,6 +402,9 @@ void wpas_notify_bss_wpaie_changed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_rsnie_changed(struct wpa_supplicant *wpa_s, unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_RSN, id); } @@ -323,6 +412,9 @@ void wpas_notify_bss_rsnie_changed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s, unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + #ifdef CONFIG_WPS wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_WPS, id); #endif /* CONFIG_WPS */ @@ -332,6 +424,9 @@ void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s, unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_IES, id); } @@ -339,18 +434,36 @@ void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s, void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s, unsigned int id) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_RATES, id); } +void wpas_notify_bss_seen(struct wpa_supplicant *wpa_s, unsigned int id) +{ + if (wpa_s->p2p_mgmt) + return; + + wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_AGE, id); +} + + void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_blob_added(wpa_s, name); } void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name) { + if (wpa_s->p2p_mgmt) + return; + wpas_dbus_signal_blob_removed(wpa_s, name); } @@ -436,9 +549,9 @@ 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); + + wpas_dbus_unregister_p2p_group(wpa_s, ssid); } @@ -535,37 +648,33 @@ static void wpas_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, 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); + if (p2p_dev_addr) + wpas_dbus_signal_p2p_peer_joined(wpa_s, p2p_dev_addr); #endif /* CONFIG_P2P */ + + /* Notify listeners a new station has been authorized */ + wpas_dbus_signal_sta_authorized(wpa_s, sta); } static void wpas_notify_ap_sta_deauthorized(struct wpa_supplicant *wpa_s, - const u8 *sta) + const u8 *sta, + const u8 *p2p_dev_addr) { #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); + if (p2p_dev_addr) + wpas_dbus_signal_p2p_peer_disconnected(wpa_s, p2p_dev_addr); #endif /* CONFIG_P2P */ + + /* Notify listeners a station has been deauthorized */ + wpas_dbus_signal_sta_deauthorized(wpa_s, sta); } @@ -576,18 +685,18 @@ void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s, if (authorized) wpas_notify_ap_sta_authorized(wpa_s, mac_addr, p2p_dev_addr); else - wpas_notify_ap_sta_deauthorized(wpa_s, mac_addr); + wpas_notify_ap_sta_deauthorized(wpa_s, mac_addr, p2p_dev_addr); } void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth, - const char *subject, const char *cert_hash, + const char *subject, const char *altsubject[], + int num_altsubject, 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=" : "", + depth, subject, cert_hash ? " hash=" : "", cert_hash ? cert_hash : ""); if (cert) { @@ -605,11 +714,20 @@ void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth, } } + if (altsubject) { + int i; + + for (i = 0; i < num_altsubject; i++) + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT + "depth=%d %s", depth, altsubject[i]); + } + /* 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); + wpas_dbus_signal_certification(wpa_s, depth, subject, altsubject, + num_altsubject, cert_hash, cert); } @@ -627,4 +745,41 @@ void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status, const char *parameter) { wpas_dbus_signal_eap_status(wpa_s, status, parameter); + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_EAP_STATUS + "status='%s' parameter='%s'", + status, parameter); +} + + +void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + if (wpa_s->current_ssid != ssid) + return; + + wpa_dbg(wpa_s, MSG_DEBUG, + "Network bssid config changed for the current network - within-ESS roaming %s", + ssid->bssid_set ? "disabled" : "enabled"); + + wpa_drv_roaming(wpa_s, !ssid->bssid_set, + ssid->bssid_set ? ssid->bssid : NULL); +} + + +void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ +#ifdef CONFIG_P2P + if (ssid->disabled == 2) { + /* Changed from normal network profile to persistent group */ + ssid->disabled = 0; + wpas_dbus_unregister_network(wpa_s, ssid->id); + ssid->disabled = 2; + wpas_dbus_register_persistent_group(wpa_s, ssid); + } else { + /* Changed from persistent group to normal network profile */ + wpas_dbus_unregister_persistent_group(wpa_s, ssid->id); + wpas_dbus_register_network(wpa_s, ssid); + } +#endif /* CONFIG_P2P */ } diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h index 58675ac0ce97e..b268332ffc336 100644 --- a/wpa_supplicant/notify.h +++ b/wpa_supplicant/notify.h @@ -71,6 +71,7 @@ void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s, unsigned int id); void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s, unsigned int id); +void wpas_notify_bss_seen(struct wpa_supplicant *wpa_s, unsigned int id); void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name); void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name); @@ -120,12 +121,17 @@ 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 char *subject, const char *altsubject[], + int num_altsubject, const char *cert_hash, const struct wpabuf *cert); void wpas_notify_preq(struct wpa_supplicant *wpa_s, const u8 *addr, const u8 *dst, const u8 *bssid, const u8 *ie, size_t ie_len, u32 ssi_signal); void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status, const char *parameter); +void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); #endif /* NOTIFY_H */ diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c index 856eca708e800..63af83afe1985 100644 --- a/wpa_supplicant/offchannel.c +++ b/wpa_supplicant/offchannel.c @@ -12,6 +12,7 @@ #include "common.h" #include "utils/eloop.h" #include "wpa_supplicant_i.h" +#include "p2p_supplicant.h" #include "driver_i.h" #include "offchannel.h" @@ -30,8 +31,7 @@ wpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src) */ iface = wpa_s->global->ifaces; while (iface) { - if (os_memcmp(wpa_s->pending_action_src, - iface->own_addr, ETH_ALEN) == 0) + if (os_memcmp(src, iface->own_addr, ETH_ALEN) == 0) break; iface = iface->next; } @@ -55,11 +55,12 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx) 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); + wpa_printf(MSG_DEBUG, + "Off-channel: Send Action callback (without_roc=%d pending_action_tx=%p pending_action_tx_done=%d)", + without_roc, wpa_s->pending_action_tx, + !!wpa_s->pending_action_tx_done); - if (wpa_s->pending_action_tx == NULL) + if (wpa_s->pending_action_tx == NULL || wpa_s->pending_action_tx_done) return; /* @@ -83,6 +84,7 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx) wpa_s->off_channel_freq, iface->assoc_freq); if (without_roc && wpa_s->off_channel_freq == 0) { + unsigned int duration = 200; /* * We may get here if wpas_send_action() found us to be * on the correct channel, but remain-on-channel cancel @@ -90,9 +92,18 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx) */ wpa_printf(MSG_DEBUG, "Off-channel: Schedule " "remain-on-channel to send Action frame"); +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->extra_roc_dur) { + wpa_printf(MSG_DEBUG, + "TESTING: Increase ROC duration %u -> %u", + duration, + duration + wpa_s->extra_roc_dur); + duration += wpa_s->extra_roc_dur; + } +#endif /* CONFIG_TESTING_OPTIONS */ if (wpa_drv_remain_on_channel( - wpa_s, wpa_s->pending_action_freq, 200) < - 0) { + wpa_s, wpa_s->pending_action_freq, + duration) < 0) { wpa_printf(MSG_DEBUG, "Off-channel: Failed to " "request driver to remain on " "channel (%u MHz) for Action Frame " @@ -159,6 +170,21 @@ void offchannel_send_action_tx_status( return; } + /* Accept report only if the contents of the frame matches */ + if (data_len - wpabuf_len(wpa_s->pending_action_tx) != 24 || + os_memcmp(data + 24, wpabuf_head(wpa_s->pending_action_tx), + wpabuf_len(wpa_s->pending_action_tx)) != 0) { + wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - " + "mismatching contents with pending frame"); + wpa_hexdump(MSG_MSGDUMP, "TX status frame data", + data, data_len); + wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame", + wpa_s->pending_action_tx); + return; + } + + wpa_printf(MSG_DEBUG, "Off-channel: Delete matching pending action frame"); + wpabuf_free(wpa_s->pending_action_tx); wpa_s->pending_action_tx = NULL; @@ -172,6 +198,14 @@ void offchannel_send_action_tx_status( wpa_s->pending_action_bssid, data, data_len, result); } + +#ifdef CONFIG_P2P + if (wpa_s->p2p_long_listen > 0) { + /* Continue the listen */ + wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state"); + wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen); + } +#endif /* CONFIG_P2P */ } @@ -220,6 +254,7 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, MAC2STR(wpa_s->pending_action_dst)); wpabuf_free(wpa_s->pending_action_tx); } + wpa_s->pending_action_tx_done = 0; 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 " @@ -236,18 +271,21 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) { struct wpa_supplicant *iface; + int ret; - iface = wpas_get_tx_interface(wpa_s, - wpa_s->pending_action_src); + iface = wpas_get_tx_interface(wpa_s, src); wpa_s->action_tx_wait_time = wait_time; - return wpa_drv_send_action( + ret = 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 (ret == 0) + wpa_s->pending_action_tx_done = 1; + return ret; } if (freq) { @@ -285,6 +323,15 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, "channel"); if (wait_time > wpa_s->max_remain_on_chan) wait_time = wpa_s->max_remain_on_chan; + else if (wait_time == 0) + wait_time = 20; +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->extra_roc_dur) { + wpa_printf(MSG_DEBUG, "TESTING: Increase ROC duration %u -> %u", + wait_time, wait_time + wpa_s->extra_roc_dur); + wait_time += wpa_s->extra_roc_dur; + } +#endif /* CONFIG_TESTING_OPTIONS */ 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 " @@ -307,15 +354,18 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, */ void offchannel_send_action_done(struct wpa_supplicant *wpa_s) { - wpa_printf(MSG_DEBUG, "Off-channel: Action frame sequence done " - "notification"); + wpa_printf(MSG_DEBUG, + "Off-channel: Action frame sequence done notification: pending_action_tx=%p drv_offchan_tx=%d action_tx_wait_time=%d off_channel_freq=%d roc_waiting_drv_freq=%d", + wpa_s->pending_action_tx, + !!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX), + wpa_s->action_tx_wait_time, wpa_s->off_channel_freq, + wpa_s->roc_waiting_drv_freq); 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) { + else 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; diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 0a09b004238c4..b200ca0102627 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -1,6 +1,7 @@ /* * wpa_supplicant - P2P * Copyright (c) 2009-2010, Atheros Communications + * Copyright (c) 2010-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -17,7 +18,11 @@ #include "p2p/p2p.h" #include "ap/hostapd.h" #include "ap/ap_config.h" +#include "ap/sta_info.h" +#include "ap/ap_drv_ops.h" +#include "ap/wps_hostapd.h" #include "ap/p2p_hostapd.h" +#include "ap/dfs.h" #include "eapol_supp/eapol_supp_sm.h" #include "rsn_supp/wpa.h" #include "wpa_supplicant_i.h" @@ -31,6 +36,7 @@ #include "offchannel.h" #include "wps_supplicant.h" #include "p2p_supplicant.h" +#include "wifi_display.h" /* @@ -52,14 +58,32 @@ #ifndef P2P_MAX_INITIAL_CONN_WAIT /* * How many seconds to wait for initial 4-way handshake to get completed after - * WPS provisioning step. + * WPS provisioning step or after the re-invocation of a persistent group on a + * P2P Client. */ #define P2P_MAX_INITIAL_CONN_WAIT 10 #endif /* P2P_MAX_INITIAL_CONN_WAIT */ -#ifndef P2P_CONCURRENT_SEARCH_DELAY -#define P2P_CONCURRENT_SEARCH_DELAY 500 -#endif /* P2P_CONCURRENT_SEARCH_DELAY */ +#ifndef P2P_MAX_INITIAL_CONN_WAIT_GO +/* + * How many seconds to wait for initial 4-way handshake to get completed after + * WPS provisioning step on the GO. This controls the extra time the P2P + * operation is considered to be in progress (e.g., to delay other scans) after + * WPS provisioning has been completed on the GO during group formation. + */ +#define P2P_MAX_INITIAL_CONN_WAIT_GO 10 +#endif /* P2P_MAX_INITIAL_CONN_WAIT_GO */ + +#ifndef P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE +/* + * How many seconds to wait for initial 4-way handshake to get completed after + * re-invocation of a persistent group on the GO when the client is expected + * to connect automatically (no user interaction). + */ +#define P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE 15 +#endif /* P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE */ + +#define P2P_MGMT_DEVICE_PREFIX "p2p-dev-" enum p2p_group_removal_reason { P2P_GROUP_REMOVAL_UNKNOWN, @@ -68,7 +92,9 @@ enum p2p_group_removal_reason { P2P_GROUP_REMOVAL_REQUESTED, P2P_GROUP_REMOVAL_IDLE_TIMEOUT, P2P_GROUP_REMOVAL_UNAVAILABLE, - P2P_GROUP_REMOVAL_GO_ENDING_SESSION + P2P_GROUP_REMOVAL_GO_ENDING_SESSION, + P2P_GROUP_REMOVAL_PSK_FAILURE, + P2P_GROUP_REMOVAL_FREQ_CONFLICT }; @@ -76,19 +102,104 @@ 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_req(struct wpa_supplicant *wpa_s, int freq); +static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, + const u8 *ssid, size_t ssid_len); +static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, + const u8 *ssid, size_t ssid_len); 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, - int auto_join); + int auto_join, int freq, + const u8 *ssid, size_t ssid_len); 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_fallback_to_go_neg(struct wpa_supplicant *wpa_s, - int group_added); -static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s); +static void wpas_p2p_group_formation_timeout(void *eloop_ctx, + void *timeout_ctx); +static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx); +static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s, + int group_added); +static void wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s); +static void wpas_stop_listen(void *ctx); +static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx); +static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s); +static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s, + enum wpa_driver_if_type type); + + +/* + * Get the number of concurrent channels that the HW can operate, but that are + * currently not in use by any of the wpa_supplicant interfaces. + */ +static int wpas_p2p_num_unused_channels(struct wpa_supplicant *wpa_s) +{ + int *freqs; + int num, unused; + + freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int)); + if (!freqs) + return -1; + + num = get_shared_radio_freqs(wpa_s, freqs, + wpa_s->num_multichan_concurrent); + os_free(freqs); + + unused = wpa_s->num_multichan_concurrent - num; + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: num_unused_channels: %d", unused); + return unused; +} + + +/* + * Get the frequencies that are currently in use by one or more of the virtual + * interfaces, and that are also valid for P2P operation. + */ +static unsigned int +wpas_p2p_valid_oper_freqs(struct wpa_supplicant *wpa_s, + struct wpa_used_freq_data *p2p_freqs, + unsigned int len) +{ + struct wpa_used_freq_data *freqs; + unsigned int num, i, j; + + freqs = os_calloc(wpa_s->num_multichan_concurrent, + sizeof(struct wpa_used_freq_data)); + if (!freqs) + return 0; + + num = get_shared_radio_freqs_data(wpa_s, freqs, + wpa_s->num_multichan_concurrent); + + os_memset(p2p_freqs, 0, sizeof(struct wpa_used_freq_data) * len); + + for (i = 0, j = 0; i < num && j < len; i++) { + if (p2p_supported_freq(wpa_s->global->p2p, freqs[i].freq)) + p2p_freqs[j++] = freqs[i]; + } + + os_free(freqs); + + dump_freq_data(wpa_s, "valid for P2P", p2p_freqs, j); + + return j; +} + + +static void wpas_p2p_set_own_freq_preference(struct wpa_supplicant *wpa_s, + int freq) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + if (wpa_s->parent->conf->p2p_ignore_shared_freq && + freq > 0 && wpa_s->num_multichan_concurrent > 1 && + wpas_p2p_num_unused_channels(wpa_s) > 0) { + wpa_printf(MSG_DEBUG, "P2P: Ignore own channel preference %d MHz due to p2p_ignore_shared_freq=1 configuration", + freq); + freq = 0; + } + p2p_set_own_freq_preference(wpa_s->global->p2p, freq); +} static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s, @@ -96,6 +207,12 @@ static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s, { size_t i; + if (wpa_s->p2p_scan_work) { + struct wpa_radio_work *work = wpa_s->p2p_scan_work; + wpa_s->p2p_scan_work = NULL; + radio_work_done(work); + } + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; @@ -104,10 +221,29 @@ static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s, for (i = 0; i < scan_res->num; i++) { struct wpa_scan_res *bss = scan_res->res[i]; + struct os_reltime time_tmp_age, entry_ts; + const u8 *ies; + size_t ies_len; + + time_tmp_age.sec = bss->age / 1000; + time_tmp_age.usec = (bss->age % 1000) * 1000; + os_reltime_sub(&scan_res->fetch_time, &time_tmp_age, &entry_ts); + + ies = (const u8 *) (bss + 1); + ies_len = bss->ie_len; + if (bss->beacon_ie_len > 0 && + !wpa_scan_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) && + wpa_scan_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) { + wpa_printf(MSG_DEBUG, "P2P: Use P2P IE(s) from Beacon frame since no P2P IE(s) in Probe Response frames received for " + MACSTR, MAC2STR(bss->bssid)); + ies = ies + ies_len; + ies_len = bss->beacon_ie_len; + } + + if (p2p_scan_res_handler(wpa_s->global->p2p, bss->bssid, - bss->freq, bss->age, bss->level, - (const u8 *) (bss + 1), - bss->ie_len) > 0) + bss->freq, &entry_ts, bss->level, + ies, ies_len) > 0) break; } @@ -115,91 +251,166 @@ static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s, } +static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_supplicant *wpa_s = work->wpa_s; + struct wpa_driver_scan_params *params = work->ctx; + int ret; + + if (deinit) { + if (!work->started) { + wpa_scan_free_params(params); + return; + } + + wpa_s->p2p_scan_work = NULL; + return; + } + + ret = wpa_drv_scan(wpa_s, params); + wpa_scan_free_params(params); + work->ctx = NULL; + if (ret) { + radio_work_done(work); + p2p_notify_scan_trigger_status(wpa_s->global->p2p, ret); + return; + } + + p2p_notify_scan_trigger_status(wpa_s->global->p2p, ret); + os_get_reltime(&wpa_s->scan_trigger_time); + wpa_s->scan_res_handler = wpas_p2p_scan_res_handler; + wpa_s->own_scan_requested = 1; + wpa_s->p2p_scan_work = work; +} + + +static int wpas_p2p_search_social_channel(struct wpa_supplicant *wpa_s, + int freq) +{ + if (wpa_s->global->p2p_24ghz_social_channels && + (freq == 2412 || freq == 2437 || freq == 2462)) { + /* + * Search all social channels regardless of whether these have + * been disabled for P2P operating channel use to avoid missing + * peers. + */ + return 1; + } + return p2p_supported_freq(wpa_s->global->p2p, freq); +} + + 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, u16 pw_id) { struct wpa_supplicant *wpa_s = ctx; - struct wpa_supplicant *ifs; - struct wpa_driver_scan_params params; - int ret; + struct wpa_driver_scan_params *params = NULL; struct wpabuf *wps_ie, *ies; - int social_channels[] = { 2412, 2437, 2462, 0, 0 }; + unsigned int num_channels = 0; + int social_channels_freq[] = { 2412, 2437, 2462, 60480 }; size_t ielen; + u8 *n, i; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; - for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { - if (ifs->sta_scan_pending && - wpas_p2p_in_progress(wpa_s) == 2) { - wpa_printf(MSG_DEBUG, "Delaying P2P scan to allow " - "pending station mode scan to be " - "completed on interface %s", ifs->ifname); - wpa_s->global->p2p_cb_on_scan_complete = 1; - wpa_supplicant_req_scan(ifs, 0, 0); - return 1; - } + if (wpa_s->p2p_scan_work) { + wpa_dbg(wpa_s, MSG_INFO, "P2P: Reject scan trigger since one is already pending"); + return -1; } - os_memset(¶ms, 0, sizeof(params)); + params = os_zalloc(sizeof(*params)); + if (params == NULL) + return -1; /* P2P Wildcard SSID */ - params.num_ssids = 1; - params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID; - params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN; + params->num_ssids = 1; + n = os_malloc(P2P_WILDCARD_SSID_LEN); + if (n == NULL) + goto fail; + os_memcpy(n, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN); + params->ssids[0].ssid = n; + params->ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN; wpa_s->wps->dev.p2p = 1; wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev, wpa_s->wps->uuid, WPS_REQ_ENROLLEE, num_req_dev_types, req_dev_types); if (wps_ie == NULL) - return -1; + goto fail; 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; + goto fail; } 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); + params->p2p_probe = 1; + n = os_malloc(wpabuf_len(ies)); + if (n == NULL) { + wpabuf_free(ies); + goto fail; + } + os_memcpy(n, wpabuf_head(ies), wpabuf_len(ies)); + params->extra_ies = n; + params->extra_ies_len = wpabuf_len(ies); + wpabuf_free(ies); switch (type) { case P2P_SCAN_SOCIAL: - params.freqs = social_channels; + params->freqs = os_calloc(ARRAY_SIZE(social_channels_freq) + 1, + sizeof(int)); + if (params->freqs == NULL) + goto fail; + for (i = 0; i < ARRAY_SIZE(social_channels_freq); i++) { + if (wpas_p2p_search_social_channel( + wpa_s, social_channels_freq[i])) + params->freqs[num_channels++] = + social_channels_freq[i]; + } + params->freqs[num_channels++] = 0; break; case P2P_SCAN_FULL: break; + case P2P_SCAN_SPECIFIC: + params->freqs = os_calloc(2, sizeof(int)); + if (params->freqs == NULL) + goto fail; + params->freqs[0] = freq; + params->freqs[1] = 0; + break; case P2P_SCAN_SOCIAL_PLUS_ONE: - social_channels[3] = freq; - params.freqs = social_channels; + params->freqs = os_calloc(ARRAY_SIZE(social_channels_freq) + 2, + sizeof(int)); + if (params->freqs == NULL) + goto fail; + for (i = 0; i < ARRAY_SIZE(social_channels_freq); i++) { + if (wpas_p2p_search_social_channel( + wpa_s, social_channels_freq[i])) + params->freqs[num_channels++] = + social_channels_freq[i]; + } + if (p2p_supported_freq(wpa_s->global->p2p, freq)) + params->freqs[num_channels++] = freq; + params->freqs[num_channels++] = 0; break; } - ret = wpa_drv_scan(wpa_s, ¶ms); - - wpabuf_free(ies); - - if (ret) { - for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { - if (ifs->scanning || - ifs->scan_res_handler == wpas_p2p_scan_res_handler) { - wpa_s->global->p2p_cb_on_scan_complete = 1; - ret = 1; - break; - } - } - } else - wpa_s->scan_res_handler = wpas_p2p_scan_res_handler; + radio_remove_works(wpa_s, "p2p-scan", 0); + if (radio_add_work(wpa_s, 0, "p2p-scan", 0, wpas_p2p_trigger_scan_cb, + params) < 0) + goto fail; + return 0; - return ret; +fail: + wpa_scan_free_params(params); + return -1; } @@ -243,6 +454,318 @@ static struct wpa_supplicant * wpas_get_p2p_group(struct wpa_supplicant *wpa_s, } +static void run_wpas_p2p_disconnect(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_printf(MSG_DEBUG, + "P2P: Complete previously requested removal of %s", + wpa_s->ifname); + wpas_p2p_disconnect(wpa_s); +} + + +static int wpas_p2p_disconnect_safely(struct wpa_supplicant *wpa_s, + struct wpa_supplicant *calling_wpa_s) +{ + if (calling_wpa_s == wpa_s && wpa_s && + wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) { + /* + * The calling wpa_s instance is going to be removed. Do that + * from an eloop callback to keep the instance available until + * the caller has returned. This my be needed, e.g., to provide + * control interface responses on the per-interface socket. + */ + if (eloop_register_timeout(0, 0, run_wpas_p2p_disconnect, + wpa_s, NULL) < 0) + return -1; + return 0; + } + + return wpas_p2p_disconnect(wpa_s); +} + + +/* Determine total number of clients in active groups where we are the GO */ +static unsigned int p2p_group_go_member_count(struct wpa_supplicant *wpa_s) +{ + unsigned int count = 0; + 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) { + wpa_printf(MSG_DEBUG, + "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d", + wpa_s, s, s->disabled, s->p2p_group, + s->mode); + if (!s->disabled && s->p2p_group && + s->mode == WPAS_MODE_P2P_GO) { + count += p2p_get_group_num_members( + wpa_s->p2p_group); + } + } + } + + return count; +} + + +/* Find an interface for a P2P group where we are the GO */ +static struct wpa_supplicant * +wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s) +{ + struct wpa_supplicant *save = NULL; + struct wpa_ssid *s; + + if (!wpa_s) + return NULL; + + 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 || !s->p2p_group || + s->mode != WPAS_MODE_P2P_GO) + continue; + + /* Prefer a group with connected clients */ + if (p2p_get_group_num_members(wpa_s->p2p_group)) + return wpa_s; + save = wpa_s; + } + } + + /* No group with connected clients, so pick the one without (if any) */ + return save; +} + + +/* Find an active P2P group where we are the GO */ +static struct wpa_ssid * wpas_p2p_group_go_ssid(struct wpa_supplicant *wpa_s, + u8 *bssid) +{ + struct wpa_ssid *s, *empty = NULL; + + if (!wpa_s) + return 0; + + 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 || !s->p2p_group || + s->mode != WPAS_MODE_P2P_GO) + continue; + + os_memcpy(bssid, wpa_s->own_addr, ETH_ALEN); + if (p2p_get_group_num_members(wpa_s->p2p_group)) + return s; + empty = s; + } + } + + return empty; +} + + +/* Find a persistent group where we are the GO */ +static struct wpa_ssid * +wpas_p2p_get_persistent_go(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *s; + + for (s = wpa_s->conf->ssid; s; s = s->next) { + if (s->disabled == 2 && s->mode == WPAS_MODE_P2P_GO) + return s; + } + + return NULL; +} + + +static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role) +{ + struct wpa_supplicant *wpa_s = ctx, *tmp_wpa_s; + struct wpa_ssid *s; + u8 conncap = P2PS_SETUP_NONE; + unsigned int owned_members = 0; + unsigned int owner = 0; + unsigned int client = 0; + struct wpa_supplicant *go_wpa_s; + struct wpa_ssid *persistent_go; + int p2p_no_group_iface; + + wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role); + + /* + * For non-concurrent capable devices: + * If persistent_go, then no new. + * If GO, then no client. + * If client, then no GO. + */ + go_wpa_s = wpas_p2p_get_go_group(wpa_s); + persistent_go = wpas_p2p_get_persistent_go(wpa_s); + p2p_no_group_iface = wpa_s->conf->p2p_no_group_iface; + + wpa_printf(MSG_DEBUG, "P2P: GO(iface)=%p persistent(ssid)=%p", + go_wpa_s, persistent_go); + + for (tmp_wpa_s = wpa_s->global->ifaces; tmp_wpa_s; + tmp_wpa_s = tmp_wpa_s->next) { + for (s = tmp_wpa_s->conf->ssid; s; s = s->next) { + wpa_printf(MSG_DEBUG, + "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d", + tmp_wpa_s, s, s->disabled, + s->p2p_group, s->mode); + if (!s->disabled && s->p2p_group) { + if (s->mode == WPAS_MODE_P2P_GO) { + owned_members += + p2p_get_group_num_members( + tmp_wpa_s->p2p_group); + owner++; + } else + client++; + } + } + } + + /* If not concurrent, restrict our choices */ + if (p2p_no_group_iface) { + wpa_printf(MSG_DEBUG, "P2P: p2p_no_group_iface"); + + if (client) + return P2PS_SETUP_NONE; + + if (go_wpa_s) { + if (role == P2PS_SETUP_CLIENT || + incoming == P2PS_SETUP_GROUP_OWNER || + p2p_client_limit_reached(go_wpa_s->p2p_group)) + return P2PS_SETUP_NONE; + + return P2PS_SETUP_GROUP_OWNER; + } + + if (persistent_go) { + if (role == P2PS_SETUP_NONE || role == P2PS_SETUP_NEW) { + if (!incoming) + return P2PS_SETUP_GROUP_OWNER | + P2PS_SETUP_CLIENT; + if (incoming == P2PS_SETUP_NEW) { + u8 r; + + if (os_get_random(&r, sizeof(r)) < 0 || + (r & 1)) + return P2PS_SETUP_CLIENT; + return P2PS_SETUP_GROUP_OWNER; + } + } + } + } + + /* If a required role has been specified, handle it here */ + if (role && role != P2PS_SETUP_NEW) { + switch (incoming) { + case P2PS_SETUP_NONE: + case P2PS_SETUP_NEW: + case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT: + case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW: + conncap = role; + goto grp_owner; + + case P2PS_SETUP_GROUP_OWNER: + /* + * Must be a complimentary role - cannot be a client to + * more than one peer. + */ + if (incoming == role || client) + return P2PS_SETUP_NONE; + + return P2PS_SETUP_CLIENT; + + case P2PS_SETUP_CLIENT: + /* Must be a complimentary role */ + if (incoming != role) { + conncap = P2PS_SETUP_GROUP_OWNER; + goto grp_owner; + } + + default: + return P2PS_SETUP_NONE; + } + } + + /* + * For now, we only will support ownership of one group, and being a + * client of one group. Therefore, if we have either an existing GO + * group, or an existing client group, we will not do a new GO + * negotiation, but rather try to re-use the existing groups. + */ + switch (incoming) { + case P2PS_SETUP_NONE: + case P2PS_SETUP_NEW: + if (client) + conncap = P2PS_SETUP_GROUP_OWNER; + else if (!owned_members) + conncap = P2PS_SETUP_NEW; + else if (incoming == P2PS_SETUP_NONE) + conncap = P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT; + else + conncap = P2PS_SETUP_CLIENT; + break; + + case P2PS_SETUP_CLIENT: + conncap = P2PS_SETUP_GROUP_OWNER; + break; + + case P2PS_SETUP_GROUP_OWNER: + if (!client) + conncap = P2PS_SETUP_CLIENT; + break; + + case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW: + case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT: + if (client) + conncap = P2PS_SETUP_GROUP_OWNER; + else { + u8 r; + + if (os_get_random(&r, sizeof(r)) < 0 || + (r & 1)) + conncap = P2PS_SETUP_CLIENT; + else + conncap = P2PS_SETUP_GROUP_OWNER; + } + break; + + default: + return P2PS_SETUP_NONE; + } + +grp_owner: + if ((conncap & P2PS_SETUP_GROUP_OWNER) || + (!incoming && (conncap & P2PS_SETUP_NEW))) { + if (go_wpa_s && p2p_client_limit_reached(go_wpa_s->p2p_group)) + conncap &= ~P2PS_SETUP_GROUP_OWNER; + wpa_printf(MSG_DEBUG, "P2P: GOs:%d members:%d conncap:%d", + owner, owned_members, conncap); + + s = wpas_p2p_get_persistent_go(wpa_s); + + if (!s && !owner && p2p_no_group_iface) { + p2p_set_intended_addr(wpa_s->global->p2p, + wpa_s->own_addr); + } else if (!s && !owner) { + 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 P2PS_SETUP_NONE; + } + wpa_s->global->pending_group_iface_for_p2ps = 1; + p2p_set_intended_addr(wpa_s->global->p2p, + wpa_s->pending_interface_addr); + } + } + + return conncap; +} + + static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, enum p2p_group_removal_reason removal_reason) { @@ -277,16 +800,30 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, (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 (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid) + wpas_notify_p2p_group_removed(wpa_s, ssid, gtype); + + if (os_strcmp(gtype, "client") == 0) { + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); + if (eloop_is_timeout_registered(wpas_p2p_psk_failure_removal, + wpa_s, NULL)) { + wpa_printf(MSG_DEBUG, + "P2P: PSK failure removal was scheduled, so use PSK failure as reason for group removal"); + removal_reason = P2P_GROUP_REMOVAL_PSK_FAILURE; + eloop_cancel_timeout(wpas_p2p_psk_failure_removal, + wpa_s, NULL); + } + } + 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); + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", + wpa_s->ifname, wpa_s->cross_connect_uplink); } switch (removal_reason) { case P2P_GROUP_REMOVAL_REQUESTED: @@ -304,21 +841,40 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, case P2P_GROUP_REMOVAL_GO_ENDING_SESSION: reason = " reason=GO_ENDING_SESSION"; break; + case P2P_GROUP_REMOVAL_PSK_FAILURE: + reason = " reason=PSK_FAILURE"; + break; + case P2P_GROUP_REMOVAL_FREQ_CONFLICT: + reason = " reason=FREQ_CONFLICT"; + break; default: reason = ""; break; } if (removal_reason != P2P_GROUP_REMOVAL_SILENT) { - wpa_msg(wpa_s->parent, MSG_INFO, - P2P_EVENT_GROUP_REMOVED "%s %s%s", - wpa_s->ifname, gtype, reason); + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_REMOVED "%s %s%s", + wpa_s->ifname, gtype, reason); } + if (eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL) > 0) + wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group freq_conflict timeout"); if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0) wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout"); + if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL) > 0) { + wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group formation " + "timeout"); + wpa_s->p2p_in_provisioning = 0; + } - if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid) - wpas_notify_p2p_group_removed(wpa_s, ssid, gtype); + wpa_s->p2p_in_invitation = 0; + + /* + * Make sure wait for the first client does not remain active after the + * group has been removed. + */ + wpa_s->global->p2p_go_wait_client.sec = 0; if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) { struct wpa_global *global; @@ -329,6 +885,7 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, global = wpa_s->global; ifname = os_strdup(wpa_s->ifname); type = wpas_p2p_if_type(wpa_s->p2p_group_interface); + eloop_cancel_timeout(run_wpas_p2p_disconnect, wpa_s, NULL); wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0); wpa_s = global->ifaces; if (wpa_s && ifname) @@ -337,6 +894,21 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, return 1; } + if (!wpa_s->p2p_go_group_formation_completed) { + wpa_s->global->p2p_group_formation = NULL; + wpa_s->p2p_in_provisioning = 0; + } + + wpa_s->show_group_started = 0; + os_free(wpa_s->go_params); + wpa_s->go_params = NULL; + + os_free(wpa_s->p2p_group_common_freqs); + wpa_s->p2p_group_common_freqs = NULL; + wpa_s->p2p_group_common_freqs_num = 0; + + wpa_s->waiting_presence_resp = 0; + wpa_printf(MSG_DEBUG, "P2P: Remove temporary group network"); if (ssid && (ssid->p2p_group || ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION || @@ -359,7 +931,6 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s, wpa_config_remove_network(wpa_s->conf, id); wpa_supplicant_clear_status(wpa_s); wpa_supplicant_cancel_sched_scan(wpa_s); - wpa_s->sta_scan_pending = 0; } else { wpa_printf(MSG_DEBUG, "P2P: Temporary group network not " "found"); @@ -389,6 +960,10 @@ static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s, bssid = wpa_s->bssid; bss = wpa_bss_get(wpa_s, bssid, ssid, ssid_len); + if (bss == NULL && wpa_s->go_params && + !is_zero_ether_addr(wpa_s->go_params->peer_device_addr)) + bss = wpa_bss_get_p2p_dev_addr( + wpa_s, wpa_s->go_params->peer_device_addr); if (bss == NULL) { u8 iface_addr[ETH_ALEN]; if (p2p_get_interface_addr(wpa_s->global->p2p, bssid, @@ -403,6 +978,9 @@ static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s, } p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE); + if (p2p == NULL) + p2p = wpa_bss_get_vendor_ie_multi_beacon(bss, + P2P_IE_VENDOR_TYPE); if (p2p == NULL) { wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether " "group is persistent - BSS " MACSTR @@ -509,13 +1087,16 @@ static int wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s, s->ssid_len = ssid->ssid_len; os_memcpy(s->ssid, ssid->ssid, s->ssid_len); } + if (ssid->mode == WPAS_MODE_P2P_GO && wpa_s->global->add_psk) { + dl_list_add(&s->psk_list, &wpa_s->global->add_psk->list); + wpa_s->global->add_psk = NULL; + changed = 1; + } -#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; } @@ -547,7 +1128,7 @@ static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s, 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, + if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN, addr, ETH_ALEN) != 0) continue; @@ -555,39 +1136,92 @@ static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s, return; /* already the most recent entry */ /* move the entry to mark it most recent */ - os_memmove(s->p2p_client_list + i * ETH_ALEN, - s->p2p_client_list + (i + 1) * ETH_ALEN, - (s->num_p2p_clients - i - 1) * ETH_ALEN); + os_memmove(s->p2p_client_list + i * 2 * ETH_ALEN, + s->p2p_client_list + (i + 1) * 2 * ETH_ALEN, + (s->num_p2p_clients - i - 1) * 2 * ETH_ALEN); os_memcpy(s->p2p_client_list + - (s->num_p2p_clients - 1) * ETH_ALEN, addr, ETH_ALEN); + (s->num_p2p_clients - 1) * 2 * ETH_ALEN, addr, + ETH_ALEN); + os_memset(s->p2p_client_list + + (s->num_p2p_clients - 1) * 2 * ETH_ALEN + ETH_ALEN, + 0xff, ETH_ALEN); found = 1; break; } if (!found && s->num_p2p_clients < P2P_MAX_STORED_CLIENTS) { n = os_realloc_array(s->p2p_client_list, - s->num_p2p_clients + 1, ETH_ALEN); + s->num_p2p_clients + 1, 2 * ETH_ALEN); if (n == NULL) return; - os_memcpy(n + s->num_p2p_clients * ETH_ALEN, addr, ETH_ALEN); + os_memcpy(n + s->num_p2p_clients * 2 * ETH_ALEN, addr, + ETH_ALEN); + os_memset(n + s->num_p2p_clients * 2 * ETH_ALEN + ETH_ALEN, + 0xff, ETH_ALEN); s->p2p_client_list = n; s->num_p2p_clients++; - } else if (!found) { + } else if (!found && s->p2p_client_list) { /* Not enough room for an additional entry - drop the oldest * entry */ os_memmove(s->p2p_client_list, - s->p2p_client_list + ETH_ALEN, - (s->num_p2p_clients - 1) * ETH_ALEN); + s->p2p_client_list + 2 * ETH_ALEN, + (s->num_p2p_clients - 1) * 2 * ETH_ALEN); os_memcpy(s->p2p_client_list + - (s->num_p2p_clients - 1) * ETH_ALEN, + (s->num_p2p_clients - 1) * 2 * ETH_ALEN, addr, ETH_ALEN); + os_memset(s->p2p_client_list + + (s->num_p2p_clients - 1) * 2 * ETH_ALEN + ETH_ALEN, + 0xff, ETH_ALEN); } -#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_p2p_group_started(struct wpa_supplicant *wpa_s, + int go, struct wpa_ssid *ssid, int freq, + const u8 *psk, const char *passphrase, + const u8 *go_dev_addr, int persistent, + const char *extra) +{ + const char *ssid_txt; + char psk_txt[65]; + + if (psk) + wpa_snprintf_hex(psk_txt, sizeof(psk_txt), psk, 32); + else + psk_txt[0] = '\0'; + + if (ssid) + ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len); + else + ssid_txt = ""; + + if (passphrase && passphrase[0] == '\0') + passphrase = NULL; + + /* + * Include PSK/passphrase only in the control interface message and + * leave it out from the debug log entry. + */ + wpa_msg_global_ctrl(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_STARTED + "%s %s ssid=\"%s\" freq=%d%s%s%s%s%s go_dev_addr=" + MACSTR "%s%s", + wpa_s->ifname, go ? "GO" : "client", ssid_txt, freq, + psk ? " psk=" : "", psk_txt, + passphrase ? " passphrase=\"" : "", + passphrase ? passphrase : "", + passphrase ? "\"" : "", + MAC2STR(go_dev_addr), + persistent ? " [PERSISTENT]" : "", extra); + wpa_printf(MSG_INFO, P2P_EVENT_GROUP_STARTED + "%s %s ssid=\"%s\" freq=%d go_dev_addr=" MACSTR "%s%s", + wpa_s->ifname, go ? "GO" : "client", ssid_txt, freq, + MAC2STR(go_dev_addr), persistent ? " [PERSISTENT]" : "", + extra); } @@ -595,7 +1229,6 @@ 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]; @@ -608,18 +1241,23 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, */ 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 (wpa_s->p2p_go_group_formation_completed) { + wpa_s->global->p2p_group_formation = NULL; + wpa_s->p2p_in_provisioning = 0; + } + wpa_s->p2p_in_invitation = 0; + wpa_s->group_formation_reported = 1; if (!success) { - wpa_msg(wpa_s->parent, MSG_INFO, - P2P_EVENT_GROUP_FORMATION_FAILURE); + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_FORMATION_FAILURE); wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_FORMATION_FAILED); return; } - wpa_msg(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_FORMATION_SUCCESS); + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_FORMATION_SUCCESS); ssid = wpa_s->current_ssid; if (ssid && ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) { @@ -630,7 +1268,6 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, 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; @@ -642,7 +1279,6 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, 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); @@ -656,25 +1292,13 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, * 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_group_started(wpa_s, 1, ssid, + ssid ? ssid->frequency : 0, + ssid && ssid->passphrase == NULL && + ssid->psk_set ? ssid->psk : NULL, + ssid ? ssid->passphrase : NULL, + go_dev_addr, persistent, ""); wpas_p2p_cross_connect_setup(wpa_s); wpas_p2p_set_group_idle_timeout(wpa_s); } @@ -682,10 +1306,69 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, if (persistent) network_id = wpas_p2p_store_persistent_group(wpa_s->parent, ssid, go_dev_addr); + else { + os_free(wpa_s->global->add_psk); + wpa_s->global->add_psk = NULL; + } if (network_id < 0 && ssid) network_id = ssid->id; - if (!client) + if (!client) { wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0); + os_get_reltime(&wpa_s->global->p2p_go_wait_client); + } +} + + +struct send_action_work { + unsigned int freq; + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + size_t len; + unsigned int wait_time; + u8 buf[0]; +}; + + +static void wpas_p2p_send_action_work_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (!wpa_s->p2p_send_action_work) + return; + + wpa_printf(MSG_DEBUG, "P2P: Send Action frame radio work timed out"); + os_free(wpa_s->p2p_send_action_work->ctx); + radio_work_done(wpa_s->p2p_send_action_work); + wpa_s->p2p_send_action_work = NULL; +} + + +static void wpas_p2p_action_tx_clear(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->p2p_send_action_work) { + struct send_action_work *awork; + awork = wpa_s->p2p_send_action_work->ctx; + if (awork->wait_time == 0) { + os_free(awork); + radio_work_done(wpa_s->p2p_send_action_work); + wpa_s->p2p_send_action_work = NULL; + } else { + /* + * In theory, this should not be needed, but number of + * places in the P2P code is still using non-zero wait + * time for the last Action frame in the sequence and + * some of these do not call send_action_done(). + */ + eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, + wpa_s, NULL); + eloop_register_timeout( + 0, awork->wait_time * 1000, + wpas_p2p_send_action_work_timeout, + wpa_s, NULL); + } + } } @@ -699,10 +1382,10 @@ static void wpas_p2p_send_action_tx_status(struct wpa_supplicant *wpa_s, { enum p2p_send_action_result res = P2P_SEND_ACTION_SUCCESS; + wpas_p2p_action_tx_clear(wpa_s); + 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: @@ -726,17 +1409,96 @@ static void wpas_p2p_send_action_tx_status(struct wpa_supplicant *wpa_s, wpa_s->pending_pd_before_join = 0; wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No ACK for PD Req " "during p2p_connect-auto"); + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_FALLBACK_TO_GO_NEG + "reason=no-ACK-to-PD-Req"); wpas_p2p_fallback_to_go_neg(wpa_s, 0); return; } } +static void wpas_send_action_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_supplicant *wpa_s = work->wpa_s; + struct send_action_work *awork = work->ctx; + + if (deinit) { + if (work->started) { + eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, + wpa_s, NULL); + wpa_s->p2p_send_action_work = NULL; + offchannel_send_action_done(wpa_s); + } + os_free(awork); + return; + } + + if (offchannel_send_action(wpa_s, awork->freq, awork->dst, awork->src, + awork->bssid, awork->buf, awork->len, + awork->wait_time, + wpas_p2p_send_action_tx_status, 1) < 0) { + os_free(awork); + radio_work_done(work); + return; + } + wpa_s->p2p_send_action_work = work; +} + + +static int wpas_send_action_work(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) +{ + struct send_action_work *awork; + + if (wpa_s->p2p_send_action_work) { + wpa_printf(MSG_DEBUG, "P2P: Cannot schedule new p2p-send-action work since one is already pending"); + return -1; + } + + awork = os_zalloc(sizeof(*awork) + len); + if (awork == NULL) + return -1; + + awork->freq = freq; + os_memcpy(awork->dst, dst, ETH_ALEN); + os_memcpy(awork->src, src, ETH_ALEN); + os_memcpy(awork->bssid, bssid, ETH_ALEN); + awork->len = len; + awork->wait_time = wait_time; + os_memcpy(awork->buf, buf, len); + + if (radio_add_work(wpa_s, freq, "p2p-send-action", 0, + wpas_send_action_cb, awork) < 0) { + os_free(awork); + return -1; + } + + return 0; +} + + 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; + int listen_freq = -1, send_freq = -1; + + if (wpa_s->p2p_listen_work) + listen_freq = wpa_s->p2p_listen_work->freq; + if (wpa_s->p2p_send_action_work) + send_freq = wpa_s->p2p_send_action_work->freq; + if (listen_freq != (int) freq && send_freq != (int) freq) { + wpa_printf(MSG_DEBUG, "P2P: Schedule new radio work for Action frame TX (listen_freq=%d send_freq=%d)", + listen_freq, send_freq); + return wpas_send_action_work(wpa_s, freq, dst, src, bssid, buf, + len, wait_time); + } + + wpa_printf(MSG_DEBUG, "P2P: Use ongoing radio work for Action frame TX"); return offchannel_send_action(wpa_s, freq, dst, src, bssid, buf, len, wait_time, wpas_p2p_send_action_tx_status, 1); @@ -746,6 +1508,15 @@ static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst, static void wpas_send_action_done(void *ctx) { struct wpa_supplicant *wpa_s = ctx; + + if (wpa_s->p2p_send_action_work) { + eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, + wpa_s, NULL); + os_free(wpa_s->p2p_send_action_work->ctx); + radio_work_done(wpa_s->p2p_send_action_work); + wpa_s->p2p_send_action_work = NULL; + } + offchannel_send_action_done(wpa_s); } @@ -766,16 +1537,33 @@ static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s, 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_s->group_formation_reported = 0; + wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR + " dev_addr " MACSTR " wps_method %d", + MAC2STR(res->peer_interface_addr), + MAC2STR(res->peer_device_addr), res->wps_method); 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) + if (res->wps_method == WPS_PBC) { wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1); - else { +#ifdef CONFIG_WPS_NFC + } else if (res->wps_method == WPS_NFC) { + wpas_wps_start_nfc(wpa_s, res->peer_device_addr, + res->peer_interface_addr, + wpa_s->parent->p2p_oob_dev_pw, + wpa_s->parent->p2p_oob_dev_pw_id, 1, + wpa_s->parent->p2p_oob_dev_pw_id == + DEV_PW_NFC_CONNECTION_HANDOVER ? + wpa_s->parent->p2p_peer_oob_pubkey_hash : + NULL, + NULL, 0, 0); +#endif /* CONFIG_WPS_NFC */ + } else { u16 dev_pw_id = DEV_PW_DEFAULT; + if (wpa_s->p2p_wps_method == WPS_P2PS) + dev_pw_id = DEV_PW_P2PS_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, @@ -784,6 +1572,88 @@ static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s, } +static void wpas_p2p_add_psk_list(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct wpa_ssid *persistent; + struct psk_list_entry *psk; + struct hostapd_data *hapd; + + if (!wpa_s->ap_iface) + return; + + persistent = wpas_p2p_get_persistent(wpa_s->parent, NULL, ssid->ssid, + ssid->ssid_len); + if (persistent == NULL) + return; + + hapd = wpa_s->ap_iface->bss[0]; + + dl_list_for_each(psk, &persistent->psk_list, struct psk_list_entry, + list) { + struct hostapd_wpa_psk *hpsk; + + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add persistent group PSK entry for " + MACSTR " psk=%d", + MAC2STR(psk->addr), psk->p2p); + hpsk = os_zalloc(sizeof(*hpsk)); + if (hpsk == NULL) + break; + os_memcpy(hpsk->psk, psk->psk, PMK_LEN); + if (psk->p2p) + os_memcpy(hpsk->p2p_dev_addr, psk->addr, ETH_ALEN); + else + os_memcpy(hpsk->addr, psk->addr, ETH_ALEN); + hpsk->next = hapd->conf->ssid.wpa_psk; + hapd->conf->ssid.wpa_psk = hpsk; + } +} + + +static void p2p_go_dump_common_freqs(struct wpa_supplicant *wpa_s) +{ + unsigned int i; + + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Common group frequencies (len=%u):", + wpa_s->p2p_group_common_freqs_num); + + for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) + wpa_dbg(wpa_s, MSG_DEBUG, "freq[%u]: %d", + i, wpa_s->p2p_group_common_freqs[i]); +} + + +static void p2p_go_save_group_common_freqs(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *params) +{ + unsigned int i, len = int_array_len(wpa_s->go_params->freq_list); + + wpa_s->p2p_group_common_freqs_num = 0; + os_free(wpa_s->p2p_group_common_freqs); + wpa_s->p2p_group_common_freqs = os_calloc(len, sizeof(int)); + if (!wpa_s->p2p_group_common_freqs) + return; + + for (i = 0; i < len; i++) { + if (!wpa_s->go_params->freq_list[i]) + break; + wpa_s->p2p_group_common_freqs[i] = + wpa_s->go_params->freq_list[i]; + } + wpa_s->p2p_group_common_freqs_num = i; +} + + +static void p2p_config_write(struct wpa_supplicant *wpa_s) +{ +#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 p2p_go_configured(void *ctx, void *data) { struct wpa_supplicant *wpa_s = ctx; @@ -791,43 +1661,59 @@ static void p2p_go_configured(void *ctx, void *data) struct wpa_ssid *ssid; int network_id = -1; + p2p_go_save_group_common_freqs(wpa_s, params); + p2p_go_dump_common_freqs(wpa_s); + 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; - if (os_strlen(params->passphrase) > 0) { - 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, - MAC2STR(wpa_s->global->p2p_dev_addr), - params->persistent_group ? " [PERSISTENT]" : - ""); - } else { - char psk[65]; - wpa_snprintf_hex(psk, sizeof(psk), params->psk, - sizeof(params->psk)); - 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, - wpa_ssid_txt(ssid->ssid, ssid->ssid_len), - ssid->frequency, psk, - MAC2STR(wpa_s->global->p2p_dev_addr), - params->persistent_group ? " [PERSISTENT]" : - ""); - } - - if (params->persistent_group) + wpas_p2p_group_started(wpa_s, 1, ssid, ssid->frequency, + params->passphrase[0] == '\0' ? + params->psk : NULL, + params->passphrase, + wpa_s->global->p2p_dev_addr, + params->persistent_group, ""); + wpa_s->group_formation_reported = 1; + + if (wpa_s->parent->p2ps_join_addr_valid) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2PS: Setting default PIN for " MACSTR, + MAC2STR(wpa_s->parent->p2ps_join_addr)); + wpa_supplicant_ap_wps_pin(wpa_s, + wpa_s->parent->p2ps_join_addr, + "12345670", NULL, 0, 0); + wpa_s->parent->p2ps_join_addr_valid = 0; + } + + os_get_reltime(&wpa_s->global->p2p_go_wait_client); + if (params->persistent_group) { network_id = wpas_p2p_store_persistent_group( wpa_s->parent, ssid, wpa_s->global->p2p_dev_addr); + wpas_p2p_add_psk_list(wpa_s, ssid); + } 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); + + if (wpa_s->p2p_first_connection_timeout) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Start group formation timeout of %d seconds until first data connection on GO", + wpa_s->p2p_first_connection_timeout); + wpa_s->p2p_go_group_formation_completed = 0; + wpa_s->global->p2p_group_formation = wpa_s; + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + eloop_register_timeout( + wpa_s->p2p_first_connection_timeout, 0, + wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + } + return; } @@ -838,10 +1724,24 @@ static void p2p_go_configured(void *ctx, void *data) "filtering"); return; } - if (params->wps_method == WPS_PBC) + 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]) +#ifdef CONFIG_WPS_NFC + } else if (params->wps_method == WPS_NFC) { + if (wpa_s->parent->p2p_oob_dev_pw_id != + DEV_PW_NFC_CONNECTION_HANDOVER && + !wpa_s->parent->p2p_oob_dev_pw) { + wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known"); + return; + } + wpas_ap_wps_add_nfc_pw( + wpa_s, wpa_s->parent->p2p_oob_dev_pw_id, + wpa_s->parent->p2p_oob_dev_pw, + wpa_s->parent->p2p_peer_oob_pk_hash_known ? + wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL); +#endif /* CONFIG_WPS_NFC */ + } else if (wpa_s->p2p_pin[0]) wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr, wpa_s->p2p_pin, NULL, 0, 0); os_free(wpa_s->go_params); @@ -869,6 +1769,8 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, } wpa_s->show_group_started = 0; + wpa_s->p2p_go_group_formation_completed = 0; + wpa_s->group_formation_reported = 0; wpa_config_set_network_defaults(ssid); ssid->temporary = 1; @@ -878,6 +1780,7 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, WPAS_MODE_P2P_GO; ssid->frequency = params->freq; ssid->ht40 = params->ht40; + ssid->vht = params->vht; ssid->ssid = os_zalloc(params->ssid_len + 1); if (ssid->ssid) { os_memcpy(ssid->ssid, params->ssid, params->ssid_len); @@ -887,11 +1790,20 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, ssid->key_mgmt = WPA_KEY_MGMT_PSK; ssid->proto = WPA_PROTO_RSN; ssid->pairwise_cipher = WPA_CIPHER_CCMP; + ssid->group_cipher = WPA_CIPHER_CCMP; + if (params->freq > 56160) { + /* + * Enable GCMP instead of CCMP as pairwise_cipher and + * group_cipher in 60 GHz. + */ + ssid->pairwise_cipher = WPA_CIPHER_GCMP; + ssid->group_cipher = WPA_CIPHER_GCMP; + } if (os_strlen(params->passphrase) > 0) { ssid->passphrase = os_strdup(params->passphrase); if (ssid->passphrase == NULL) { - wpa_msg(wpa_s, MSG_ERROR, "P2P: Failed to copy " - "passphrase for GO"); + wpa_msg_global(wpa_s, MSG_ERROR, + "P2P: Failed to copy passphrase for GO"); wpa_config_remove_network(wpa_s->conf, ssid->id); return; } @@ -907,6 +1819,7 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s, 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->scan_req = NORMAL_SCAN_REQ; wpa_s->connect_without_scan = ssid; wpa_s->reassociate = 1; wpa_s->disconnected = 0; @@ -944,6 +1857,41 @@ static void wpas_p2p_clone_config(struct wpa_supplicant *dst, d->persistent_reconnect = s->persistent_reconnect; d->max_num_sta = s->max_num_sta; d->pbc_in_m1 = s->pbc_in_m1; + d->ignore_old_scan_res = s->ignore_old_scan_res; + d->beacon_int = s->beacon_int; + d->dtim_period = s->dtim_period; + d->p2p_go_ctwindow = s->p2p_go_ctwindow; + d->disassoc_low_ack = s->disassoc_low_ack; + d->disable_scan_offload = s->disable_scan_offload; + d->passive_scan = s->passive_scan; + + if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey) { + d->wps_nfc_dh_privkey = wpabuf_dup(s->wps_nfc_dh_privkey); + d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey); + } +} + + +static void wpas_p2p_get_group_ifname(struct wpa_supplicant *wpa_s, + char *ifname, size_t len) +{ + char *ifname_ptr = wpa_s->ifname; + + if (os_strncmp(wpa_s->ifname, P2P_MGMT_DEVICE_PREFIX, + os_strlen(P2P_MGMT_DEVICE_PREFIX)) == 0) { + ifname_ptr = os_strrchr(wpa_s->ifname, '-') + 1; + } + + os_snprintf(ifname, len, "p2p-%s-%d", ifname_ptr, wpa_s->p2p_group_idx); + if (os_strlen(ifname) >= IFNAMSIZ && + os_strlen(wpa_s->ifname) < IFNAMSIZ) { + int res; + + /* Try to avoid going over the IFNAMSIZ length limit */ + res = os_snprintf(ifname, len, "p2p-%d", wpa_s->p2p_group_idx); + if (os_snprintf_error(len, res) && len) + ifname[len - 1] = '\0'; + } } @@ -964,14 +1912,7 @@ static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s, 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); - } + wpas_p2p_get_group_ifname(wpa_s, ifname, sizeof(ifname)); force_ifname[0] = '\0'; wpa_printf(MSG_DEBUG, "P2P: Create a new interface %s for the group", @@ -1015,6 +1956,7 @@ static void wpas_p2p_remove_pending_group_interface( wpa_s->pending_interface_name); os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN); wpa_s->pending_interface_name[0] = '\0'; + wpa_s->global->pending_group_iface_for_p2ps = 0; } @@ -1041,19 +1983,25 @@ wpas_p2p_init_group_interface(struct wpa_supplicant *wpa_s, int go) 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; + if (wpa_s->conf->ctrl_interface == NULL && + wpa_s->parent != wpa_s && + wpa_s->p2p_mgmt && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) + iface.ctrl_interface = wpa_s->parent->conf->ctrl_interface; + else + 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); + group_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s); 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; + wpa_s->global->pending_group_iface_for_p2ps = 0; wpas_p2p_clone_config(group_wpa_s, wpa_s); @@ -1066,15 +2014,44 @@ static void wpas_p2p_group_formation_timeout(void *eloop_ctx, { struct wpa_supplicant *wpa_s = eloop_ctx; wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out"); + wpas_p2p_group_formation_failed(wpa_s); +} + + +void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); 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) +static void wpas_p2p_grpform_fail_after_wps(struct wpa_supplicant *wpa_s) +{ + wpa_printf(MSG_DEBUG, "P2P: Reject group formation due to WPS provisioning failure"); + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + wpa_s->global->p2p_fail_on_wps_complete = 0; +} + + +void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->global->p2p_group_formation != wpa_s) + return; + /* Speed up group formation timeout since this cannot succeed */ + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); +} + + +static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) { struct wpa_supplicant *wpa_s = ctx; @@ -1085,8 +2062,9 @@ void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) } if (res->status) { - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_FAILURE "status=%d", - res->status); + wpa_msg_global(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; @@ -1094,8 +2072,16 @@ void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) if (wpa_s->p2p_go_ht40) res->ht40 = 1; - - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS); + if (wpa_s->p2p_go_vht) + res->vht = 1; + + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS "role=%s " + "freq=%d ht40=%d peer_dev=" MACSTR " peer_iface=" MACSTR + " wps_method=%s", + res->role_go ? "GO" : "client", res->freq, res->ht40, + MAC2STR(res->peer_device_addr), + MAC2STR(res->peer_interface_addr), + p2p_wps_method_text(res->wps_method)); wpas_notify_p2p_go_neg_completed(wpa_s, res); if (res->role_go && wpa_s->p2p_persistent_id >= 0) { @@ -1117,6 +2103,9 @@ void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res) wpas_p2p_init_group_interface(wpa_s, res->role_go); if (group_wpa_s == NULL) { wpas_p2p_remove_pending_group_interface(wpa_s); + eloop_cancel_timeout(wpas_p2p_long_listen_timeout, + wpa_s, NULL); + wpas_p2p_group_formation_failed(wpa_s); return; } if (group_wpa_s != wpa_s) { @@ -1152,33 +2141,92 @@ 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) +static 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); + wpa_msg_global(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) +static void wpas_dev_found(void *ctx, const u8 *addr, + const struct p2p_peer_info *info, + int new_device) { #ifndef CONFIG_NO_STDOUT_DEBUG struct wpa_supplicant *wpa_s = ctx; char devtype[WPS_DEV_TYPE_BUFSIZE]; + char *wfd_dev_info_hex = NULL; - 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); +#ifdef CONFIG_WIFI_DISPLAY + wfd_dev_info_hex = wifi_display_subelem_hex(info->wfd_subelems, + WFD_SUBELEM_DEVICE_INFO); +#endif /* CONFIG_WIFI_DISPLAY */ + + if (info->p2ps_instance) { + char str[256]; + const u8 *buf = wpabuf_head(info->p2ps_instance); + size_t len = wpabuf_len(info->p2ps_instance); + + while (len) { + u32 id; + u16 methods; + u8 str_len; + + if (len < 4 + 2 + 1) + break; + id = WPA_GET_LE32(buf); + buf += sizeof(u32); + methods = WPA_GET_BE16(buf); + buf += sizeof(u16); + str_len = *buf++; + if (str_len > len - 4 - 2 - 1) + break; + os_memcpy(str, buf, str_len); + str[str_len] = '\0'; + buf += str_len; + len -= str_len + sizeof(u32) + sizeof(u16) + sizeof(u8); + + wpa_msg_global(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" + " adv_id=%x asp_svc=%s%s", + MAC2STR(addr), + MAC2STR(info->p2p_device_addr), + wps_dev_type_bin2str( + info->pri_dev_type, + devtype, sizeof(devtype)), + info->device_name, methods, + info->dev_capab, info->group_capab, + id, str, + info->vendor_elems ? + " vendor_elems=1" : ""); + } + goto done; + } + + wpa_msg_global(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%s%s%s new=%d", + 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, + wfd_dev_info_hex ? " wfd_dev_info=0x" : "", + wfd_dev_info_hex ? wfd_dev_info_hex : "", + info->vendor_elems ? " vendor_elems=1" : "", + new_device); + +done: + os_free(wfd_dev_info_hex); #endif /* CONFIG_NO_STDOUT_DEBUG */ wpas_notify_p2p_device_found(ctx, info->p2p_device_addr, new_device); @@ -1189,39 +2237,131 @@ 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)); + wpa_msg_global(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) +static void wpas_find_stopped(void *ctx) { struct wpa_supplicant *wpa_s = ctx; + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_FIND_STOPPED); +} + - wpa_drv_set_ap_wps_ie(wpa_s, NULL, probe_resp_ie, NULL); +struct wpas_p2p_listen_work { + unsigned int freq; + unsigned int duration; + struct wpabuf *probe_resp_ie; +}; + + +static void wpas_p2p_listen_work_free(struct wpas_p2p_listen_work *lwork) +{ + if (lwork == NULL) + return; + wpabuf_free(lwork->probe_resp_ie); + os_free(lwork); +} + + +static void wpas_p2p_listen_work_done(struct wpa_supplicant *wpa_s) +{ + struct wpas_p2p_listen_work *lwork; + + if (!wpa_s->p2p_listen_work) + return; + + lwork = wpa_s->p2p_listen_work->ctx; + wpas_p2p_listen_work_free(lwork); + radio_work_done(wpa_s->p2p_listen_work); + wpa_s->p2p_listen_work = NULL; +} + + +static void wpas_start_listen_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_supplicant *wpa_s = work->wpa_s; + struct wpas_p2p_listen_work *lwork = work->ctx; + unsigned int duration; + + if (deinit) { + if (work->started) { + wpa_s->p2p_listen_work = NULL; + wpas_stop_listen(wpa_s); + } + wpas_p2p_listen_work_free(lwork); + return; + } + + wpa_s->p2p_listen_work = work; + + wpa_drv_set_ap_wps_ie(wpa_s, NULL, lwork->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; + wpas_p2p_listen_work_done(wpa_s); + return; } - wpa_s->pending_listen_freq = freq; - wpa_s->pending_listen_duration = duration; + wpa_s->pending_listen_freq = lwork->freq; + wpa_s->pending_listen_duration = lwork->duration; - if (wpa_drv_remain_on_channel(wpa_s, freq, duration) < 0) { + duration = lwork->duration; +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->extra_roc_dur) { + wpa_printf(MSG_DEBUG, "TESTING: Increase ROC duration %u -> %u", + duration, duration + wpa_s->extra_roc_dur); + duration += wpa_s->extra_roc_dur; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, duration) < 0) { wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver " "to remain on channel (%u MHz) for Listen " - "state", freq); + "state", lwork->freq); + wpas_p2p_listen_work_done(wpa_s); wpa_s->pending_listen_freq = 0; - return -1; + return; } wpa_s->off_channel_freq = 0; - wpa_s->roc_waiting_drv_freq = freq; + wpa_s->roc_waiting_drv_freq = lwork->freq; +} + + +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; + struct wpas_p2p_listen_work *lwork; + + if (wpa_s->p2p_listen_work) { + wpa_printf(MSG_DEBUG, "P2P: Reject start_listen since p2p_listen_work already exists"); + return -1; + } + + lwork = os_zalloc(sizeof(*lwork)); + if (lwork == NULL) + return -1; + lwork->freq = freq; + lwork->duration = duration; + if (probe_resp_ie) { + lwork->probe_resp_ie = wpabuf_dup(probe_resp_ie); + if (lwork->probe_resp_ie == NULL) { + wpas_p2p_listen_work_free(lwork); + return -1; + } + } + + if (radio_add_work(wpa_s, freq, "p2p-listen", 0, wpas_start_listen_cb, + lwork) < 0) { + wpas_p2p_listen_work_free(lwork); + return -1; + } return 0; } @@ -1237,6 +2377,7 @@ static void wpas_stop_listen(void *ctx) } wpa_drv_set_ap_wps_ie(wpa_s, NULL, NULL, NULL); wpa_drv_probe_req_report(wpa_s, 0); + wpas_p2p_listen_work_done(wpa_s); } @@ -1411,8 +2552,8 @@ wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version, } -static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto, - u8 srv_trans_id) +static void wpas_sd_add_empty(struct wpabuf *resp, u8 srv_proto, + u8 srv_trans_id, u8 status) { u8 *len_pos; @@ -1424,12 +2565,35 @@ static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto, wpabuf_put_u8(resp, srv_proto); wpabuf_put_u8(resp, srv_trans_id); /* Status Code */ - wpabuf_put_u8(resp, P2P_SD_PROTO_NOT_AVAILABLE); + wpabuf_put_u8(resp, status); /* Response Data: empty */ WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2); } +static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto, + u8 srv_trans_id) +{ + wpas_sd_add_empty(resp, srv_proto, srv_trans_id, + P2P_SD_PROTO_NOT_AVAILABLE); +} + + +static void wpas_sd_add_bad_request(struct wpabuf *resp, u8 srv_proto, + u8 srv_trans_id) +{ + wpas_sd_add_empty(resp, srv_proto, srv_trans_id, P2P_SD_BAD_REQUEST); +} + + +static void wpas_sd_add_not_found(struct wpabuf *resp, u8 srv_proto, + u8 srv_trans_id) +{ + wpas_sd_add_empty(resp, srv_proto, srv_trans_id, + P2P_SD_REQUESTED_INFO_NOT_AVAILABLE); +} + + static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s, struct wpabuf *resp, u8 srv_trans_id) { @@ -1737,8 +2901,150 @@ static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s, #endif /* CONFIG_WIFI_DISPLAY */ -void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, - u16 update_indic, const u8 *tlvs, size_t tlvs_len) +static int find_p2ps_substr(struct p2ps_advertisement *adv_data, + const u8 *needle, size_t needle_len) +{ + const u8 *haystack = (const u8 *) adv_data->svc_info; + size_t haystack_len, i; + + /* Allow search term to be empty */ + if (!needle || !needle_len) + return 1; + + if (!haystack) + return 0; + + haystack_len = os_strlen(adv_data->svc_info); + for (i = 0; i < haystack_len; i++) { + if (haystack_len - i < needle_len) + break; + if (os_memcmp(haystack + i, needle, needle_len) == 0) + return 1; + } + + return 0; +} + + +static void wpas_sd_req_asp(struct wpa_supplicant *wpa_s, + struct wpabuf *resp, u8 srv_trans_id, + const u8 *query, size_t query_len) +{ + struct p2ps_advertisement *adv_data; + const u8 *svc = &query[1]; + const u8 *info = NULL; + size_t svc_len = query[0]; + size_t info_len = 0; + int prefix = 0; + u8 *count_pos = NULL; + u8 *len_pos = NULL; + + wpa_hexdump(MSG_DEBUG, "P2P: SD Request for ASP", query, query_len); + + if (!wpa_s->global->p2p) { + wpa_printf(MSG_DEBUG, "P2P: ASP protocol not available"); + wpas_sd_add_proto_not_avail(resp, P2P_SERV_P2PS, srv_trans_id); + return; + } + + /* Info block is optional */ + if (svc_len + 1 < query_len) { + info = &svc[svc_len]; + info_len = *info++; + } + + /* Range check length of svc string and info block */ + if (svc_len + (info_len ? info_len + 2 : 1) > query_len) { + wpa_printf(MSG_DEBUG, "P2P: ASP bad request"); + wpas_sd_add_bad_request(resp, P2P_SERV_P2PS, srv_trans_id); + return; + } + + /* Detect and correct for prefix search */ + if (svc_len && svc[svc_len - 1] == '*') { + prefix = 1; + svc_len--; + } + + for (adv_data = p2p_get_p2ps_adv_list(wpa_s->global->p2p); + adv_data; adv_data = adv_data->next) { + /* If not a prefix match, reject length mismatches */ + if (!prefix && svc_len != os_strlen(adv_data->svc_name)) + continue; + + /* Search each service for request */ + if (os_memcmp(adv_data->svc_name, svc, svc_len) == 0 && + find_p2ps_substr(adv_data, info, info_len)) { + size_t len = os_strlen(adv_data->svc_name); + size_t svc_info_len = 0; + + if (adv_data->svc_info) + svc_info_len = os_strlen(adv_data->svc_info); + + if (len > 0xff || svc_info_len > 0xffff) + return; + + /* Length & Count to be filled as we go */ + if (!len_pos && !count_pos) { + if (wpabuf_tailroom(resp) < + len + svc_info_len + 16) + return; + + len_pos = wpabuf_put(resp, 2); + wpabuf_put_u8(resp, P2P_SERV_P2PS); + wpabuf_put_u8(resp, srv_trans_id); + /* Status Code */ + wpabuf_put_u8(resp, P2P_SD_SUCCESS); + count_pos = wpabuf_put(resp, 1); + *count_pos = 0; + } else if (wpabuf_tailroom(resp) < + len + svc_info_len + 10) + return; + + if (svc_info_len) { + wpa_printf(MSG_DEBUG, + "P2P: Add Svc: %s info: %s", + adv_data->svc_name, + adv_data->svc_info); + } else { + wpa_printf(MSG_DEBUG, "P2P: Add Svc: %s", + adv_data->svc_name); + } + + /* Advertisement ID */ + wpabuf_put_le32(resp, adv_data->id); + + /* Config Methods */ + wpabuf_put_be16(resp, adv_data->config_methods); + + /* Service Name */ + wpabuf_put_u8(resp, (u8) len); + wpabuf_put_data(resp, adv_data->svc_name, len); + + /* Service State */ + wpabuf_put_u8(resp, adv_data->state); + + /* Service Information */ + wpabuf_put_le16(resp, (u16) svc_info_len); + wpabuf_put_data(resp, adv_data->svc_info, svc_info_len); + + /* Update length and count */ + (*count_pos)++; + WPA_PUT_LE16(len_pos, + (u8 *) wpabuf_put(resp, 0) - len_pos - 2); + } + } + + /* Return error if no matching svc found */ + if (count_pos == NULL) { + wpa_printf(MSG_DEBUG, "P2P: ASP service not found"); + wpas_sd_add_not_found(resp, P2P_SERV_P2PS, srv_trans_id); + } +} + + +static 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; @@ -1834,6 +3140,10 @@ void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token, pos, tlv_end - pos); break; #endif /* CONFIG_WIFI_DISPLAY */ + case P2P_SERV_P2PS: + wpas_sd_req_asp(wpa_s, resp, srv_trans_id, + pos, tlv_end - pos); + break; default: wpa_printf(MSG_DEBUG, "P2P: Unavailable service " "protocol %u", srv_proto); @@ -1855,8 +3165,82 @@ done: } -void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, - const u8 *tlvs, size_t tlvs_len) +static void wpas_sd_p2ps_serv_response(struct wpa_supplicant *wpa_s, + const u8 *sa, u8 srv_trans_id, + const u8 *pos, const u8 *tlv_end) +{ + u8 left = *pos++; + u32 adv_id; + u8 svc_status; + u16 config_methods; + char svc_str[256]; + + while (left-- && pos < tlv_end) { + char *buf = NULL; + size_t buf_len; + u8 svc_len; + + /* Sanity check fixed length+svc_str */ + if (pos + 6 >= tlv_end) + break; + svc_len = pos[6]; + if (pos + svc_len + 10 > tlv_end) + break; + + /* Advertisement ID */ + adv_id = WPA_GET_LE32(pos); + pos += sizeof(u32); + + /* Config Methods */ + config_methods = WPA_GET_BE16(pos); + pos += sizeof(u16); + + /* Service Name */ + pos++; /* svc_len */ + os_memcpy(svc_str, pos, svc_len); + svc_str[svc_len] = '\0'; + pos += svc_len; + + /* Service Status */ + svc_status = *pos++; + + /* Service Information Length */ + buf_len = WPA_GET_LE16(pos); + pos += sizeof(u16); + + /* Sanity check buffer length */ + if (buf_len > (unsigned int) (tlv_end - pos)) + break; + + if (buf_len) { + buf = os_zalloc(2 * buf_len + 1); + if (buf) { + utf8_escape((const char *) pos, buf_len, buf, + 2 * buf_len + 1); + } + } + + pos += buf_len; + + if (buf) { + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP + MACSTR " %x %x %x %x %s '%s'", + MAC2STR(sa), srv_trans_id, adv_id, + svc_status, config_methods, svc_str, + buf); + os_free(buf); + } else { + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP + MACSTR " %x %x %x %x %s", + MAC2STR(sa), srv_trans_id, adv_id, + svc_status, config_methods, svc_str); + } + } +} + + +static 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; @@ -1913,6 +3297,11 @@ void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data", pos, tlv_end - pos); + if (srv_proto == P2P_SERV_P2PS && pos < tlv_end) { + wpas_sd_p2ps_serv_response(wpa_s, sa, srv_trans_id, + pos, tlv_end); + } + pos = tlv_end; } @@ -1923,8 +3312,6 @@ void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic, u64 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 wpa_drv_p2p_sd_request(wpa_s, dst, tlvs); if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return 0; return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs); @@ -1951,13 +3338,44 @@ u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, } +u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id, + const char *svc_str, const char *info_substr) +{ + struct wpabuf *tlvs; + size_t plen, svc_len, substr_len = 0; + u64 ret; + + svc_len = os_strlen(svc_str); + if (info_substr) + substr_len = os_strlen(info_substr); + + if (svc_len > 0xff || substr_len > 0xff) + return 0; + + plen = 1 + 1 + 1 + svc_len + 1 + substr_len; + tlvs = wpabuf_alloc(2 + plen); + if (tlvs == NULL) + return 0; + + wpabuf_put_le16(tlvs, plen); + wpabuf_put_u8(tlvs, P2P_SERV_P2PS); + wpabuf_put_u8(tlvs, id); /* Service Transaction ID */ + wpabuf_put_u8(tlvs, (u8) svc_len); /* Service String Length */ + wpabuf_put_data(tlvs, svc_str, svc_len); + wpabuf_put_u8(tlvs, (u8) substr_len); /* Info Substring Length */ + wpabuf_put_data(tlvs, info_substr, substr_len); + ret = wpas_p2p_sd_request(wpa_s, dst, tlvs); + wpabuf_free(tlvs); + + return ret; +} + + #ifdef CONFIG_WIFI_DISPLAY static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst, const struct wpabuf *tlvs) { - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - return 0; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return 0; return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs); @@ -2035,8 +3453,6 @@ u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s, int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req) { - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - return wpa_drv_p2p_sd_cancel_request(wpa_s, req); if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; return p2p_sd_cancel_request(wpa_s->global->p2p, @@ -2048,11 +3464,6 @@ 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, @@ -2062,10 +3473,6 @@ void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq, 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); } @@ -2105,6 +3512,35 @@ void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s) } +int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id) +{ + if (adv_id == 0) + return 1; + + if (p2p_service_p2ps_id(wpa_s->global->p2p, adv_id)) + return 1; + + return 0; +} + + +int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id) +{ + return p2p_service_del_asp(wpa_s->global->p2p, adv_id); +} + + +int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s, + int auto_accept, u32 adv_id, + const char *adv_str, u8 svc_state, + u16 config_methods, const char *svc_info) +{ + return p2p_service_add_asp(wpa_s->global->p2p, auto_accept, adv_id, + adv_str, svc_state, config_methods, + svc_info); +} + + int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s, struct wpabuf *query, struct wpabuf *resp) { @@ -2177,24 +3613,24 @@ 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); + wpa_msg_global(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); + wpa_msg_global(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) +static 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]; @@ -2202,6 +3638,7 @@ void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods, u8 empty_dev_type[8]; unsigned int generated_pin = 0; struct wpa_supplicant *group = NULL; + int res; if (group_id) { for (group = wpa_s->global->ifaces; group; group = group->next) @@ -2220,15 +3657,17 @@ void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods, 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 : ""); + res = 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 : ""); + if (os_snprintf_error(sizeof(params), res)) + wpa_printf(MSG_DEBUG, "P2P: PD Request event truncated"); params[sizeof(params) - 1] = '\0'; if (config_methods & WPS_CONFIG_DISPLAY) { @@ -2238,8 +3677,8 @@ void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods, } 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); + wpa_msg_global(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, @@ -2247,7 +3686,7 @@ void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods, } -void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods) +static void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods) { struct wpa_supplicant *wpa_s = ctx; unsigned int generated_pin = 0; @@ -2259,15 +3698,19 @@ void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods) wpa_s->pending_pd_before_join = 0; wpa_printf(MSG_DEBUG, "P2P: Starting pending " "join-existing-group operation"); - wpas_p2p_join_start(wpa_s); + wpas_p2p_join_start(wpa_s, 0, NULL, 0); return; } if (wpa_s->pending_pd_use == AUTO_PD_JOIN || - wpa_s->pending_pd_use == AUTO_PD_GO_NEG) - os_snprintf(params, sizeof(params), " peer_go=%d", - wpa_s->pending_pd_use == AUTO_PD_JOIN); - else + wpa_s->pending_pd_use == AUTO_PD_GO_NEG) { + int res; + + res = os_snprintf(params, sizeof(params), " peer_go=%d", + wpa_s->pending_pd_use == AUTO_PD_JOIN); + if (os_snprintf_error(sizeof(params), res)) + params[sizeof(params) - 1] = '\0'; + } else params[0] = '\0'; if (config_methods & WPS_CONFIG_DISPLAY) @@ -2277,8 +3720,8 @@ void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods) wpas_prov_disc_local_display(wpa_s, peer, params, generated_pin); } else if (config_methods & WPS_CONFIG_PUSHBUTTON) - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP MACSTR - "%s", MAC2STR(peer), params); + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_PBC_RESP + MACSTR "%s", MAC2STR(peer), params); wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */, P2P_PROV_DISC_SUCCESS, @@ -2287,13 +3730,18 @@ void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods) static void wpas_prov_disc_fail(void *ctx, const u8 *peer, - enum p2p_prov_disc_status status) + enum p2p_prov_disc_status status, + u32 adv_id, const u8 *adv_mac, + const char *deferred_session_resp) { struct wpa_supplicant *wpa_s = ctx; if (wpa_s->p2p_fallback_to_go_neg) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: PD for p2p_connect-auto " "failed - fall back to GO Negotiation"); + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_FALLBACK_TO_GO_NEG + "reason=PD-failed"); wpas_p2p_fallback_to_go_neg(wpa_s, 0); return; } @@ -2303,33 +3751,96 @@ static void wpas_prov_disc_fail(void *ctx, const u8 *peer, wpa_printf(MSG_DEBUG, "P2P: Starting pending " "join-existing-group operation (no ACK for PD " "Req attempts)"); - wpas_p2p_join_start(wpa_s); + wpas_p2p_join_start(wpa_s, 0, NULL, 0); return; } - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE - " p2p_dev_addr=" MACSTR " status=%d", - MAC2STR(peer), status); + if (adv_id && adv_mac && deferred_session_resp) { + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE + " p2p_dev_addr=" MACSTR " status=%d adv_id=%x" + " deferred_session_resp='%s'", + MAC2STR(peer), status, adv_id, + deferred_session_resp); + } else if (adv_id && adv_mac) { + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE + " p2p_dev_addr=" MACSTR " status=%d adv_id=%x", + MAC2STR(peer), status, adv_id); + } else { + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE + " p2p_dev_addr=" MACSTR " status=%d", + MAC2STR(peer), status); + } wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */, status, 0, 0); } +static int freq_included(const struct p2p_channels *channels, unsigned int freq) +{ + if (channels == NULL) + return 1; /* Assume no restrictions */ + return p2p_channels_includes_freq(channels, freq); + +} + + +/** + * Pick the best frequency to use from all the currently used frequencies. + */ +static int wpas_p2p_pick_best_used_freq(struct wpa_supplicant *wpa_s, + struct wpa_used_freq_data *freqs, + unsigned int num) +{ + unsigned int i, c; + + /* find a candidate freq that is supported by P2P */ + for (c = 0; c < num; c++) + if (p2p_supported_freq(wpa_s->global->p2p, freqs[c].freq)) + break; + + if (c == num) + return 0; + + /* once we have a candidate, try to find a 'better' one */ + for (i = c + 1; i < num; i++) { + if (!p2p_supported_freq(wpa_s->global->p2p, freqs[i].freq)) + continue; + + /* + * 1. Infrastructure station interfaces have higher preference. + * 2. P2P Clients have higher preference. + * 3. All others. + */ + if (freqs[i].flags & WPA_FREQ_USED_BY_INFRA_STATION) { + c = i; + break; + } + + if ((freqs[i].flags & WPA_FREQ_USED_BY_P2P_CLIENT)) + c = i; + } + return freqs[c].freq; +} + + 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) + int *force_freq, int persistent_group, + const struct p2p_channels *channels, + int dev_pw_id) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *s; - u8 cur_bssid[ETH_ALEN]; - int res; + struct wpa_used_freq_data *freqs; struct wpa_supplicant *grp; + int best_freq; if (!persistent_group) { wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR - " to join an active group", MAC2STR(sa)); + " to join an active group (SSID: %s)", + MAC2STR(sa), wpa_ssid_txt(ssid, ssid_len)); if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) && (os_memcmp(go_dev_addr, wpa_s->p2p_auth_invite, ETH_ALEN) == 0 || @@ -2338,6 +3849,21 @@ static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid, "authorized invitation"); goto accept_inv; } + +#ifdef CONFIG_WPS_NFC + if (dev_pw_id >= 0 && wpa_s->p2p_nfc_tag_enabled && + dev_pw_id == wpa_s->p2p_oob_dev_pw_id) { + wpa_printf(MSG_DEBUG, "P2P: Accept invitation based on local enabled NFC Tag"); + wpa_s->p2p_wps_method = WPS_NFC; + wpa_s->pending_join_wps_method = WPS_NFC; + os_memcpy(wpa_s->pending_join_dev_addr, + go_dev_addr, ETH_ALEN); + os_memcpy(wpa_s->pending_join_iface_addr, + bssid, ETH_ALEN); + goto accept_inv; + } +#endif /* CONFIG_WPS_NFC */ + /* * Do not accept the invitation automatically; notify user and * request approval. @@ -2354,7 +3880,12 @@ static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid, goto accept_inv; } - if (!wpa_s->conf->persistent_reconnect) + if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) && + os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "P2P: Accept previously initiated " + "invitation to re-invoke a persistent group"); + os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); + } else if (!wpa_s->conf->persistent_reconnect) return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE; for (s = wpa_s->conf->ssid; s; s = s->next) { @@ -2394,19 +3925,47 @@ static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid, } 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; + wpas_p2p_set_own_freq_preference(wpa_s, 0); + + best_freq = 0; + freqs = os_calloc(wpa_s->num_multichan_concurrent, + sizeof(struct wpa_used_freq_data)); + if (freqs) { + int num_channels = wpa_s->num_multichan_concurrent; + int num = wpas_p2p_valid_oper_freqs(wpa_s, freqs, num_channels); + best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num); + os_free(freqs); + } + + /* Get one of the frequencies currently in use */ + if (best_freq > 0) { + wpa_printf(MSG_DEBUG, "P2P: Trying to prefer a channel already used by one of the interfaces"); + wpas_p2p_set_own_freq_preference(wpa_s, best_freq); + + if (wpa_s->num_multichan_concurrent < 2 || + wpas_p2p_num_unused_channels(wpa_s) < 1) { + wpa_printf(MSG_DEBUG, "P2P: No extra channels available - trying to force channel to match a channel already used by one of the interfaces"); + *force_freq = best_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; + if (*force_freq > 0 && wpa_s->num_multichan_concurrent > 1 && + wpas_p2p_num_unused_channels(wpa_s) > 0) { + if (*go == 0) { + /* We are the client */ + wpa_printf(MSG_DEBUG, "P2P: Peer was found to be " + "running a GO but we are capable of MCC, " + "figure out the best channel to use"); + *force_freq = 0; + } else if (!freq_included(channels, *force_freq)) { + /* We are the GO, and *force_freq is not in the + * intersection */ + wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not " + "in intersection but we are capable of MCC, " + "figure out the best channel to use", + *force_freq); + *force_freq = 0; + } } return P2P_SC_SUCCESS; @@ -2430,16 +3989,18 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid, if (status == P2P_SC_SUCCESS) { wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR - " was accepted; op_freq=%d MHz", - MAC2STR(sa), op_freq); + " was accepted; op_freq=%d MHz, SSID=%s", + MAC2STR(sa), op_freq, wpa_ssid_txt(ssid, ssid_len)); if (s) { int go = s->mode == WPAS_MODE_P2P_GO; wpas_p2p_group_add_persistent( - wpa_s, s, go, go ? op_freq : 0, 0); + wpa_s, s, go, 0, op_freq, 0, 0, NULL, + go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0); } else if (bssid) { wpa_s->user_initiated_pd = 0; wpas_p2p_join(wpa_s, bssid, go_dev_addr, - wpa_s->p2p_wps_method, 0); + wpa_s->p2p_wps_method, 0, op_freq, + ssid, ssid_len); } return; } @@ -2452,44 +4013,131 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid, 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)); + wpa_msg_global(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)); + wpa_msg_global(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); + if (s->mode == WPAS_MODE_P2P_GO && op_freq) { + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED + "sa=" MACSTR " persistent=%d freq=%d", + MAC2STR(sa), s->id, op_freq); + } else { + wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RECEIVED + "sa=" MACSTR " persistent=%d", + MAC2STR(sa), s->id); + } +} + + +static void wpas_remove_persistent_peer(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + const u8 *peer, int inv) +{ + size_t i; + + if (ssid == NULL) + return; + + for (i = 0; ssid->p2p_client_list && i < ssid->num_p2p_clients; i++) { + if (os_memcmp(ssid->p2p_client_list + i * 2 * ETH_ALEN, peer, + ETH_ALEN) == 0) + break; + } + if (i >= ssid->num_p2p_clients || !ssid->p2p_client_list) { + if (ssid->mode != WPAS_MODE_P2P_GO && + os_memcmp(ssid->bssid, peer, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "P2P: Remove persistent group %d " + "due to invitation result", ssid->id); + wpas_notify_network_removed(wpa_s, ssid); + wpa_config_remove_network(wpa_s->conf, ssid->id); + return; + } + return; /* Peer not found in client list */ + } + + wpa_printf(MSG_DEBUG, "P2P: Remove peer " MACSTR " from persistent " + "group %d client list%s", + MAC2STR(peer), ssid->id, + inv ? " due to invitation result" : ""); + os_memmove(ssid->p2p_client_list + i * 2 * ETH_ALEN, + ssid->p2p_client_list + (i + 1) * 2 * ETH_ALEN, + (ssid->num_p2p_clients - i - 1) * 2 * ETH_ALEN); + ssid->num_p2p_clients--; + 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"); } -static void wpas_invitation_result(void *ctx, int status, const u8 *bssid) +static void wpas_remove_persistent_client(struct wpa_supplicant *wpa_s, + const u8 *peer) +{ + struct wpa_ssid *ssid; + + wpa_s = wpa_s->global->p2p_invite_group; + if (wpa_s == NULL) + return; /* No known invitation group */ + ssid = wpa_s->current_ssid; + if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GO || + !ssid->p2p_persistent_group) + return; /* Not operating as a GO in persistent group */ + ssid = wpas_p2p_get_persistent(wpa_s->parent, peer, + ssid->ssid, ssid->ssid_len); + wpas_remove_persistent_peer(wpa_s, ssid, peer, 1); +} + + +static void wpas_invitation_result(void *ctx, int status, const u8 *bssid, + const struct p2p_channels *channels, + const u8 *peer, int neg_freq, + int peer_oper_freq) { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *ssid; + int freq; if (bssid) { - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT - "status=%d " MACSTR, - status, MAC2STR(bssid)); + wpa_msg_global(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); + wpa_msg_global(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) + wpa_printf(MSG_DEBUG, "P2P: Invitation result - status=%d peer=" MACSTR, + status, MAC2STR(peer)); + if (wpa_s->pending_invite_ssid_id == -1) { + if (status == P2P_SC_FAIL_UNKNOWN_GROUP) + wpas_remove_persistent_client(wpa_s, peer); return; /* Invitation to active group */ + } + + if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) { + wpa_printf(MSG_DEBUG, "P2P: Waiting for peer to start another " + "invitation exchange to indicate readiness for " + "re-invocation"); + } if (status != P2P_SC_SUCCESS) { + if (status == P2P_SC_FAIL_UNKNOWN_GROUP) { + ssid = wpa_config_get_network( + wpa_s->conf, wpa_s->pending_invite_ssid_id); + wpas_remove_persistent_peer(wpa_s, ssid, peer, 1); + } wpas_p2p_remove_pending_group_interface(wpa_s); return; } @@ -2514,28 +4162,35 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid) "starting persistent group"); os_sleep(0, 50000); + if (neg_freq > 0 && ssid->mode == WPAS_MODE_P2P_GO && + freq_included(channels, neg_freq)) + freq = neg_freq; + else if (peer_oper_freq > 0 && ssid->mode != WPAS_MODE_P2P_GO && + freq_included(channels, peer_oper_freq)) + freq = peer_oper_freq; + else + freq = 0; + + wpa_printf(MSG_DEBUG, "P2P: Persistent group invitation success - op_freq=%d MHz SSID=%s", + freq, wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); wpas_p2p_group_add_persistent(wpa_s, ssid, ssid->mode == WPAS_MODE_P2P_GO, wpa_s->p2p_persistent_go_freq, - wpa_s->p2p_go_ht40); + freq, + wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht, + channels, + ssid->mode == WPAS_MODE_P2P_GO ? + P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : + 0); } static int wpas_p2p_disallowed_freq(struct wpa_global *global, unsigned int freq) { - unsigned int i; - - if (global->p2p_disallow_freq == NULL) - return 0; - - for (i = 0; i < global->num_p2p_disallow_freq; i++) { - if (freq >= global->p2p_disallow_freq[i].min && - freq <= global->p2p_disallow_freq[i].max) - return 1; - } - - return 0; + if (freq_range_list_includes(&global->p2p_go_avoid_freq, freq)) + return 1; + return freq_range_list_includes(&global->p2p_disallow_freq, freq); } @@ -2547,10 +4202,15 @@ static void wpas_p2p_add_chan(struct p2p_reg_class *reg, u8 chan) static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s, - struct p2p_channels *chan) + struct p2p_channels *chan, + struct p2p_channels *cli_chan) { int i, cla = 0; + wpa_s->global->p2p_24ghz_social_channels = 1; + + os_memset(cli_chan, 0, sizeof(*cli_chan)); + wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for 2.4 GHz " "band"); @@ -2618,6 +4278,10 @@ static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, } +enum chan_allowed { + NOT_ALLOWED, NO_IR, ALLOWED +}; + static int has_channel(struct wpa_global *global, struct hostapd_hw_modes *mode, u8 chan, int *flags) { @@ -2627,21 +4291,23 @@ static int has_channel(struct wpa_global *global, freq = (mode->mode == HOSTAPD_MODE_IEEE80211A ? 5000 : 2407) + chan * 5; if (wpas_p2p_disallowed_freq(global, freq)) - return 0; + return NOT_ALLOWED; 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)); + if (mode->channels[i].flag & + (HOSTAPD_CHAN_DISABLED | + HOSTAPD_CHAN_RADAR)) + return NOT_ALLOWED; + if (mode->channels[i].flag & HOSTAPD_CHAN_NO_IR) + return NO_IR; + return ALLOWED; } } - return 0; + return NOT_ALLOWED; } @@ -2651,7 +4317,7 @@ struct p2p_oper_class_map { u8 min_chan; u8 max_chan; u8 inc; - enum { BW20, BW40PLUS, BW40MINUS } bw; + enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160 } bw; }; static struct p2p_oper_class_map op_class[] = { @@ -2666,73 +4332,174 @@ static struct p2p_oper_class_map op_class[] = { { HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS }, { HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS }, { HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS }, + + /* + * IEEE P802.11ac/D7.0 Table E-4 actually talks about channel center + * frequency index 42, 58, 106, 122, 138, 155 with channel spacing of + * 80 MHz, but currently use the following definition for simplicity + * (these center frequencies are not actual channels, which makes + * has_channel() fail). wpas_p2p_verify_80mhz() should take care of + * removing invalid channels. + */ + { HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80 }, + { HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160 }, { -1, 0, 0, 0, 0, BW20 } }; -static int wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s, - struct hostapd_hw_modes *mode, - u8 channel, u8 bw) +static int wpas_p2p_get_center_80mhz(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, + u8 channel) { - int flag; + u8 center_channels[] = { 42, 58, 106, 122, 138, 155 }; + unsigned int i; - if (!has_channel(wpa_s->global, mode, channel, &flag)) - return -1; - if (bw == BW40MINUS && - (!(flag & HOSTAPD_CHAN_HT40MINUS) || - !has_channel(wpa_s->global, mode, channel - 4, NULL))) + if (mode->mode != HOSTAPD_MODE_IEEE80211A) return 0; - if (bw == BW40PLUS && - (!(flag & HOSTAPD_CHAN_HT40PLUS) || - !has_channel(wpa_s->global, mode, channel + 4, NULL))) - return 0; - return 1; + + for (i = 0; i < ARRAY_SIZE(center_channels); i++) + /* + * In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48), + * so the center channel is 6 channels away from the start/end. + */ + if (channel >= center_channels[i] - 6 && + channel <= center_channels[i] + 6) + return center_channels[i]; + + return 0; +} + + +static enum chan_allowed wpas_p2p_verify_80mhz(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, + u8 channel, u8 bw) +{ + u8 center_chan; + int i, flags; + enum chan_allowed res, ret = ALLOWED; + + center_chan = wpas_p2p_get_center_80mhz(wpa_s, mode, channel); + if (!center_chan) + return NOT_ALLOWED; + if (center_chan >= 58 && center_chan <= 138) + return NOT_ALLOWED; /* Do not allow DFS channels for P2P */ + + /* check all the channels are available */ + for (i = 0; i < 4; i++) { + int adj_chan = center_chan - 6 + i * 4; + + res = has_channel(wpa_s->global, mode, adj_chan, &flags); + if (res == NOT_ALLOWED) + return NOT_ALLOWED; + if (res == NO_IR) + ret = NO_IR; + + if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) + return NOT_ALLOWED; + if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) + return NOT_ALLOWED; + if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) + return NOT_ALLOWED; + if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10)) + return NOT_ALLOWED; + } + + return ret; +} + + +static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, + u8 channel, u8 bw) +{ + int flag = 0; + enum chan_allowed res, res2; + + res2 = res = has_channel(wpa_s->global, mode, channel, &flag); + if (bw == BW40MINUS) { + if (!(flag & HOSTAPD_CHAN_HT40MINUS)) + return NOT_ALLOWED; + res2 = has_channel(wpa_s->global, mode, channel - 4, NULL); + } else if (bw == BW40PLUS) { + if (!(flag & HOSTAPD_CHAN_HT40PLUS)) + return NOT_ALLOWED; + res2 = has_channel(wpa_s->global, mode, channel + 4, NULL); + } else if (bw == BW80) { + res2 = wpas_p2p_verify_80mhz(wpa_s, mode, channel, bw); + } + + if (res == NOT_ALLOWED || res2 == NOT_ALLOWED) + return NOT_ALLOWED; + if (res == NO_IR || res2 == NO_IR) + return NO_IR; + return res; } static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, - struct p2p_channels *chan) + struct p2p_channels *chan, + struct p2p_channels *cli_chan) { struct hostapd_hw_modes *mode; - int cla, op; + int cla, op, cli_cla; 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); + return wpas_p2p_default_channels(wpa_s, chan, cli_chan); } - cla = 0; + cla = cli_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; + struct p2p_reg_class *reg = NULL, *cli_reg = NULL; mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode); if (mode == NULL) continue; + if (mode->mode == HOSTAPD_MODE_IEEE80211G) + wpa_s->global->p2p_24ghz_social_channels = 1; for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) { - if (wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw) < 1) - 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; + enum chan_allowed res; + res = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw); + if (res == ALLOWED) { + 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++; + } else if (res == NO_IR && + wpa_s->conf->p2p_add_cli_chan) { + if (cli_reg == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Add operating class %u (client only)", + o->op_class); + cli_reg = &cli_chan->reg_class[cli_cla]; + cli_cla++; + cli_reg->reg_class = o->op_class; + } + cli_reg->channel[cli_reg->channels] = ch; + cli_reg->channels++; } - reg->channel[reg->channels] = ch; - reg->channels++; } if (reg) { wpa_hexdump(MSG_DEBUG, "P2P: Channels", reg->channel, reg->channels); } + if (cli_reg) { + wpa_hexdump(MSG_DEBUG, "P2P: Channels (client only)", + cli_reg->channel, cli_reg->channels); + } } chan->reg_classes = cla; + cli_chan->reg_classes = cli_cla; return 0; } @@ -2741,7 +4508,8 @@ static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s, int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, u8 channel) { - int op, ret; + int op; + enum chan_allowed ret; for (op = 0; op_class[op].op_class; op++) { struct p2p_oper_class_map *o = &op_class[op]; @@ -2752,18 +4520,24 @@ int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, o->bw == BW20 || ch != channel) continue; ret = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw); - if (ret < 0) - continue; - else if (ret > 0) + if (ret == ALLOWED) return (o->bw == BW40MINUS) ? -1 : 1; - else - return 0; } } return 0; } +int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, u8 channel) +{ + if (!wpas_p2p_verify_channel(wpa_s, mode, channel, BW80)) + return 0; + + return wpas_p2p_get_center_80mhz(wpa_s, mode, channel); +} + + static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf, size_t buf_len) { @@ -2780,10 +4554,31 @@ static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf, } -static int wpas_go_connected(void *ctx, const u8 *dev_addr) +struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s, + const u8 *ssid, size_t ssid_len) { - struct wpa_supplicant *wpa_s = ctx; + for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { + struct wpa_ssid *s = wpa_s->current_ssid; + if (s == NULL) + continue; + if (s->mode != WPAS_MODE_P2P_GO && + s->mode != WPAS_MODE_AP && + s->mode != WPAS_MODE_P2P_GROUP_FORMATION) + continue; + if (s->ssid_len != ssid_len || + os_memcmp(ssid, s->ssid, ssid_len) != 0) + continue; + return wpa_s; + } + + return NULL; + +} + +struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s, + const u8 *peer_dev_addr) +{ 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) @@ -2793,14 +4588,486 @@ static int wpas_go_connected(void *ctx, const u8 *dev_addr) 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) + if (os_memcmp(wpa_s->go_dev_addr, peer_dev_addr, ETH_ALEN) == 0) + return wpa_s; + } + + return NULL; +} + + +static int wpas_go_connected(void *ctx, const u8 *dev_addr) +{ + struct wpa_supplicant *wpa_s = ctx; + + return wpas_get_p2p_client_iface(wpa_s, dev_addr) != NULL; +} + + +static int wpas_is_concurrent_session_active(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_supplicant *ifs; + + for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { + if (ifs == wpa_s) + continue; + if (ifs->wpa_state > WPA_ASSOCIATED) return 1; } + return 0; +} + + +static void wpas_p2p_debug_print(void *ctx, int level, const char *msg) +{ + struct wpa_supplicant *wpa_s = ctx; + wpa_msg_global(wpa_s, level, "P2P: %s", msg); +} + +int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s, + const char *conf_p2p_dev) +{ + struct wpa_interface iface; + struct wpa_supplicant *p2pdev_wpa_s; + char ifname[100]; + char force_name[100]; + int ret; + + ret = os_snprintf(ifname, sizeof(ifname), P2P_MGMT_DEVICE_PREFIX "%s", + wpa_s->ifname); + if (os_snprintf_error(sizeof(ifname), ret)) + return -1; + force_name[0] = '\0'; + wpa_s->pending_interface_type = WPA_IF_P2P_DEVICE; + ret = wpa_drv_if_add(wpa_s, WPA_IF_P2P_DEVICE, ifname, NULL, NULL, + force_name, wpa_s->pending_interface_addr, NULL); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "P2P: Failed to create P2P Device interface"); + return ret; + } + os_strlcpy(wpa_s->pending_interface_name, ifname, + sizeof(wpa_s->pending_interface_name)); + + os_memset(&iface, 0, sizeof(iface)); + iface.p2p_mgmt = 1; + iface.ifname = wpa_s->pending_interface_name; + iface.driver = wpa_s->driver->name; + iface.driver_param = wpa_s->conf->driver_param; + + /* + * If a P2P Device configuration file was given, use it as the interface + * configuration file (instead of using parent's configuration file. + */ + if (conf_p2p_dev) { + iface.confname = conf_p2p_dev; + iface.ctrl_interface = NULL; + } else { + iface.confname = wpa_s->confname; + iface.ctrl_interface = wpa_s->conf->ctrl_interface; + } + iface.conf_p2p_dev = NULL; + + p2pdev_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s); + if (!p2pdev_wpa_s) { + wpa_printf(MSG_DEBUG, "P2P: Failed to add P2P Device interface"); + return -1; + } + wpa_s->p2p_dev = p2pdev_wpa_s; + + wpa_s->pending_interface_name[0] = '\0'; return 0; } +static void wpas_presence_resp(void *ctx, const u8 *src, u8 status, + const u8 *noa, size_t noa_len) +{ + struct wpa_supplicant *wpa_s, *intf = ctx; + char hex[100]; + + for (wpa_s = intf->global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (wpa_s->waiting_presence_resp) + break; + } + if (!wpa_s) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No group interface was waiting for presence response"); + return; + } + wpa_s->waiting_presence_resp = 0; + + wpa_snprintf_hex(hex, sizeof(hex), noa, noa_len); + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PRESENCE_RESPONSE "src=" MACSTR + " status=%u noa=%s", MAC2STR(src), status, hex); +} + + +static int wpas_get_persistent_group(void *ctx, const u8 *addr, const u8 *ssid, + size_t ssid_len, u8 *go_dev_addr, + u8 *ret_ssid, size_t *ret_ssid_len) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_ssid *s; + + s = wpas_p2p_get_persistent(wpa_s, addr, ssid, ssid_len); + if (s) { + os_memcpy(ret_ssid, s->ssid, s->ssid_len); + *ret_ssid_len = s->ssid_len; + os_memcpy(go_dev_addr, s->bssid, ETH_ALEN); + return 1; + } + + return 0; +} + + +static int wpas_get_go_info(void *ctx, u8 *intended_addr, + u8 *ssid, size_t *ssid_len, int *group_iface) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_ssid *s; + u8 bssid[ETH_ALEN]; + + s = wpas_p2p_group_go_ssid(wpa_s, bssid); + if (!s) { + s = wpas_p2p_get_persistent_go(wpa_s); + if (s) + os_memcpy(bssid, s->bssid, ETH_ALEN); + } + + *group_iface = wpas_p2p_create_iface(wpa_s); + if (!s) + return 0; + + os_memcpy(intended_addr, bssid, ETH_ALEN); + os_memcpy(ssid, s->ssid, s->ssid_len); + *ssid_len = s->ssid_len; + + return 1; +} + + +static int wpas_remove_stale_groups(void *ctx, const u8 *peer, const u8 *go, + const u8 *ssid, size_t ssid_len) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_ssid *s; + int save_config = 0; + size_t i; + + /* Start with our first choice of Persistent Groups */ + while ((s = wpas_p2p_get_persistent(wpa_s, peer, NULL, 0))) { + if (go && ssid && ssid_len && + s->ssid_len == ssid_len && + os_memcmp(go, s->bssid, ETH_ALEN) == 0 && + os_memcmp(ssid, s->ssid, ssid_len) == 0) + break; + + /* Remove stale persistent group */ + if (s->mode != WPAS_MODE_P2P_GO || s->num_p2p_clients <= 1) { + wpa_config_remove_network(wpa_s->conf, s->id); + save_config = 1; + continue; + } + + for (i = 0; i < s->num_p2p_clients; i++) { + if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN, + peer, ETH_ALEN) != 0) + continue; + + os_memmove(s->p2p_client_list + i * 2 * ETH_ALEN, + s->p2p_client_list + (i + 1) * 2 * ETH_ALEN, + (s->num_p2p_clients - i - 1) * 2 * ETH_ALEN); + break; + } + s->num_p2p_clients--; + save_config = 1; + } + + if (save_config) + p2p_config_write(wpa_s); + + /* Return TRUE if valid SSID remains */ + return s != NULL; +} + + +static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev, + const u8 *adv_mac, const u8 *ses_mac, + const u8 *grp_mac, u32 adv_id, u32 ses_id, + u8 conncap, int passwd_id, + const u8 *persist_ssid, + size_t persist_ssid_size, int response_done, + int prov_start, const char *session_info) +{ + struct wpa_supplicant *wpa_s = ctx; + u8 mac[ETH_ALEN]; + struct wpa_ssid *persistent_go, *stale, *s; + int save_config = 0; + struct wpa_supplicant *go_wpa_s; + + if (!dev) + return; + + os_memset(mac, 0, ETH_ALEN); + if (!adv_mac) + adv_mac = mac; + if (!ses_mac) + ses_mac = mac; + if (!grp_mac) + grp_mac = mac; + + if (prov_start) { + if (session_info == NULL) { + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_P2PS_PROVISION_START MACSTR + " adv_id=%x conncap=%x" + " adv_mac=" MACSTR + " session=%x mac=" MACSTR + " dev_passwd_id=%d", + MAC2STR(dev), adv_id, conncap, + MAC2STR(adv_mac), + ses_id, MAC2STR(ses_mac), + passwd_id); + } else { + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_P2PS_PROVISION_START MACSTR + " adv_id=%x conncap=%x" + " adv_mac=" MACSTR + " session=%x mac=" MACSTR + " dev_passwd_id=%d info='%s'", + MAC2STR(dev), adv_id, conncap, + MAC2STR(adv_mac), + ses_id, MAC2STR(ses_mac), + passwd_id, session_info); + } + return; + } + + go_wpa_s = wpas_p2p_get_go_group(wpa_s); + persistent_go = wpas_p2p_get_persistent_go(wpa_s); + + if (status && status != P2P_SC_SUCCESS_DEFERRED) { + if (go_wpa_s && !p2p_group_go_member_count(wpa_s)) + wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname); + + if (persistent_go && !persistent_go->num_p2p_clients) { + /* remove empty persistent GO */ + wpa_config_remove_network(wpa_s->conf, + persistent_go->id); + } + + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_P2PS_PROVISION_DONE MACSTR + " status=%d" + " adv_id=%x adv_mac=" MACSTR + " session=%x mac=" MACSTR, + MAC2STR(dev), status, + adv_id, MAC2STR(adv_mac), + ses_id, MAC2STR(ses_mac)); + return; + } + + /* Clean up stale persistent groups with this device */ + s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid, + persist_ssid_size); + for (;;) { + stale = wpas_p2p_get_persistent(wpa_s, dev, NULL, 0); + if (!stale) + break; + + if (s && s->ssid_len == stale->ssid_len && + os_memcmp(stale->bssid, s->bssid, ETH_ALEN) == 0 && + os_memcmp(stale->ssid, s->ssid, s->ssid_len) == 0) + break; + + /* Remove stale persistent group */ + if (stale->mode != WPAS_MODE_P2P_GO || + stale->num_p2p_clients <= 1) { + wpa_config_remove_network(wpa_s->conf, stale->id); + } else { + size_t i; + + for (i = 0; i < stale->num_p2p_clients; i++) { + if (os_memcmp(stale->p2p_client_list + + i * ETH_ALEN, + dev, ETH_ALEN) == 0) { + os_memmove(stale->p2p_client_list + + i * ETH_ALEN, + stale->p2p_client_list + + (i + 1) * ETH_ALEN, + (stale->num_p2p_clients - + i - 1) * ETH_ALEN); + break; + } + } + stale->num_p2p_clients--; + } + save_config = 1; + } + + if (save_config) + p2p_config_write(wpa_s); + + if (s) { + if (go_wpa_s && !p2p_group_go_member_count(wpa_s)) + wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname); + + if (persistent_go && s != persistent_go && + !persistent_go->num_p2p_clients) { + /* remove empty persistent GO */ + wpa_config_remove_network(wpa_s->conf, + persistent_go->id); + /* Save config */ + } + + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_P2PS_PROVISION_DONE MACSTR + " status=%d" + " adv_id=%x adv_mac=" MACSTR + " session=%x mac=" MACSTR + " persist=%d", + MAC2STR(dev), status, + adv_id, MAC2STR(adv_mac), + ses_id, MAC2STR(ses_mac), s->id); + return; + } + + if (conncap == P2PS_SETUP_GROUP_OWNER) { + const char *go_ifname = NULL; + if (!go_wpa_s) { + wpa_s->global->pending_p2ps_group = 1; + + if (wpa_s->conf->p2p_no_group_iface) + go_ifname = wpa_s->ifname; + else if (wpa_s->pending_interface_name[0]) + go_ifname = wpa_s->pending_interface_name; + + if (!go_ifname) { + wpas_p2ps_prov_complete( + wpa_s, P2P_SC_FAIL_UNKNOWN_GROUP, + dev, adv_mac, ses_mac, + NULL, adv_id, ses_id, 0, 0, + NULL, 0, 0, 0, NULL); + return; + } + + /* If PD Resp complete, start up the GO */ + if (response_done && persistent_go) { + wpas_p2p_group_add_persistent( + wpa_s, persistent_go, + 0, 0, 0, 0, 0, NULL, + persistent_go->mode == + WPAS_MODE_P2P_GO ? + P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : + 0); + } else if (response_done) { + wpas_p2p_group_add(wpa_s, 1, 0, 0, 0); + } + + if (passwd_id == DEV_PW_P2PS_DEFAULT) { + os_memcpy(wpa_s->p2ps_join_addr, dev, ETH_ALEN); + wpa_s->p2ps_join_addr_valid = 1; + wpa_dbg(wpa_s, MSG_DEBUG, + "P2PS: Saving PIN for " MACSTR, + MAC2STR(dev)); + } + } else if (passwd_id == DEV_PW_P2PS_DEFAULT) { + go_ifname = go_wpa_s->ifname; + + wpa_dbg(go_wpa_s, MSG_DEBUG, + "P2P: Setting PIN-1 For " MACSTR, MAC2STR(dev)); + wpa_supplicant_ap_wps_pin(go_wpa_s, dev, "12345670", + NULL, 0, 0); + + os_memcpy(wpa_s->p2ps_join_addr, dev, ETH_ALEN); + wpa_s->p2ps_join_addr_valid = 1; + wpa_dbg(wpa_s, MSG_DEBUG, + "P2PS: Saving PIN for " MACSTR, MAC2STR(dev)); + } + + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_P2PS_PROVISION_DONE MACSTR + " status=%d conncap=%x" + " adv_id=%x adv_mac=" MACSTR + " session=%x mac=" MACSTR + " dev_passwd_id=%d go=%s", + MAC2STR(dev), status, conncap, + adv_id, MAC2STR(adv_mac), + ses_id, MAC2STR(ses_mac), + passwd_id, go_ifname); + return; + } + + if (go_wpa_s && !p2p_group_go_member_count(wpa_s)) + wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname); + + if (persistent_go && !persistent_go->num_p2p_clients) { + /* remove empty persistent GO */ + wpa_config_remove_network(wpa_s->conf, persistent_go->id); + } + + if (conncap == P2PS_SETUP_CLIENT) { + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_P2PS_PROVISION_DONE MACSTR + " status=%d conncap=%x" + " adv_id=%x adv_mac=" MACSTR + " session=%x mac=" MACSTR + " dev_passwd_id=%d join=" MACSTR, + MAC2STR(dev), status, conncap, + adv_id, MAC2STR(adv_mac), + ses_id, MAC2STR(ses_mac), + passwd_id, MAC2STR(grp_mac)); + } else { + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_P2PS_PROVISION_DONE MACSTR + " status=%d conncap=%x" + " adv_id=%x adv_mac=" MACSTR + " session=%x mac=" MACSTR + " dev_passwd_id=%d", + MAC2STR(dev), status, conncap, + adv_id, MAC2STR(adv_mac), + ses_id, MAC2STR(ses_mac), + passwd_id); + } +} + + +static int _wpas_p2p_in_progress(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + return wpas_p2p_in_progress(wpa_s); +} + + +static int wpas_prov_disc_resp_cb(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + struct wpa_ssid *persistent_go; + + if (!wpa_s->global->pending_p2ps_group) + return 0; + + wpa_s->global->pending_p2ps_group = 0; + + if (wpas_p2p_get_go_group(wpa_s)) + return 0; + persistent_go = wpas_p2p_get_persistent_go(wpa_s); + + if (persistent_go) { + wpas_p2p_group_add_persistent( + wpa_s, persistent_go, 0, 0, 0, 0, 0, NULL, + persistent_go->mode == WPAS_MODE_P2P_GO ? + P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0); + } else { + wpas_p2p_group_add(wpa_s, 1, 0, 0, 0); + } + + return 1; +} + + /** * wpas_p2p_init - Initialize P2P module for %wpa_supplicant * @global: Pointer to global data from wpa_supplicant_init() @@ -2810,37 +5077,20 @@ static int wpas_go_connected(void *ctx, const u8 *dev_addr) 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)) + if (wpa_s->conf->p2p_disabled) return 0; - if (global->p2p) + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE)) 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; - + if (global->p2p) return 0; - } os_memset(&p2p, 0, sizeof(p2p)); - p2p.msg_ctx = wpa_s; p2p.cb_ctx = wpa_s; + p2p.debug_print = wpas_p2p_debug_print; p2p.p2p_scan = wpas_p2p_scan; p2p.send_action = wpas_send_action; p2p.send_action_done = wpas_send_action_done; @@ -2848,6 +5098,7 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) p2p.go_neg_req_rx = wpas_go_neg_req_rx; p2p.dev_found = wpas_dev_found; p2p.dev_lost = wpas_dev_lost; + p2p.find_stopped = wpas_find_stopped; p2p.start_listen = wpas_start_listen; p2p.stop_listen = wpas_stop_listen; p2p.send_probe_resp = wpas_send_probe_resp; @@ -2861,6 +5112,15 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) p2p.invitation_result = wpas_invitation_result; p2p.get_noa = wpas_get_noa; p2p.go_connected = wpas_go_connected; + p2p.presence_resp = wpas_presence_resp; + p2p.is_concurrent_session_active = wpas_is_concurrent_session_active; + p2p.is_p2p_in_progress = _wpas_p2p_in_progress; + p2p.get_persistent_group = wpas_get_persistent_group; + p2p.get_go_info = wpas_get_go_info; + p2p.remove_stale_groups = wpas_remove_stale_groups; + p2p.p2ps_prov_complete = wpas_p2ps_prov_complete; + p2p.prov_disc_resp_cb = wpas_prov_disc_resp_cb; + p2p.p2ps_group_capability = p2ps_group_capability; 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); @@ -2874,20 +5134,32 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) p2p.config_methods = wpa_s->wps->config_methods; } + if (wpas_p2p_setup_channels(wpa_s, &p2p.channels, &p2p.cli_channels)) { + wpa_printf(MSG_ERROR, + "P2P: Failed to configure supported channel list"); + return -1; + } + 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; + p2p.channel_forced = 1; } 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; + if (p2p_config_get_random_social(&p2p, &p2p.reg_class, + &p2p.channel) != 0) { + wpa_printf(MSG_ERROR, + "P2P: Failed to select random social channel as listen channel"); + return -1; + } + p2p.channel_forced = 0; } - wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d", p2p.channel); + wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d:%d", + p2p.reg_class, p2p.channel); if (wpa_s->conf->p2p_oper_reg_class && wpa_s->conf->p2p_oper_channel) { @@ -2898,29 +5170,33 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) "%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. + * Use random operation channel from 2.4 GHz band social + * channels (1, 6, 11) or band 60 GHz social channel (2) if no + * other preference is indicated. */ - os_get_random((u8 *) &r, sizeof(r)); - p2p.op_channel = 1 + (r % 3) * 5; + if (p2p_config_get_random_social(&p2p, &p2p.op_reg_class, + &p2p.op_channel) != 0) { + wpa_printf(MSG_ERROR, + "P2P: Failed to select random social channel as operation channel"); + return -1; + } 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->p2p_pref_chan && wpa_s->conf->num_p2p_pref_chan) { + p2p.pref_chan = wpa_s->conf->p2p_pref_chan; + p2p.num_pref_chan = wpa_s->conf->num_p2p_pref_chan; + } + 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); @@ -2946,6 +5222,12 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) p2p.max_listen = wpa_s->max_remain_on_chan; + if (wpa_s->conf->p2p_passphrase_len >= 8 && + wpa_s->conf->p2p_passphrase_len <= 63) + p2p.passphrase_len = wpa_s->conf->p2p_passphrase_len; + else + p2p.passphrase_len = 8; + global->p2p = p2p_init(&p2p); if (global->p2p == NULL) return -1; @@ -2958,6 +5240,8 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) global->p2p, wpa_s->conf->wps_vendor_ext[i]); } + p2p_set_no_go_freq(global->p2p, &wpa_s->conf->p2p_no_go_freq); + return 0; } @@ -2982,12 +5266,28 @@ void wpas_p2p_deinit(struct wpa_supplicant *wpa_s) os_free(wpa_s->go_params); wpa_s->go_params = NULL; + eloop_cancel_timeout(wpas_p2p_psk_failure_removal, wpa_s, NULL); eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_p2p_join_scan, 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); + eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL); + wpas_p2p_listen_work_done(wpa_s); + if (wpa_s->p2p_send_action_work) { + os_free(wpa_s->p2p_send_action_work->ctx); + radio_work_done(wpa_s->p2p_send_action_work); + wpa_s->p2p_send_action_work = NULL; + } + eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, wpa_s, NULL); + + wpabuf_free(wpa_s->p2p_oob_dev_pw); + wpa_s->p2p_oob_dev_pw = NULL; + + os_free(wpa_s->p2p_group_common_freqs); + wpa_s->p2p_group_common_freqs = NULL; + wpa_s->p2p_group_common_freqs_num = 0; /* TODO: remove group interface from the driver if this wpa_s instance * is on top of a P2P group interface */ @@ -3000,16 +5300,13 @@ void wpas_p2p_deinit(struct wpa_supplicant *wpa_s) * * This function deinitializes the global (per device) P2P module. */ -void wpas_p2p_deinit_global(struct wpa_global *global) +static void wpas_p2p_deinit_global(struct wpa_global *global) { struct wpa_supplicant *wpa_s, *tmp; wpa_s = global->ifaces; - if (wpa_s) - wpas_p2p_service_flush(wpa_s); - if (global->p2p == NULL) - return; + wpas_p2p_service_flush(global->p2p_init_wpa_s); /* Remove remaining P2P group interfaces */ while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) @@ -3044,7 +5341,8 @@ void wpas_p2p_deinit_global(struct wpa_global *global) static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s) { - if (wpa_s->conf->p2p_no_group_iface) + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && + wpa_s->conf->p2p_no_group_iface) return 0; /* separate interface disabled per configuration */ if (wpa_s->drv_flags & (WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE | @@ -3071,12 +5369,6 @@ static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s, 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); - } - /* * Increase GO config timeout if HT40 is used since it takes some time * to scan channels for coex purposes before the BSS can be started. @@ -3088,7 +5380,9 @@ static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s, go_intent, own_interface_addr, force_freq, persistent_group, ssid ? ssid->ssid : NULL, ssid ? ssid->ssid_len : 0, - wpa_s->p2p_pd_before_go_neg, pref_freq); + wpa_s->p2p_pd_before_go_neg, pref_freq, + wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id : + 0); } @@ -3102,13 +5396,12 @@ static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s, 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, ssid ? ssid->ssid : NULL, - ssid ? ssid->ssid_len : 0, pref_freq); + ssid ? ssid->ssid_len : 0, pref_freq, + wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id : + 0); } @@ -3124,56 +5417,52 @@ static void wpas_p2p_check_join_scan_limit(struct wpa_supplicant *wpa_s) eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL); if (wpa_s->p2p_auto_pd) { wpa_s->p2p_auto_pd = 0; - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE - " p2p_dev_addr=" MACSTR " status=N/A", - MAC2STR(wpa_s->pending_join_dev_addr)); + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_PROV_DISC_FAILURE + " p2p_dev_addr=" MACSTR " status=N/A", + MAC2STR(wpa_s->pending_join_dev_addr)); return; } - wpa_msg(wpa_s->parent, MSG_INFO, - P2P_EVENT_GROUP_FORMATION_FAILURE); + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_FORMATION_FAILURE); } } static int wpas_check_freq_conflict(struct wpa_supplicant *wpa_s, int freq) { - struct wpa_supplicant *iface; - int shared_freq; - u8 bssid[ETH_ALEN]; + int res; + unsigned int num, i; + struct wpa_used_freq_data *freqs; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT) + if (wpas_p2p_num_unused_channels(wpa_s) > 0) { + /* Multiple channels are supported and not all are in use */ return 0; + } - for (iface = wpa_s->global->ifaces; iface; iface = iface->next) { - if (!wpas_p2p_create_iface(wpa_s) && iface == wpa_s) - continue; - if (iface->current_ssid == NULL || iface->assoc_freq == 0) - continue; - if (iface->current_ssid->mode == WPAS_MODE_AP || - iface->current_ssid->mode == WPAS_MODE_P2P_GO) - shared_freq = iface->current_ssid->frequency; - else if (wpa_drv_get_bssid(iface, bssid) == 0) - shared_freq = iface->assoc_freq; - else - shared_freq = 0; + freqs = os_calloc(wpa_s->num_multichan_concurrent, + sizeof(struct wpa_used_freq_data)); + if (!freqs) + return 1; - if (shared_freq && freq != shared_freq) { - wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - %s " - "connected on %d MHz - new connection on " - "%d MHz", iface->ifname, shared_freq, freq); - return 1; + num = wpas_p2p_valid_oper_freqs(wpa_s, freqs, + wpa_s->num_multichan_concurrent); + + for (i = 0; i < num; i++) { + if (freqs[i].freq == freq) { + wpa_printf(MSG_DEBUG, "P2P: Frequency %d MHz in use by another virtual interface and can be used", + freq); + res = 0; + goto exit_free; } } - shared_freq = wpa_drv_shared_freq(wpa_s); - if (shared_freq > 0 && shared_freq != freq) { - wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - shared " - "virtual interface connected on %d MHz - new " - "connection on %d MHz", shared_freq, freq); - return 1; - } + wpa_printf(MSG_DEBUG, "P2P: No valid operating frequencies"); + res = 1; - return 0; +exit_free: + os_free(freqs); + return res; } @@ -3192,7 +5481,8 @@ static int wpas_p2p_peer_go(struct wpa_supplicant *wpa_s, return 0; } - updated = os_time_before(&wpa_s->p2p_auto_started, &bss->last_update); + updated = os_reltime_before(&wpa_s->p2p_auto_started, + &bss->last_update); wpa_printf(MSG_DEBUG, "P2P: Current BSS entry for peer updated at " "%ld.%06ld (%supdated in last scan)", bss->last_update.sec, bss->last_update.usec, @@ -3205,7 +5495,7 @@ static int wpas_p2p_peer_go(struct wpa_supplicant *wpa_s, static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res) { - struct wpa_bss *bss; + struct wpa_bss *bss = NULL; int freq; u8 iface_addr[ETH_ALEN]; @@ -3227,8 +5517,8 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, if (join == 0 && wpa_s->auto_pd_scan_retry < P2P_AUTO_PD_SCAN_ATTEMPTS) { wpa_s->auto_pd_scan_retry++; - bss = wpa_bss_get_bssid(wpa_s, - wpa_s->pending_join_dev_addr); + bss = wpa_bss_get_bssid_latest( + wpa_s, wpa_s->pending_join_dev_addr); if (bss) { freq = bss->freq; wpa_printf(MSG_DEBUG, "P2P: Scan retry %d for " @@ -3237,7 +5527,7 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, MAC2STR(wpa_s-> pending_join_dev_addr), freq); - wpas_p2p_join_scan_req(wpa_s, freq); + wpas_p2p_join_scan_req(wpa_s, freq, NULL, 0); return; } } @@ -3250,13 +5540,14 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "P2P: Auto PD with " MACSTR " join=%d", MAC2STR(wpa_s->pending_join_dev_addr), join); if (p2p_prov_disc_req(wpa_s->global->p2p, - wpa_s->pending_join_dev_addr, + wpa_s->pending_join_dev_addr, NULL, wpa_s->pending_pd_config_methods, join, 0, wpa_s->user_initiated_pd) < 0) { wpa_s->p2p_auto_pd = 0; - wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE - " p2p_dev_addr=" MACSTR " status=N/A", - MAC2STR(wpa_s->pending_join_dev_addr)); + wpa_msg_global(wpa_s, MSG_INFO, + P2P_EVENT_PROV_DISC_FAILURE + " p2p_dev_addr=" MACSTR " status=N/A", + MAC2STR(wpa_s->pending_join_dev_addr)); } return; } @@ -3267,6 +5558,9 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, if (join < 0) { wpa_printf(MSG_DEBUG, "P2P: Peer was not found to be " "running a GO -> use GO Negotiation"); + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_FALLBACK_TO_GO_NEG + "reason=peer-not-running-GO"); wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin, wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0, 0, 0, @@ -3274,15 +5568,19 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, wpa_s->p2p_connect_freq, wpa_s->p2p_persistent_id, wpa_s->p2p_pd_before_go_neg, - wpa_s->p2p_go_ht40); + wpa_s->p2p_go_ht40, + wpa_s->p2p_go_vht); return; } wpa_printf(MSG_DEBUG, "P2P: Peer was found running GO%s -> " "try to join the group", join ? "" : " in older scan"); - if (!join) + if (!join) { + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_FALLBACK_TO_GO_NEG_ENABLED); wpa_s->p2p_fallback_to_go_neg = 1; + } } freq = p2p_get_oper_freq(wpa_s->global->p2p, @@ -3291,8 +5589,8 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, 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) - { + os_memcmp(iface_addr, wpa_s->pending_join_dev_addr, ETH_ALEN) != 0 + && !wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr)) { wpa_printf(MSG_DEBUG, "P2P: Overwrite pending interface " "address for join from " MACSTR " to " MACSTR " based on newly discovered P2P peer entry", @@ -3308,19 +5606,35 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, 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 (wpa_s->p2p_join_ssid_len) { + wpa_printf(MSG_DEBUG, "P2P: Trying to find target GO BSS entry based on BSSID " + MACSTR " and SSID %s", + MAC2STR(wpa_s->pending_join_iface_addr), + wpa_ssid_txt(wpa_s->p2p_join_ssid, + wpa_s->p2p_join_ssid_len)); + bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr, + wpa_s->p2p_join_ssid, + wpa_s->p2p_join_ssid_len); + } + if (!bss) { + wpa_printf(MSG_DEBUG, "P2P: Trying to find target GO BSS entry based on BSSID " + MACSTR, MAC2STR(wpa_s->pending_join_iface_addr)); + bss = wpa_bss_get_bssid_latest(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); + "from BSS table: %d MHz (SSID %s)", freq, + wpa_ssid_txt(bss->ssid, bss->ssid_len)); } if (freq > 0) { u16 method; if (wpas_check_freq_conflict(wpa_s, freq) > 0) { - wpa_msg(wpa_s->parent, MSG_INFO, - P2P_EVENT_GROUP_FORMATION_FAILURE - "reason=FREQ_CONFLICT"); + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_GROUP_FORMATION_FAILURE + "reason=FREQ_CONFLICT"); return; } @@ -3361,7 +5675,8 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, } if (p2p_prov_disc_req(wpa_s->global->p2p, - wpa_s->pending_join_dev_addr, method, 1, + wpa_s->pending_join_dev_addr, + NULL, method, 1, freq, wpa_s->user_initiated_pd) < 0) { wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision " "Discovery Request before joining an " @@ -3380,11 +5695,12 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s, start: /* Start join operation immediately */ - wpas_p2p_join_start(wpa_s); + wpas_p2p_join_start(wpa_s, 0, NULL, 0); } -static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq) +static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, + const u8 *ssid, size_t ssid_len) { int ret; struct wpa_driver_scan_params params; @@ -3396,8 +5712,16 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq) /* 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 (ssid && ssid_len) { + params.ssids[0].ssid = ssid; + params.ssids[0].ssid_len = ssid_len; + os_memcpy(wpa_s->p2p_join_ssid, ssid, ssid_len); + wpa_s->p2p_join_ssid_len = ssid_len; + } else { + params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID; + params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN; + wpa_s->p2p_join_ssid_len = 0; + } wpa_s->wps->dev.p2p = 1; wps_ie = wps_build_probe_req_ie(DEV_PW_DEFAULT, &wpa_s->wps->dev, @@ -3423,6 +5747,18 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq) params.p2p_probe = 1; params.extra_ies = wpabuf_head(ies); params.extra_ies_len = wpabuf_len(ies); + + if (!freq) { + int oper_freq; + /* + * If freq is not provided, check the operating freq of the GO + * and use a single channel scan on if possible. + */ + oper_freq = p2p_get_oper_freq(wpa_s->global->p2p, + wpa_s->pending_join_iface_addr); + if (oper_freq > 0) + freq = oper_freq; + } if (freq > 0) { freqs[0] = freq; params.freqs = freqs; @@ -3433,8 +5769,11 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq) * the new scan results become available. */ ret = wpa_drv_scan(wpa_s, ¶ms); - if (!ret) + if (!ret) { + os_get_reltime(&wpa_s->scan_trigger_time); wpa_s->scan_res_handler = wpas_p2p_scan_res_join; + wpa_s->own_scan_requested = 1; + } wpabuf_free(ies); @@ -3451,18 +5790,23 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq) static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; - wpas_p2p_join_scan_req(wpa_s, 0); + wpas_p2p_join_scan_req(wpa_s, 0, NULL, 0); } static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr, const u8 *dev_addr, enum p2p_wps_method wps_method, - int auto_join) + int auto_join, int op_freq, + const u8 *ssid, size_t ssid_len) { wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface " - MACSTR " dev " MACSTR ")%s", - MAC2STR(iface_addr), MAC2STR(dev_addr), + MACSTR " dev " MACSTR " op_freq=%d)%s", + MAC2STR(iface_addr), MAC2STR(dev_addr), op_freq, auto_join ? " (auto_join)" : ""); + if (ssid && ssid_len) { + wpa_printf(MSG_DEBUG, "P2P: Group SSID specified: %s", + wpa_ssid_txt(ssid, ssid_len)); + } wpa_s->p2p_auto_pd = 0; wpa_s->p2p_auto_join = !!auto_join; @@ -3474,12 +5818,13 @@ static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr, wpas_p2p_stop_find(wpa_s); wpa_s->p2p_join_scan_count = 0; - wpas_p2p_join_scan(wpa_s, NULL); + wpas_p2p_join_scan_req(wpa_s, op_freq, ssid, ssid_len); return 0; } -static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s) +static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq, + const u8 *ssid, size_t ssid_len) { struct wpa_supplicant *group; struct p2p_go_neg_results res; @@ -3492,21 +5837,40 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s) os_memcpy(group->p2p_pin, wpa_s->p2p_pin, sizeof(group->p2p_pin)); group->p2p_wps_method = wpa_s->p2p_wps_method; + } else { + /* + * Need to mark the current interface for p2p_group_formation + * when a separate group interface is not used. This is needed + * to allow p2p_cancel stop a pending p2p_connect-join. + * wpas_p2p_init_group_interface() addresses this for the case + * where a separate group interface is used. + */ + wpa_s->global->p2p_group_formation = wpa_s; } group->p2p_in_provisioning = 1; - wpa_s->global->p2p_group_formation = wpa_s; group->p2p_fallback_to_go_neg = wpa_s->p2p_fallback_to_go_neg; os_memset(&res, 0, sizeof(res)); + os_memcpy(res.peer_device_addr, wpa_s->pending_join_dev_addr, ETH_ALEN); os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr, ETH_ALEN); res.wps_method = wpa_s->pending_join_wps_method; - bss = wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr); - if (bss) { - res.freq = bss->freq; - res.ssid_len = bss->ssid_len; - os_memcpy(res.ssid, bss->ssid, bss->ssid_len); + if (freq && ssid && ssid_len) { + res.freq = freq; + res.ssid_len = ssid_len; + os_memcpy(res.ssid, ssid, ssid_len); + } else { + bss = wpa_bss_get_bssid_latest(wpa_s, + wpa_s->pending_join_iface_addr); + if (bss) { + res.freq = bss->freq; + res.ssid_len = bss->ssid_len; + os_memcpy(res.ssid, bss->ssid, bss->ssid_len); + wpa_printf(MSG_DEBUG, "P2P: Join target GO operating frequency from BSS table: %d MHz (SSID %s)", + bss->freq, + wpa_ssid_txt(bss->ssid, bss->ssid_len)); + } } if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) { @@ -3531,6 +5895,106 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s) } +static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq, + int *force_freq, int *pref_freq, int go) +{ + struct wpa_used_freq_data *freqs; + int res, best_freq, num_unused; + unsigned int freq_in_use = 0, num, i; + + freqs = os_calloc(wpa_s->num_multichan_concurrent, + sizeof(struct wpa_used_freq_data)); + if (!freqs) + return -1; + + num = wpas_p2p_valid_oper_freqs(wpa_s, freqs, + wpa_s->num_multichan_concurrent); + + /* + * It is possible that the total number of used frequencies is bigger + * than the number of frequencies used for P2P, so get the system wide + * number of unused frequencies. + */ + num_unused = wpas_p2p_num_unused_channels(wpa_s); + + wpa_printf(MSG_DEBUG, + "P2P: Setup freqs: freq=%d num_MCC=%d shared_freqs=%u num_unused=%d", + freq, wpa_s->num_multichan_concurrent, num, num_unused); + + if (freq > 0) { + int ret; + if (go) + ret = p2p_supported_freq(wpa_s->global->p2p, freq); + else + ret = p2p_supported_freq_cli(wpa_s->global->p2p, freq); + if (!ret) { + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) && + ieee80211_is_dfs(freq)) { + /* + * If freq is a DFS channel and DFS is offloaded + * to the driver, allow P2P GO to use it. + */ + wpa_printf(MSG_DEBUG, + "P2P: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded to the driver", + freq); + } else { + wpa_printf(MSG_DEBUG, + "P2P: The forced channel (%u MHz) is not supported for P2P uses", + freq); + res = -3; + goto exit_free; + } + } + + for (i = 0; i < num; i++) { + if (freqs[i].freq == freq) + freq_in_use = 1; + } + + if (num_unused <= 0 && !freq_in_use) { + wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group on %u MHz as there are no available channels", + freq); + res = -2; + goto exit_free; + } + wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the " + "requested channel (%u MHz)", freq); + *force_freq = freq; + goto exit_ok; + } + + best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num); + + /* We have a candidate frequency to use */ + if (best_freq > 0) { + if (*pref_freq == 0 && num_unused > 0) { + wpa_printf(MSG_DEBUG, "P2P: Try to prefer a frequency (%u MHz) we are already using", + best_freq); + *pref_freq = best_freq; + } else { + wpa_printf(MSG_DEBUG, "P2P: Try to force us to use frequency (%u MHz) which is already in use", + best_freq); + *force_freq = best_freq; + } + } else if (num_unused > 0) { + wpa_printf(MSG_DEBUG, + "P2P: Current operating channels are not available for P2P. Try to use another channel"); + *force_freq = 0; + } else { + wpa_printf(MSG_DEBUG, + "P2P: All channels are in use and none of them are P2P enabled. Cannot start P2P group"); + res = -2; + goto exit_free; + } + +exit_ok: + res = 0; +exit_free: + os_free(freqs); + return res; +} + + /** * wpas_p2p_connect - Request P2P Group Formation to be started * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface() @@ -3549,6 +6013,7 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s) * @pd: Whether to send Provision Discovery prior to GO Negotiation as an * interoperability workaround when initiating group formation * @ht40: Start GO with 40 MHz channel width + * @vht: Start GO with VHT support * 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 @@ -3557,11 +6022,10 @@ 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 auto_join, int join, int auth, int go_intent, int freq, int persistent_id, int pd, - int ht40) + int ht40, int vht) { - int force_freq = 0, pref_freq = 0, oper_freq = 0; - u8 bssid[ETH_ALEN]; - int ret = 0; + int force_freq = 0, pref_freq = 0; + int ret = 0, res; enum wpa_driver_if_type iftype; const u8 *if_addr; struct wpa_ssid *ssid = NULL; @@ -3576,6 +6040,12 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, return -1; } + os_free(wpa_s->global->add_psk); + wpa_s->global->add_psk = NULL; + + wpa_s->global->p2p_fail_on_wps_complete = 0; + wpa_s->global->pending_p2ps_group = 0; + if (go_intent < 0) go_intent = wpa_s->conf->p2p_go_intent; @@ -3590,13 +6060,16 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, wpa_s->p2p_fallback_to_go_neg = 0; wpa_s->p2p_pd_before_go_neg = !!pd; wpa_s->p2p_go_ht40 = !!ht40; + wpa_s->p2p_go_vht = !!vht; 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); + res = os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin), + "%08d", ret); + if (os_snprintf_error(sizeof(wpa_s->p2p_pin), res)) + wpa_s->p2p_pin[sizeof(wpa_s->p2p_pin) - 1] = '\0'; wpa_printf(MSG_DEBUG, "P2P: Randomly generated PIN: %s", wpa_s->p2p_pin); } else @@ -3619,7 +6092,7 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, dev_addr); } if (auto_join) { - os_get_time(&wpa_s->p2p_auto_started); + os_get_reltime(&wpa_s->p2p_auto_started); wpa_printf(MSG_DEBUG, "P2P: Auto join started at " "%ld.%06ld", wpa_s->p2p_auto_started.sec, @@ -3627,65 +6100,17 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, } wpa_s->user_initiated_pd = 1; if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method, - auto_join) < 0) + auto_join, freq, NULL, 0) < 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_s->drv_flags & - WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) { - wpa_printf(MSG_DEBUG, "P2P: Trying to prefer the channel we " - "are already using (%u MHz) on another interface", - oper_freq); - pref_freq = oper_freq; - } 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; - } + res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq, + go_intent == 15); + if (res) + return res; + wpas_p2p_set_own_freq_preference(wpa_s, + force_freq ? force_freq : pref_freq); wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s); @@ -3738,24 +6163,28 @@ void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s, { if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; - if (wpa_s->off_channel_freq == wpa_s->pending_listen_freq) { + wpa_printf(MSG_DEBUG, "P2P: remain-on-channel callback (off_channel_freq=%u pending_listen_freq=%d roc_waiting_drv_freq=%d freq=%u duration=%u)", + wpa_s->off_channel_freq, wpa_s->pending_listen_freq, + wpa_s->roc_waiting_drv_freq, freq, duration); + if (wpa_s->off_channel_freq && + 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; + } else { + wpa_printf(MSG_DEBUG, "P2P: Ignore remain-on-channel callback (off_channel_freq=%u pending_listen_freq=%d freq=%u duration=%u)", + wpa_s->off_channel_freq, wpa_s->pending_listen_freq, + freq, duration); } } -static int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s, - unsigned int timeout) +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); } @@ -3775,17 +6204,24 @@ void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel callback " "(p2p_long_listen=%d ms pending_action_tx=%p)", wpa_s->p2p_long_listen, offchannel_pending_action_tx(wpa_s)); + wpas_p2p_listen_work_done(wpa_s); if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; + if (wpa_s->p2p_long_listen > 0) + wpa_s->p2p_long_listen -= wpa_s->max_remain_on_chan; if (p2p_listen_end(wpa_s->global->p2p, freq) > 0) return; /* P2P module started a new operation */ if (offchannel_pending_action_tx(wpa_s)) 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); + } else { + /* + * When listen duration is over, stop listen & update p2p_state + * to IDLE. + */ + p2p_stop_listen(wpa_s->global->p2p); } } @@ -3806,6 +6242,7 @@ void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname) { struct wpa_global *global = wpa_s->global; + struct wpa_supplicant *calling_wpa_s = wpa_s; if (os_strcmp(ifname, "*") == 0) { struct wpa_supplicant *prev; @@ -3813,7 +6250,11 @@ int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname) while (wpa_s) { prev = wpa_s; wpa_s = wpa_s->next; - wpas_p2p_disconnect(prev); + if (prev->p2p_group_interface != + NOT_P2P_GROUP_INTERFACE || + (prev->current_ssid && + prev->current_ssid->p2p_group)) + wpas_p2p_disconnect_safely(prev, calling_wpa_s); } return 0; } @@ -3823,94 +6264,275 @@ int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname) break; } - return wpas_p2p_disconnect(wpa_s); + return wpas_p2p_disconnect_safely(wpa_s, calling_wpa_s); +} + + +static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) +{ + unsigned int r; + + 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_go(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 { + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + return -1; + 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_go(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 { + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + return -1; + freq = 5180 + (r % 4) * 20; + if (!p2p_supported_freq_go(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_go(wpa_s->global->p2p, freq)) { + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) && + ieee80211_is_dfs(freq)) { + /* + * If freq is a DFS channel and DFS is offloaded to the + * driver, allow P2P GO to use it. + */ + wpa_printf(MSG_DEBUG, "P2P: " + "%s: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded", + __func__, freq); + return freq; + } + wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO " + "(%u MHz) is not supported for P2P uses", + freq); + return -1; + } + + return freq; +} + + +static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s, + struct p2p_go_neg_results *params, + const struct p2p_channels *channels) +{ + unsigned int i, r; + + /* first try some random selection of the social channels */ + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + return -1; + + for (i = 0; i < 3; i++) { + params->freq = 2412 + ((r + i) % 3) * 25; + if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && + freq_included(channels, params->freq) && + p2p_supported_freq(wpa_s->global->p2p, params->freq)) + goto out; + } + + /* try all channels in reg. class 81 */ + for (i = 0; i < 11; i++) { + params->freq = 2412 + i * 5; + if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && + freq_included(channels, params->freq) && + p2p_supported_freq(wpa_s->global->p2p, params->freq)) + goto out; + } + + /* try all channels in operating class 115 */ + for (i = 0; i < 4; i++) { + params->freq = 5180 + i * 20; + if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && + freq_included(channels, params->freq) && + p2p_supported_freq(wpa_s->global->p2p, params->freq)) + goto out; + } + + /* try all channels in operating class 124 */ + for (i = 0; i < 4; i++) { + params->freq = 5745 + i * 20; + if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && + freq_included(channels, params->freq) && + p2p_supported_freq(wpa_s->global->p2p, params->freq)) + goto out; + } + + /* try social channel class 180 channel 2 */ + params->freq = 58320 + 1 * 2160; + if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && + freq_included(channels, params->freq) && + p2p_supported_freq(wpa_s->global->p2p, params->freq)) + goto out; + + /* try all channels in reg. class 180 */ + for (i = 0; i < 4; i++) { + params->freq = 58320 + i * 2160; + if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) && + freq_included(channels, params->freq) && + p2p_supported_freq(wpa_s->global->p2p, params->freq)) + goto out; + } + + wpa_printf(MSG_DEBUG, "P2P: No 2.4, 5, or 60 GHz channel allowed"); + return -1; +out: + wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference known)", + params->freq); + return 0; } static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *params, - int freq, int ht40) + int freq, int ht40, int vht, + const struct p2p_channels *channels) { - u8 bssid[ETH_ALEN]; - int res; + struct wpa_used_freq_data *freqs; + unsigned int pref_freq, cand_freq; + unsigned int num, i; os_memset(params, 0, sizeof(*params)); params->role_go = 1; params->ht40 = ht40; + params->vht = vht; if (freq) { + if (!freq_included(channels, freq)) { + wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not " + "accepted", freq); + return -1; + } 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) { + wpa_s->conf->p2p_oper_channel <= 11 && + freq_included(channels, + 2407 + 5 * wpa_s->conf->p2p_oper_channel)) { 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) { + } else if ((wpa_s->conf->p2p_oper_reg_class == 115 || + wpa_s->conf->p2p_oper_reg_class == 116 || + wpa_s->conf->p2p_oper_reg_class == 117 || + wpa_s->conf->p2p_oper_reg_class == 124 || + wpa_s->conf->p2p_oper_reg_class == 126 || + wpa_s->conf->p2p_oper_reg_class == 127) && + freq_included(channels, + 5000 + 5 * wpa_s->conf->p2p_oper_channel)) { 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)) { + p2p_supported_freq_go(wpa_s->global->p2p, + wpa_s->best_overall_freq) && + freq_included(channels, 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)) { + p2p_supported_freq_go(wpa_s->global->p2p, + wpa_s->best_24_freq) && + freq_included(channels, 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)) { + p2p_supported_freq_go(wpa_s->global->p2p, + wpa_s->best_5_freq) && + freq_included(channels, 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 if ((pref_freq = p2p_get_pref_freq(wpa_s->global->p2p, + channels))) { + params->freq = pref_freq; + wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred " + "channels", params->freq); } else { - int chan; - for (chan = 0; chan < 11; chan++) { - params->freq = 2412 + chan * 5; - if (!wpas_p2p_disallowed_freq(wpa_s->global, - params->freq)) - break; - } - if (chan == 11) { - wpa_printf(MSG_DEBUG, "P2P: No 2.4 GHz channel " - "allowed"); + /* no preference, select some channel */ + if (wpas_p2p_select_freq_no_pref(wpa_s, params, channels) < 0) return -1; - } - 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); + freqs = os_calloc(wpa_s->num_multichan_concurrent, + sizeof(struct wpa_used_freq_data)); + if (!freqs) return -1; + + num = wpas_p2p_valid_oper_freqs(wpa_s, freqs, + wpa_s->num_multichan_concurrent); + + cand_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num); + + /* First try the best used frequency if possible */ + if (!freq && cand_freq > 0 && freq_included(channels, cand_freq)) { + params->freq = cand_freq; + } else if (!freq) { + /* Try any of the used frequencies */ + for (i = 0; i < num; i++) { + if (freq_included(channels, freqs[i].freq)) { + wpa_printf(MSG_DEBUG, "P2P: Force GO on a channel we are already using (%u MHz)", + freqs[i].freq); + params->freq = freqs[i].freq; + break; + } + } + + if (i == num) { + if (wpas_p2p_num_unused_channels(wpa_s) <= 0) { + wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using"); + os_free(freqs); + return -1; + } else { + wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using. Use one of the free channels"); + } + } + } else { + for (i = 0; i < num; i++) { + if (freqs[i].freq == freq) + break; + } + + if (i == num) { + if (wpas_p2p_num_unused_channels(wpa_s) <= 0) { + if (freq) + wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on freq (%u MHz) as all the channels are in use", freq); + os_free(freqs); + return -1; + } else { + wpa_printf(MSG_DEBUG, "P2P: Use one of the free channels"); + } + } } + os_free(freqs); return 0; } @@ -3924,24 +6546,27 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, if (!wpas_p2p_create_iface(wpa_s)) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use same interface for group " "operations"); + wpa_s->p2p_first_connection_timeout = 0; return wpa_s; } if (wpas_p2p_add_group_interface(wpa_s, go ? WPA_IF_P2P_GO : WPA_IF_P2P_CLIENT) < 0) { - wpa_msg(wpa_s, MSG_ERROR, "P2P: Failed to add group interface"); + wpa_msg_global(wpa_s, MSG_ERROR, + "P2P: Failed to add group interface"); return NULL; } group_wpa_s = wpas_p2p_init_group_interface(wpa_s, go); if (group_wpa_s == NULL) { - wpa_msg(wpa_s, MSG_ERROR, "P2P: Failed to initialize group " - "interface"); + wpa_msg_global(wpa_s, MSG_ERROR, + "P2P: Failed to initialize group interface"); wpas_p2p_remove_pending_group_interface(wpa_s); return NULL; } wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use separate group interface %s", group_wpa_s->ifname); + group_wpa_s->p2p_first_connection_timeout = 0; return group_wpa_s; } @@ -3951,78 +6576,51 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, * @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 + * @ht40: Start GO with 40 MHz channel width + * @vht: Start GO with VHT support * 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, int ht40) + int freq, int ht40, int vht) { struct p2p_go_neg_results params; - unsigned int r; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; + os_free(wpa_s->global->add_psk); + wpa_s->global->add_psk = NULL; + /* 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_oper(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); + freq = wpas_p2p_select_go_freq(wpa_s, freq); + if (freq < 0) return -1; - } - if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40)) + if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, vht, NULL)) 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_supported_freq_go(wpa_s->global->p2p, params.freq)) { + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) && + ieee80211_is_dfs(params.freq)) { + /* + * If freq is a DFS channel and DFS is offloaded to the + * driver, allow P2P GO to use it. + */ + wpa_printf(MSG_DEBUG, + "P2P: %s: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded to driver", + __func__, params.freq); + } else { + 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; @@ -4037,13 +6635,15 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s, - struct wpa_ssid *params, int addr_allocated) + struct wpa_ssid *params, int addr_allocated, + int freq) { struct wpa_ssid *ssid; wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0); if (wpa_s == NULL) return -1; + wpa_s->p2p_last_4way_hs_fail = NULL; wpa_supplicant_ap_deinit(wpa_s); @@ -4072,9 +6672,16 @@ static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s, if (params->passphrase) ssid->passphrase = os_strdup(params->passphrase); - wpa_supplicant_select_network(wpa_s, ssid); - wpa_s->show_group_started = 1; + wpa_s->p2p_in_invitation = 1; + wpa_s->p2p_invite_go_freq = freq; + + eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent, + NULL); + eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0, + wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + wpa_supplicant_select_network(wpa_s, ssid); return 0; } @@ -4082,10 +6689,12 @@ static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s, int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int addr_allocated, - int freq, int ht40) + int force_freq, int neg_freq, int ht40, + int vht, const struct p2p_channels *channels, + int connection_timeout) { struct p2p_go_neg_results params; - int go = 0; + int go = 0, freq; if (ssid->disabled != 2 || ssid->ssid == NULL) return -1; @@ -4097,18 +6706,39 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, return 0; } + os_free(wpa_s->global->add_psk); + wpa_s->global->add_psk = NULL; + /* Make sure we are not running find during connection establishment */ wpas_p2p_stop_find_oper(wpa_s); wpa_s->p2p_fallback_to_go_neg = 0; + if (ssid->mode == WPAS_MODE_P2P_GO) { + if (force_freq > 0) { + freq = wpas_p2p_select_go_freq(wpa_s, force_freq); + if (freq < 0) + return -1; + } else { + freq = wpas_p2p_select_go_freq(wpa_s, neg_freq); + if (freq < 0 || + (freq > 0 && !freq_included(channels, freq))) + freq = 0; + } + } else { + freq = neg_freq; + if (freq < 0 || + (freq > 0 && !freq_included(channels, freq))) + freq = 0; + } + if (ssid->mode == WPAS_MODE_INFRA) - return wpas_start_p2p_client(wpa_s, ssid, addr_allocated); + return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq); if (ssid->mode != WPAS_MODE_P2P_GO) return -1; - if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40)) + if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, ht40, vht, channels)) return -1; params.role_go = 1; @@ -4132,6 +6762,9 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, if (wpa_s == NULL) return -1; + p2p_channels_to_freqs(channels, params.freq_list, P2P_MAX_CHANNELS); + + wpa_s->p2p_first_connection_timeout = connection_timeout; wpas_start_wps_go(wpa_s, ¶ms, 0); return 0; @@ -4169,9 +6802,14 @@ static void wpas_p2p_idle_update(void *ctx, int idle) if (!wpa_s->ap_iface) return; wpa_printf(MSG_DEBUG, "P2P: GO - group %sidle", idle ? "" : "not "); - if (idle) + if (idle) { + if (wpa_s->global->p2p_fail_on_wps_complete && + wpa_s->p2p_in_provisioning) { + wpas_p2p_grpform_fail_after_wps(wpa_s); + return; + } wpas_p2p_set_group_idle_timeout(wpa_s); - else + } else eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL); } @@ -4182,8 +6820,6 @@ struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, 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; @@ -4203,6 +6839,7 @@ struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, cfg->max_clients = wpa_s->conf->max_num_sta; os_memcpy(cfg->ssid, ssid->ssid, ssid->ssid_len); cfg->ssid_len = ssid->ssid_len; + cfg->freq = ssid->frequency; cfg->cb_ctx = wpa_s; cfg->ie_update = wpas_p2p_ie_update; cfg->idle_update = wpas_p2p_idle_update; @@ -4239,6 +6876,7 @@ void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent, NULL); + wpa_s->p2p_go_group_formation_completed = 1; if (ssid && ssid->mode == WPAS_MODE_INFRA) { /* * Use a separate timeout for initial data connection to @@ -4246,14 +6884,31 @@ void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, * something goes wrong in this step before the P2P group idle * timeout mechanism is taken into use. */ + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Re-start group formation timeout (%d seconds) as client for initial connection", + P2P_MAX_INITIAL_CONN_WAIT); eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0, wpas_p2p_group_formation_timeout, wpa_s->parent, NULL); + } else if (ssid) { + /* + * Use a separate timeout for initial data connection to + * complete to allow the group to be removed automatically if + * the client does not complete data connection successfully. + */ + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Re-start group formation timeout (%d seconds) as GO for initial connection", + P2P_MAX_INITIAL_CONN_WAIT_GO); + eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT_GO, 0, + wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + /* + * Complete group formation on first successful data connection + */ + wpa_s->p2p_go_group_formation_completed = 0; } 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); } @@ -4274,18 +6929,55 @@ void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s, } wpas_notify_p2p_wps_failed(wpa_s, fail); + + if (wpa_s == wpa_s->global->p2p_group_formation) { + /* + * Allow some time for the failed WPS negotiation exchange to + * complete, but remove the group since group formation cannot + * succeed after provisioning failure. + */ + wpa_printf(MSG_DEBUG, "P2P: WPS step failed during group formation - reject connection from timeout"); + wpa_s->global->p2p_fail_on_wps_complete = 1; + eloop_deplete_timeout(0, 50000, + wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL); + } +} + + +int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->global->p2p_fail_on_wps_complete || + !wpa_s->p2p_in_provisioning) + return 0; + + wpas_p2p_grpform_fail_after_wps(wpa_s); + + return 1; } int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, const char *config_method, - enum wpas_p2p_prov_disc_use use) + enum wpas_p2p_prov_disc_use use, + struct p2ps_provision *p2ps_prov) { u16 config_methods; + wpa_s->global->pending_p2ps_group = 0; wpa_s->p2p_fallback_to_go_neg = 0; wpa_s->pending_pd_use = NORMAL_PD; - if (os_strncmp(config_method, "display", 7) == 0) + if (p2ps_prov && use == WPAS_P2P_PD_FOR_ASP) { + p2ps_prov->conncap = p2ps_group_capability( + wpa_s, P2PS_SETUP_NONE, p2ps_prov->role); + wpa_printf(MSG_DEBUG, + "P2P: %s conncap: %d - ASP parsed: %x %x %d %s", + __func__, p2ps_prov->conncap, + p2ps_prov->adv_id, p2ps_prov->conncap, + p2ps_prov->status, p2ps_prov->info); + + config_methods = 0; + } else 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; @@ -4294,6 +6986,7 @@ int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, config_methods = WPS_CONFIG_PUSHBUTTON; else { wpa_printf(MSG_DEBUG, "P2P: Unknown config method"); + os_free(p2ps_prov); return -1; } @@ -4306,7 +6999,7 @@ int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, wpa_s->auto_pd_scan_retry = 0; wpas_p2p_stop_find(wpa_s); wpa_s->p2p_join_scan_count = 0; - os_get_time(&wpa_s->p2p_auto_started); + os_get_reltime(&wpa_s->p2p_auto_started); wpa_printf(MSG_DEBUG, "P2P: Auto PD started at %ld.%06ld", wpa_s->p2p_auto_started.sec, wpa_s->p2p_auto_started.usec); @@ -4314,16 +7007,12 @@ int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, return 0; } - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) { - return wpa_drv_p2p_prov_disc_req(wpa_s, peer_addr, - config_methods, - use == WPAS_P2P_PD_FOR_JOIN); - } - - if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) + if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) { + os_free(p2ps_prov); return -1; + } - return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr, + return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr, p2ps_prov, config_methods, use == WPAS_P2P_PD_FOR_JOIN, 0, 1); } @@ -4341,6 +7030,8 @@ static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s) if (!offchannel_pending_action_tx(wpa_s)) return; + wpas_p2p_action_tx_clear(wpa_s); + wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new " "operation request"); offchannel_clear_pending_action_tx(wpa_s); @@ -4350,14 +7041,12 @@ static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s) 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, unsigned int search_delay) + const u8 *dev_id, unsigned int search_delay, + u8 seek_cnt, const char **seek_string, int freq) { 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 || wpa_s->p2p_in_provisioning) return -1; @@ -4366,35 +7055,55 @@ int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout, return p2p_find(wpa_s->global->p2p, timeout, type, num_req_dev_types, req_dev_types, dev_id, - search_delay); + search_delay, seek_cnt, seek_string, freq); +} + + +static void wpas_p2p_scan_res_ignore_search(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + wpa_printf(MSG_DEBUG, "P2P: Ignore scan results"); + + if (wpa_s->p2p_scan_work) { + struct wpa_radio_work *work = wpa_s->p2p_scan_work; + wpa_s->p2p_scan_work = NULL; + radio_work_done(work); + } + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + + /* + * Indicate that results have been processed so that the P2P module can + * continue pending tasks. + */ + p2p_scan_res_handled(wpa_s->global->p2p); } -static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s) +static void wpas_p2p_stop_find_oper(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->global->p2p_cb_on_scan_complete = 0; - - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) { - wpa_drv_p2p_stop_find(wpa_s); - return 1; - } if (wpa_s->global->p2p) p2p_stop_find(wpa_s->global->p2p); - return 0; + if (wpa_s->scan_res_handler == wpas_p2p_scan_res_handler) { + wpa_printf(MSG_DEBUG, + "P2P: Do not consider the scan results after stop_find"); + wpa_s->scan_res_handler = wpas_p2p_scan_res_ignore_search; + } } void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s) { - if (wpas_p2p_stop_find_oper(wpa_s) > 0) - return; - wpas_p2p_remove_pending_group_interface(wpa_s); + wpas_p2p_stop_find_oper(wpa_s); + if (!wpa_s->global->pending_group_iface_for_p2ps) + wpas_p2p_remove_pending_group_interface(wpa_s); } @@ -4454,6 +7163,8 @@ int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, if (wpa_s->global->p2p_disabled) return -1; + if (wpa_s->conf->p2p_disabled) + return -1; if (wpa_s->global->p2p == NULL) return -1; if (bss == NULL) @@ -4519,7 +7230,7 @@ void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies) } -void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s) +static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s) { p2p_group_deinit(wpa_s->p2p_group); wpa_s->p2p_group = NULL; @@ -4535,9 +7246,6 @@ 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; @@ -4548,10 +7256,19 @@ int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *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, int freq, - int ht40) + int ht40, int vht, int pref_freq) { enum p2p_invite_role role; u8 *bssid = NULL; + int force_freq = 0; + int res; + int no_pref_freq_given = pref_freq == 0; + + wpa_s->global->p2p_invite_group = NULL; + if (peer_addr) + os_memcpy(wpa_s->p2p_auth_invite, peer_addr, ETH_ALEN); + else + os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN); wpa_s->p2p_persistent_go_freq = freq; wpa_s->p2p_go_ht40 = !!ht40; @@ -4579,16 +7296,32 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr, } 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); + res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq, + role == P2P_INVITE_ROLE_GO); + if (res) + return res; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; + if (wpa_s->parent->conf->p2p_ignore_shared_freq && + no_pref_freq_given && pref_freq > 0 && + wpa_s->num_multichan_concurrent > 1 && + wpas_p2p_num_unused_channels(wpa_s) > 0) { + wpa_printf(MSG_DEBUG, "P2P: Ignore own channel preference %d MHz for invitation due to p2p_ignore_shared_freq=1 configuration", + pref_freq); + pref_freq = 0; + } + + /* + * Stop any find/listen operations before invitation and possibly + * connection establishment. + */ + wpas_p2p_stop_find_oper(wpa_s); + return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid, - ssid->ssid, ssid->ssid_len, freq, go_dev_addr, 1); + ssid->ssid, ssid->ssid_len, force_freq, go_dev_addr, + 1, pref_freq, -1); } @@ -4601,9 +7334,12 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, u8 *bssid = NULL; struct wpa_ssid *ssid; int persistent; + int freq = 0, force_freq = 0, pref_freq = 0; + int res; wpa_s->p2p_persistent_go_freq = 0; wpa_s->p2p_go_ht40 = 0; + wpa_s->p2p_go_vht = 0; for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { if (os_strcmp(wpa_s->ifname, ifname) == 0) @@ -4621,6 +7357,7 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, return -1; } + wpa_s->global->p2p_invite_group = wpa_s; persistent = ssid->p2p_persistent_group && wpas_p2p_get_persistent(wpa_s->parent, peer_addr, ssid->ssid, ssid->ssid_len); @@ -4630,6 +7367,7 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, bssid = wpa_s->own_addr; if (go_dev_addr == NULL) go_dev_addr = wpa_s->global->p2p_dev_addr; + freq = ssid->frequency; } else { role = P2P_INVITE_ROLE_CLIENT; if (wpa_s->wpa_state < WPA_ASSOCIATED) { @@ -4641,31 +7379,35 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname, if (go_dev_addr == NULL && !is_zero_ether_addr(wpa_s->go_dev_addr)) go_dev_addr = wpa_s->go_dev_addr; + freq = wpa_s->current_bss ? wpa_s->current_bss->freq : + (int) wpa_s->assoc_freq; } 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; + res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq, + role == P2P_INVITE_ROLE_ACTIVE_GO); + if (res) + return res; + wpas_p2p_set_own_freq_preference(wpa_s, force_freq); + return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid, - ssid->ssid, ssid->ssid_len, wpa_s->assoc_freq, - go_dev_addr, persistent); + ssid->ssid, ssid->ssid_len, force_freq, + go_dev_addr, persistent, pref_freq, -1); } 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; + u8 ip[3 * 4]; + char ip_addr[100]; if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) { eloop_cancel_timeout(wpas_p2p_group_formation_timeout, @@ -4673,11 +7415,10 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) } if (!wpa_s->show_group_started || !ssid) - goto done; + 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); @@ -4690,52 +7431,41 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) 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]" : ""); + + ip_addr[0] = '\0'; + if (wpa_sm_get_p2p_ip_addr(wpa_s->wpa, ip) == 0) { + int res; + + res = os_snprintf(ip_addr, sizeof(ip_addr), + " ip_addr=%u.%u.%u.%u " + "ip_mask=%u.%u.%u.%u go_ip_addr=%u.%u.%u.%u", + ip[0], ip[1], ip[2], ip[3], + ip[4], ip[5], ip[6], ip[7], + ip[8], ip[9], ip[10], ip[11]); + if (os_snprintf_error(sizeof(ip_addr), res)) + ip_addr[0] = '\0'; } + wpas_p2p_group_started(wpa_s, 0, ssid, freq, + ssid->passphrase == NULL && ssid->psk_set ? + ssid->psk : NULL, + ssid->passphrase, go_dev_addr, persistent, + ip_addr); + 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); - -done: - if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled && - wpa_s->global->p2p != NULL) { - wpa_s->global->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 " - "continued after successful connection"); - p2p_increase_search_delay( - wpa_s->global->p2p, - wpas_p2p_search_delay(wpa_s)); - } - } } 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; + int ret; + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; @@ -4744,18 +7474,19 @@ int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1, 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); + ret = p2p_presence_req(wpa_s->global->p2p, wpa_s->bssid, + wpa_s->own_addr, wpa_s->assoc_freq, + duration1, interval1, duration2, interval2); + if (ret == 0) + wpa_s->waiting_presence_resp = 1; + + return ret; } 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; @@ -4857,8 +7588,6 @@ int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, { if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return 0; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - return 0; if (!locally_generated) p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie, @@ -4886,8 +7615,6 @@ void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, { if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return; - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) - return; if (!locally_generated) p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie, @@ -4964,20 +7691,27 @@ void wpas_p2p_update_config(struct wpa_supplicant *wpa_s) u8 reg_class, channel; int ret; unsigned int r; + u8 channel_forced; + 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; + channel_forced = 1; } 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; + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + channel = 1; + else + channel = 1 + (r % 3) * 5; + channel_forced = 0; } - ret = p2p_set_listen_channel(p2p, reg_class, channel); + ret = p2p_set_listen_channel(p2p, reg_class, channel, + channel_forced); if (ret) wpa_printf(MSG_ERROR, "P2P: Own listen channel update " "failed: %d", ret); @@ -4997,8 +7731,10 @@ void wpas_p2p_update_config(struct wpa_supplicant *wpa_s) * 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; + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + op_channel = 1; + else + op_channel = 1 + (r % 3) * 5; cfg_op_channel = 0; } ret = p2p_set_oper_channel(p2p, op_reg_class, op_channel, @@ -5014,7 +7750,15 @@ void wpas_p2p_update_config(struct wpa_supplicant *wpa_s) wpa_printf(MSG_ERROR, "P2P: Preferred channel list " "update failed"); } + + if (p2p_set_no_go_freq(p2p, &wpa_s->conf->p2p_no_go_freq) < 0) { + wpa_printf(MSG_ERROR, "P2P: No GO channel list " + "update failed"); + } } + + if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_PASSPHRASE_LEN) + p2p_set_passphrase_len(p2p, wpa_s->conf->p2p_passphrase_len); } @@ -5032,8 +7776,6 @@ 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); @@ -5048,9 +7790,10 @@ int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled) 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); + wpa_msg_global(iface->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", + iface->ifname, + iface->cross_connect_uplink); } } @@ -5077,9 +7820,9 @@ static void wpas_p2p_enable_cross_connect(struct wpa_supplicant *uplink) 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); + wpa_msg_global(iface->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s", + iface->ifname, iface->cross_connect_uplink); } } @@ -5097,9 +7840,9 @@ static void wpas_p2p_disable_cross_connect(struct wpa_supplicant *uplink) 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); + wpa_msg_global(iface->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_DISABLE "%s %s", + iface->ifname, iface->cross_connect_uplink); iface->cross_connect_in_use = 0; } } @@ -5142,7 +7885,8 @@ static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s) if (iface->drv_flags & WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE) continue; - if (iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) + if ((iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) && + iface != wpa_s->parent) continue; wpa_s->cross_connect_enabled = 1; @@ -5159,9 +7903,9 @@ static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s) 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); + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_CROSS_CONNECT_ENABLE "%s %s", + wpa_s->ifname, wpa_s->cross_connect_uplink); break; } } @@ -5177,33 +7921,57 @@ int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s) "session overlap"); if (wpa_s != wpa_s->parent) wpa_msg_ctrl(wpa_s->parent, MSG_INFO, WPS_EVENT_OVERLAP); + wpas_p2p_group_formation_failed(wpa_s); + return 1; +} - 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_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpas_p2p_notif_pbc_overlap(wpa_s); } void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s) { - struct p2p_channels chan; + struct p2p_channels chan, cli_chan; + struct wpa_supplicant *ifs; 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)) { + os_memset(&cli_chan, 0, sizeof(cli_chan)); + if (wpas_p2p_setup_channels(wpa_s, &chan, &cli_chan)) { wpa_printf(MSG_ERROR, "P2P: Failed to update supported " "channel list"); return; } - p2p_update_channel_list(wpa_s->global->p2p, &chan); + p2p_update_channel_list(wpa_s->global->p2p, &chan, &cli_chan); + + for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { + int freq; + if (!ifs->current_ssid || + !ifs->current_ssid->p2p_group || + (ifs->current_ssid->mode != WPAS_MODE_P2P_GO && + ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION)) + continue; + freq = ifs->current_ssid->frequency; + if (freq_included(&chan, freq)) { + wpa_dbg(ifs, MSG_DEBUG, + "P2P GO operating frequency %d MHz in valid range", + freq); + continue; + } + + wpa_dbg(ifs, MSG_DEBUG, + "P2P GO operating in invalid frequency %d MHz", freq); + /* TODO: Consider using CSA or removing the group within + * wpa_supplicant */ + wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP); + } } @@ -5269,6 +8037,11 @@ int wpas_p2p_cancel(struct wpa_supplicant *wpa_s) wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_REQUESTED); break; + } else if (wpa_s->p2p_in_invitation) { + wpa_printf(MSG_DEBUG, "P2P: Interface %s in invitation found - cancelling", + wpa_s->ifname); + found = 1; + wpas_p2p_group_formation_failed(wpa_s); } } @@ -5296,7 +8069,7 @@ 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)) + if (p2p == NULL) return; p2p_set_best_channels(p2p, freq_24, freq_5, freq_overall); } @@ -5307,7 +8080,7 @@ 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)) + if (p2p == NULL) return -1; if (hwaddr_aton(addr, peer)) @@ -5341,10 +8114,42 @@ int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s) int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s) { + int ret; + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return 0; - return p2p_in_progress(wpa_s->global->p2p); + ret = p2p_in_progress(wpa_s->global->p2p); + if (ret == 0) { + /* + * Check whether there is an ongoing WPS provisioning step (or + * other parts of group formation) on another interface since + * p2p_in_progress() does not report this to avoid issues for + * scans during such provisioning step. + */ + if (wpa_s->global->p2p_group_formation && + wpa_s->global->p2p_group_formation != wpa_s) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Another interface (%s) " + "in group formation", + wpa_s->global->p2p_group_formation->ifname); + ret = 1; + } + } + + if (!ret && wpa_s->global->p2p_go_wait_client.sec) { + struct os_reltime now; + os_get_reltime(&now); + if (os_reltime_expired(&now, &wpa_s->global->p2p_go_wait_client, + P2P_MAX_INITIAL_CONN_WAIT_GO)) { + /* Wait for the first client has expired */ + wpa_s->global->p2p_go_wait_client.sec = 0; + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Waiting for initial client connection during group formation"); + ret = 1; + } + } + + return ret; } @@ -5384,12 +8189,17 @@ struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s, (ssid_len != s->ssid_len || os_memcmp(ssid, s->ssid, ssid_len) != 0)) continue; + if (addr == NULL) { + if (s->mode == WPAS_MODE_P2P_GO) + return s; + 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, + if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN, addr, ETH_ALEN) == 0) return s; /* peer is P2P client in persistent * group */ @@ -5403,34 +8213,75 @@ struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s, void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, const u8 *addr) { + if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout, + wpa_s->parent, NULL) > 0) { + /* + * This can happen if WPS provisioning step is not terminated + * cleanly (e.g., P2P Client does not send WSC_Done). Since the + * peer was able to connect, there is no need to time out group + * formation after this, though. In addition, this is used with + * the initial connection wait on the GO as a separate formation + * timeout and as such, expected to be hit after the initial WPS + * provisioning step. + */ + wpa_printf(MSG_DEBUG, "P2P: Canceled P2P group formation timeout on data connection"); + + if (!wpa_s->p2p_go_group_formation_completed && + !wpa_s->group_formation_reported) { + /* + * GO has not yet notified group formation success since + * the WPS step was not completed cleanly. Do that + * notification now since the P2P Client was able to + * connect and as such, must have received the + * credential from the WPS step. + */ + if (wpa_s->global->p2p) + p2p_wps_success_cb(wpa_s->global->p2p, addr); + wpas_group_formation_completed(wpa_s, 1); + } + } + if (!wpa_s->p2p_go_group_formation_completed) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Marking group formation completed on GO on first data connection"); + wpa_s->p2p_go_group_formation_completed = 1; + wpa_s->global->p2p_group_formation = NULL; + wpa_s->p2p_in_provisioning = 0; + wpa_s->p2p_in_invitation = 0; + } + wpa_s->global->p2p_go_wait_client.sec = 0; if (addr == NULL) return; wpas_p2p_add_persistent_group_client(wpa_s, addr); } -static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s, - int group_added) +static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s, + int group_added) { struct wpa_supplicant *group = wpa_s; + int ret = 0; + if (wpa_s->global->p2p_group_formation) group = wpa_s->global->p2p_group_formation; wpa_s = wpa_s->parent; offchannel_send_action_done(wpa_s); if (group_added) - wpas_p2p_group_delete(group, P2P_GROUP_REMOVAL_SILENT); + ret = wpas_p2p_group_delete(group, P2P_GROUP_REMOVAL_SILENT); wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Fall back to GO Negotiation"); wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin, wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0, 0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq, wpa_s->p2p_persistent_id, wpa_s->p2p_pd_before_go_neg, - wpa_s->p2p_go_ht40); + wpa_s->p2p_go_ht40, + wpa_s->p2p_go_vht); + return ret; } int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s) { + int res; + if (!wpa_s->p2p_fallback_to_go_neg || wpa_s->p2p_in_provisioning <= 5) return 0; @@ -5440,45 +8291,1008 @@ int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s) wpa_dbg(wpa_s, MSG_DEBUG, "P2P: GO not found for p2p_connect-auto - " "fallback to GO Negotiation"); - wpas_p2p_fallback_to_go_neg(wpa_s, 1); + wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG + "reason=GO-not-found"); + res = wpas_p2p_fallback_to_go_neg(wpa_s, 1); - return 1; + return res == 1 ? 2 : 1; } unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s) { - const char *rn, *rn2; struct wpa_supplicant *ifs; if (wpa_s->wpa_state > WPA_SCANNING) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search delay due to " "concurrent operation", - P2P_CONCURRENT_SEARCH_DELAY); - return P2P_CONCURRENT_SEARCH_DELAY; + wpa_s->conf->p2p_search_delay); + return wpa_s->conf->p2p_search_delay; } - if (!wpa_s->driver->get_radio_name) - return 0; - rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv); - if (rn == NULL || rn[0] == '\0') + dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, + radio_list) { + if (ifs != wpa_s && ifs->wpa_state > WPA_SCANNING) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search " + "delay due to concurrent operation on " + "interface %s", + wpa_s->conf->p2p_search_delay, + ifs->ifname); + return wpa_s->conf->p2p_search_delay; + } + } + + return 0; +} + + +static int wpas_p2p_remove_psk_entry(struct wpa_supplicant *wpa_s, + struct wpa_ssid *s, const u8 *addr, + int iface_addr) +{ + struct psk_list_entry *psk, *tmp; + int changed = 0; + + dl_list_for_each_safe(psk, tmp, &s->psk_list, struct psk_list_entry, + list) { + if ((iface_addr && !psk->p2p && + os_memcmp(addr, psk->addr, ETH_ALEN) == 0) || + (!iface_addr && psk->p2p && + os_memcmp(addr, psk->addr, ETH_ALEN) == 0)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Remove persistent group PSK list entry for " + MACSTR " p2p=%u", + MAC2STR(psk->addr), psk->p2p); + dl_list_del(&psk->list); + os_free(psk); + changed++; + } + } + + return changed; +} + + +void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, + const u8 *p2p_dev_addr, + const u8 *psk, size_t psk_len) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + struct wpa_ssid *persistent; + struct psk_list_entry *p, *last; + + if (psk_len != sizeof(p->psk)) + return; + + if (p2p_dev_addr) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New PSK for addr=" MACSTR + " p2p_dev_addr=" MACSTR, + MAC2STR(mac_addr), MAC2STR(p2p_dev_addr)); + if (is_zero_ether_addr(p2p_dev_addr)) + p2p_dev_addr = NULL; + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New PSK for addr=" MACSTR, + MAC2STR(mac_addr)); + } + + if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: new_psk_cb during group formation"); + /* To be added to persistent group once created */ + if (wpa_s->global->add_psk == NULL) { + wpa_s->global->add_psk = os_zalloc(sizeof(*p)); + if (wpa_s->global->add_psk == NULL) + return; + } + p = wpa_s->global->add_psk; + if (p2p_dev_addr) { + p->p2p = 1; + os_memcpy(p->addr, p2p_dev_addr, ETH_ALEN); + } else { + p->p2p = 0; + os_memcpy(p->addr, mac_addr, ETH_ALEN); + } + os_memcpy(p->psk, psk, psk_len); + return; + } + + if (ssid->mode != WPAS_MODE_P2P_GO || !ssid->p2p_persistent_group) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Ignore new_psk_cb on not-persistent GO"); + return; + } + + persistent = wpas_p2p_get_persistent(wpa_s->parent, NULL, ssid->ssid, + ssid->ssid_len); + if (!persistent) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not find persistent group information to store the new PSK"); + return; + } + + p = os_zalloc(sizeof(*p)); + if (p == NULL) + return; + if (p2p_dev_addr) { + p->p2p = 1; + os_memcpy(p->addr, p2p_dev_addr, ETH_ALEN); + } else { + p->p2p = 0; + os_memcpy(p->addr, mac_addr, ETH_ALEN); + } + os_memcpy(p->psk, psk, psk_len); + + if (dl_list_len(&persistent->psk_list) > P2P_MAX_STORED_CLIENTS && + (last = dl_list_last(&persistent->psk_list, + struct psk_list_entry, list))) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove oldest PSK entry for " + MACSTR " (p2p=%u) to make room for a new one", + MAC2STR(last->addr), last->p2p); + dl_list_del(&last->list); + os_free(last); + } + + wpas_p2p_remove_psk_entry(wpa_s->parent, persistent, + p2p_dev_addr ? p2p_dev_addr : mac_addr, + p2p_dev_addr == NULL); + if (p2p_dev_addr) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add new PSK for p2p_dev_addr=" + MACSTR, MAC2STR(p2p_dev_addr)); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add new PSK for addr=" MACSTR, + MAC2STR(mac_addr)); + } + dl_list_add(&persistent->psk_list, &p->list); + + 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"); +} + + +static void wpas_p2p_remove_psk(struct wpa_supplicant *wpa_s, + struct wpa_ssid *s, const u8 *addr, + int iface_addr) +{ + int res; + + res = wpas_p2p_remove_psk_entry(wpa_s, s, addr, iface_addr); + if (res > 0 && wpa_s->conf->update_config && + wpa_config_write(wpa_s->confname, wpa_s->conf)) + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Failed to update configuration"); +} + + +static void wpas_p2p_remove_client_go(struct wpa_supplicant *wpa_s, + const u8 *peer, int iface_addr) +{ + struct hostapd_data *hapd; + struct hostapd_wpa_psk *psk, *prev, *rem; + struct sta_info *sta; + + if (wpa_s->ap_iface == NULL || wpa_s->current_ssid == NULL || + wpa_s->current_ssid->mode != WPAS_MODE_P2P_GO) + return; + + /* Remove per-station PSK entry */ + hapd = wpa_s->ap_iface->bss[0]; + prev = NULL; + psk = hapd->conf->ssid.wpa_psk; + while (psk) { + if ((iface_addr && os_memcmp(peer, psk->addr, ETH_ALEN) == 0) || + (!iface_addr && + os_memcmp(peer, psk->p2p_dev_addr, ETH_ALEN) == 0)) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove operating group PSK entry for " + MACSTR " iface_addr=%d", + MAC2STR(peer), iface_addr); + if (prev) + prev->next = psk->next; + else + hapd->conf->ssid.wpa_psk = psk->next; + rem = psk; + psk = psk->next; + os_free(rem); + } else { + prev = psk; + psk = psk->next; + } + } + + /* Disconnect from group */ + if (iface_addr) + sta = ap_get_sta(hapd, peer); + else + sta = ap_get_sta_p2p(hapd, peer); + if (sta) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disconnect peer " MACSTR + " (iface_addr=%d) from group", + MAC2STR(peer), iface_addr); + hostapd_drv_sta_deauth(hapd, sta->addr, + WLAN_REASON_DEAUTH_LEAVING); + ap_sta_deauthenticate(hapd, sta, WLAN_REASON_DEAUTH_LEAVING); + } +} + + +void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer, + int iface_addr) +{ + struct wpa_ssid *s; + struct wpa_supplicant *w; + + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove client " MACSTR, MAC2STR(peer)); + + /* Remove from any persistent group */ + for (s = wpa_s->parent->conf->ssid; s; s = s->next) { + if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO) + continue; + if (!iface_addr) + wpas_remove_persistent_peer(wpa_s, s, peer, 0); + wpas_p2p_remove_psk(wpa_s->parent, s, peer, iface_addr); + } + + /* Remove from any operating group */ + for (w = wpa_s->global->ifaces; w; w = w->next) + wpas_p2p_remove_client_go(w, peer, iface_addr); +} + + +static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_PSK_FAILURE); +} + + +static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - terminate group"); + wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_FREQ_CONFLICT); +} + + +int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq, + struct wpa_ssid *ssid) +{ + struct wpa_supplicant *iface; + + for (iface = wpa_s->global->ifaces; iface; iface = iface->next) { + if (!iface->current_ssid || + iface->current_ssid->frequency == freq || + (iface->p2p_group_interface == NOT_P2P_GROUP_INTERFACE && + !iface->current_ssid->p2p_group)) + continue; + + /* Remove the connection with least priority */ + if (!wpas_is_p2p_prioritized(iface)) { + /* STA connection has priority over existing + * P2P connection, so remove the interface. */ + wpa_printf(MSG_DEBUG, "P2P: Removing P2P connection due to single channel concurrent mode frequency conflict"); + eloop_register_timeout(0, 0, + wpas_p2p_group_freq_conflict, + iface, NULL); + /* If connection in progress is P2P connection, do not + * proceed for the connection. */ + if (wpa_s == iface) + return -1; + else + return 0; + } else { + /* P2P connection has priority, disable the STA network + */ + wpa_supplicant_disable_network(wpa_s->global->ifaces, + ssid); + wpa_msg(wpa_s->global->ifaces, MSG_INFO, + WPA_EVENT_FREQ_CONFLICT " id=%d", ssid->id); + os_memset(wpa_s->global->ifaces->pending_bssid, 0, + ETH_ALEN); + /* If P2P connection is in progress, continue + * connecting...*/ + if (wpa_s == iface) + return 0; + else + return -1; + } + } + + return 0; +} + + +int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (ssid == NULL || !ssid->p2p_group) return 0; - for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) { - if (ifs == wpa_s || !ifs->driver->get_radio_name) + if (wpa_s->p2p_last_4way_hs_fail && + wpa_s->p2p_last_4way_hs_fail == ssid) { + u8 go_dev_addr[ETH_ALEN]; + struct wpa_ssid *persistent; + + if (wpas_p2p_persistent_group(wpa_s, go_dev_addr, + ssid->ssid, + ssid->ssid_len) <= 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not determine whether 4-way handshake failures were for a persistent group"); + goto disconnect; + } + + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Two 4-way handshake failures for a P2P group - go_dev_addr=" + MACSTR, MAC2STR(go_dev_addr)); + persistent = wpas_p2p_get_persistent(wpa_s->parent, go_dev_addr, + ssid->ssid, + ssid->ssid_len); + if (persistent == NULL || persistent->mode != WPAS_MODE_INFRA) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No matching persistent group stored"); + goto disconnect; + } + wpa_msg_global(wpa_s->parent, MSG_INFO, + P2P_EVENT_PERSISTENT_PSK_FAIL "%d", + persistent->id); + disconnect: + wpa_s->p2p_last_4way_hs_fail = NULL; + /* + * Remove the group from a timeout to avoid issues with caller + * continuing to use the interface if this is on a P2P group + * interface. + */ + eloop_register_timeout(0, 0, wpas_p2p_psk_failure_removal, + wpa_s, NULL); + return 1; + } + + wpa_s->p2p_last_4way_hs_fail = ssid; + return 0; +} + + +#ifdef CONFIG_WPS_NFC + +static struct wpabuf * wpas_p2p_nfc_handover(int ndef, struct wpabuf *wsc, + struct wpabuf *p2p) +{ + struct wpabuf *ret; + size_t wsc_len; + + if (p2p == NULL) { + wpabuf_free(wsc); + wpa_printf(MSG_DEBUG, "P2P: No p2p buffer for handover"); + return NULL; + } + + wsc_len = wsc ? wpabuf_len(wsc) : 0; + ret = wpabuf_alloc(2 + wsc_len + 2 + wpabuf_len(p2p)); + if (ret == NULL) { + wpabuf_free(wsc); + wpabuf_free(p2p); + return NULL; + } + + wpabuf_put_be16(ret, wsc_len); + if (wsc) + wpabuf_put_buf(ret, wsc); + wpabuf_put_be16(ret, wpabuf_len(p2p)); + wpabuf_put_buf(ret, p2p); + + wpabuf_free(wsc); + wpabuf_free(p2p); + wpa_hexdump_buf(MSG_DEBUG, + "P2P: Generated NFC connection handover message", ret); + + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_p2p(ret); + wpabuf_free(ret); + if (tmp == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Failed to NDEF encapsulate handover request"); + return NULL; + } + ret = tmp; + } + + return ret; +} + + +static int wpas_p2p_cli_freq(struct wpa_supplicant *wpa_s, + struct wpa_ssid **ssid, u8 *go_dev_addr) +{ + struct wpa_supplicant *iface; + + if (go_dev_addr) + os_memset(go_dev_addr, 0, ETH_ALEN); + if (ssid) + *ssid = NULL; + for (iface = wpa_s->global->ifaces; iface; iface = iface->next) { + if (iface->wpa_state < WPA_ASSOCIATING || + iface->current_ssid == NULL || iface->assoc_freq == 0 || + !iface->current_ssid->p2p_group || + iface->current_ssid->mode != WPAS_MODE_INFRA) + continue; + if (ssid) + *ssid = iface->current_ssid; + if (go_dev_addr) + os_memcpy(go_dev_addr, iface->go_dev_addr, ETH_ALEN); + return iface->assoc_freq; + } + return 0; +} + + +struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s, + int ndef) +{ + struct wpabuf *wsc, *p2p; + struct wpa_ssid *ssid; + u8 go_dev_addr[ETH_ALEN]; + int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr); + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) { + wpa_printf(MSG_DEBUG, "P2P: P2P disabled - cannot build handover request"); + return NULL; + } + + if (wpa_s->conf->wps_nfc_dh_pubkey == NULL && + wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey, + &wpa_s->conf->wps_nfc_dh_privkey) < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No DH key available for handover request"); + return NULL; + } + + if (cli_freq == 0) { + wsc = wps_build_nfc_handover_req_p2p( + wpa_s->parent->wps, wpa_s->conf->wps_nfc_dh_pubkey); + } else + wsc = NULL; + p2p = p2p_build_nfc_handover_req(wpa_s->global->p2p, cli_freq, + go_dev_addr, ssid ? ssid->ssid : NULL, + ssid ? ssid->ssid_len : 0); + + return wpas_p2p_nfc_handover(ndef, wsc, p2p); +} + + +struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s, + int ndef, int tag) +{ + struct wpabuf *wsc, *p2p; + struct wpa_ssid *ssid; + u8 go_dev_addr[ETH_ALEN]; + int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr); + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return NULL; + + if (!tag && wpa_s->conf->wps_nfc_dh_pubkey == NULL && + wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey, + &wpa_s->conf->wps_nfc_dh_privkey) < 0) + return NULL; + + if (cli_freq == 0) { + wsc = wps_build_nfc_handover_sel_p2p( + wpa_s->parent->wps, + tag ? wpa_s->conf->wps_nfc_dev_pw_id : + DEV_PW_NFC_CONNECTION_HANDOVER, + wpa_s->conf->wps_nfc_dh_pubkey, + tag ? wpa_s->conf->wps_nfc_dev_pw : NULL); + } else + wsc = NULL; + p2p = p2p_build_nfc_handover_sel(wpa_s->global->p2p, cli_freq, + go_dev_addr, ssid ? ssid->ssid : NULL, + ssid ? ssid->ssid_len : 0); + + return wpas_p2p_nfc_handover(ndef, wsc, p2p); +} + + +static int wpas_p2p_nfc_join_group(struct wpa_supplicant *wpa_s, + struct p2p_nfc_params *params) +{ + wpa_printf(MSG_DEBUG, "P2P: Initiate join-group based on NFC " + "connection handover (freq=%d)", + params->go_freq); + + if (params->go_freq && params->go_ssid_len) { + wpa_s->p2p_wps_method = WPS_NFC; + wpa_s->pending_join_wps_method = WPS_NFC; + os_memset(wpa_s->pending_join_iface_addr, 0, ETH_ALEN); + os_memcpy(wpa_s->pending_join_dev_addr, params->go_dev_addr, + ETH_ALEN); + return wpas_p2p_join_start(wpa_s, params->go_freq, + params->go_ssid, + params->go_ssid_len); + } + + return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL, + WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent, + params->go_freq, -1, 0, 1, 1); +} + + +static int wpas_p2p_nfc_auth_join(struct wpa_supplicant *wpa_s, + struct p2p_nfc_params *params, int tag) +{ + int res, persistent; + struct wpa_ssid *ssid; + + wpa_printf(MSG_DEBUG, "P2P: Authorize join-group based on NFC " + "connection handover"); + for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) { + ssid = wpa_s->current_ssid; + if (ssid == NULL) + continue; + if (ssid->mode != WPAS_MODE_P2P_GO) + continue; + if (wpa_s->ap_iface == NULL) continue; + break; + } + if (wpa_s == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Could not find GO interface"); + return -1; + } + + if (wpa_s->parent->p2p_oob_dev_pw_id != + DEV_PW_NFC_CONNECTION_HANDOVER && + !wpa_s->parent->p2p_oob_dev_pw) { + wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known"); + return -1; + } + res = wpas_ap_wps_add_nfc_pw( + wpa_s, wpa_s->parent->p2p_oob_dev_pw_id, + wpa_s->parent->p2p_oob_dev_pw, + wpa_s->parent->p2p_peer_oob_pk_hash_known ? + wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL); + if (res) + return res; + + if (!tag) { + wpa_printf(MSG_DEBUG, "P2P: Negotiated handover - wait for peer to join without invitation"); + return 0; + } + + if (!params->peer || + !(params->peer->dev_capab & P2P_DEV_CAPAB_INVITATION_PROCEDURE)) + return 0; + + wpa_printf(MSG_DEBUG, "P2P: Static handover - invite peer " MACSTR + " to join", MAC2STR(params->peer->p2p_device_addr)); + + wpa_s->global->p2p_invite_group = wpa_s; + persistent = ssid->p2p_persistent_group && + wpas_p2p_get_persistent(wpa_s->parent, + params->peer->p2p_device_addr, + ssid->ssid, ssid->ssid_len); + wpa_s->parent->pending_invite_ssid_id = -1; + + return p2p_invite(wpa_s->global->p2p, params->peer->p2p_device_addr, + P2P_INVITE_ROLE_ACTIVE_GO, wpa_s->own_addr, + ssid->ssid, ssid->ssid_len, ssid->frequency, + wpa_s->global->p2p_dev_addr, persistent, 0, + wpa_s->parent->p2p_oob_dev_pw_id); +} + + +static int wpas_p2p_nfc_init_go_neg(struct wpa_supplicant *wpa_s, + struct p2p_nfc_params *params, + int forced_freq) +{ + wpa_printf(MSG_DEBUG, "P2P: Initiate GO Negotiation based on NFC " + "connection handover"); + return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL, + WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent, + forced_freq, -1, 0, 1, 1); +} + + +static int wpas_p2p_nfc_resp_go_neg(struct wpa_supplicant *wpa_s, + struct p2p_nfc_params *params, + int forced_freq) +{ + int res; - rn2 = ifs->driver->get_radio_name(ifs->drv_priv); - if (!rn2 || os_strcmp(rn, rn2) != 0) + wpa_printf(MSG_DEBUG, "P2P: Authorize GO Negotiation based on NFC " + "connection handover"); + res = wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL, + WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent, + forced_freq, -1, 0, 1, 1); + if (res) + return res; + + res = wpas_p2p_listen(wpa_s, 60); + if (res) { + p2p_unauthorize(wpa_s->global->p2p, + params->peer->p2p_device_addr); + } + + return res; +} + + +static int wpas_p2p_nfc_connection_handover(struct wpa_supplicant *wpa_s, + const struct wpabuf *data, + int sel, int tag, int forced_freq) +{ + const u8 *pos, *end; + u16 len, id; + struct p2p_nfc_params params; + int res; + + os_memset(¶ms, 0, sizeof(params)); + params.sel = sel; + + wpa_hexdump_buf(MSG_DEBUG, "P2P: Received NFC tag payload", data); + + pos = wpabuf_head(data); + end = pos + wpabuf_len(data); + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of WSC " + "attributes"); + return -1; + } + len = WPA_GET_BE16(pos); + pos += 2; + if (len > end - pos) { + wpa_printf(MSG_DEBUG, "P2P: Not enough data for WSC " + "attributes"); + return -1; + } + params.wsc_attr = pos; + params.wsc_len = len; + pos += len; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of P2P " + "attributes"); + return -1; + } + len = WPA_GET_BE16(pos); + pos += 2; + if (len > end - pos) { + wpa_printf(MSG_DEBUG, "P2P: Not enough data for P2P " + "attributes"); + return -1; + } + params.p2p_attr = pos; + params.p2p_len = len; + pos += len; + + wpa_hexdump(MSG_DEBUG, "P2P: WSC attributes", + params.wsc_attr, params.wsc_len); + wpa_hexdump(MSG_DEBUG, "P2P: P2P attributes", + params.p2p_attr, params.p2p_len); + if (pos < end) { + wpa_hexdump(MSG_DEBUG, + "P2P: Ignored extra data after P2P attributes", + pos, end - pos); + } + + res = p2p_process_nfc_connection_handover(wpa_s->global->p2p, ¶ms); + if (res) + return res; + + if (params.next_step == NO_ACTION) + return 0; + + if (params.next_step == BOTH_GO) { + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_BOTH_GO "peer=" MACSTR, + MAC2STR(params.peer->p2p_device_addr)); + return 0; + } + + if (params.next_step == PEER_CLIENT) { + if (!is_zero_ether_addr(params.go_dev_addr)) { + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT + "peer=" MACSTR " freq=%d go_dev_addr=" MACSTR + " ssid=\"%s\"", + MAC2STR(params.peer->p2p_device_addr), + params.go_freq, + MAC2STR(params.go_dev_addr), + wpa_ssid_txt(params.go_ssid, + params.go_ssid_len)); + } else { + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT + "peer=" MACSTR " freq=%d", + MAC2STR(params.peer->p2p_device_addr), + params.go_freq); + } + return 0; + } + + if (wpas_p2p_cli_freq(wpa_s, NULL, NULL)) { + wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_WHILE_CLIENT "peer=" + MACSTR, MAC2STR(params.peer->p2p_device_addr)); + return 0; + } + + wpabuf_free(wpa_s->p2p_oob_dev_pw); + wpa_s->p2p_oob_dev_pw = NULL; + + if (params.oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2) { + wpa_printf(MSG_DEBUG, "P2P: No peer OOB Dev Pw " + "received"); + return -1; + } + + id = WPA_GET_BE16(params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN); + wpa_printf(MSG_DEBUG, "P2P: Peer OOB Dev Pw %u", id); + wpa_hexdump(MSG_DEBUG, "P2P: Peer OOB Public Key hash", + params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN); + os_memcpy(wpa_s->p2p_peer_oob_pubkey_hash, + params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN); + wpa_s->p2p_peer_oob_pk_hash_known = 1; + + if (tag) { + if (id < 0x10) { + wpa_printf(MSG_DEBUG, "P2P: Static handover - invalid " + "peer OOB Device Password Id %u", id); + return -1; + } + wpa_printf(MSG_DEBUG, "P2P: Static handover - use peer OOB " + "Device Password Id %u", id); + wpa_hexdump_key(MSG_DEBUG, "P2P: Peer OOB Device Password", + params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2, + params.oob_dev_pw_len - + WPS_OOB_PUBKEY_HASH_LEN - 2); + wpa_s->p2p_oob_dev_pw_id = id; + wpa_s->p2p_oob_dev_pw = wpabuf_alloc_copy( + params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2, + params.oob_dev_pw_len - + WPS_OOB_PUBKEY_HASH_LEN - 2); + if (wpa_s->p2p_oob_dev_pw == NULL) + return -1; + + if (wpa_s->conf->wps_nfc_dh_pubkey == NULL && + wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey, + &wpa_s->conf->wps_nfc_dh_privkey) < 0) + return -1; + } else { + wpa_printf(MSG_DEBUG, "P2P: Using abbreviated WPS handshake " + "without Device Password"); + wpa_s->p2p_oob_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER; + } + + switch (params.next_step) { + case NO_ACTION: + case BOTH_GO: + case PEER_CLIENT: + /* already covered above */ + return 0; + case JOIN_GROUP: + return wpas_p2p_nfc_join_group(wpa_s, ¶ms); + case AUTH_JOIN: + return wpas_p2p_nfc_auth_join(wpa_s, ¶ms, tag); + case INIT_GO_NEG: + return wpas_p2p_nfc_init_go_neg(wpa_s, ¶ms, forced_freq); + case RESP_GO_NEG: + /* TODO: use own OOB Dev Pw */ + return wpas_p2p_nfc_resp_go_neg(wpa_s, ¶ms, forced_freq); + } + + return -1; +} + + +int wpas_p2p_nfc_tag_process(struct wpa_supplicant *wpa_s, + const struct wpabuf *data, int forced_freq) +{ + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + return wpas_p2p_nfc_connection_handover(wpa_s, data, 1, 1, forced_freq); +} + + +int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init, + const struct wpabuf *req, + const struct wpabuf *sel, int forced_freq) +{ + struct wpabuf *tmp; + int ret; + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "NFC: P2P connection handover reported"); + + wpa_hexdump_ascii(MSG_DEBUG, "NFC: Req", + wpabuf_head(req), wpabuf_len(req)); + wpa_hexdump_ascii(MSG_DEBUG, "NFC: Sel", + wpabuf_head(sel), wpabuf_len(sel)); + if (forced_freq) + wpa_printf(MSG_DEBUG, "NFC: Forced freq %d", forced_freq); + tmp = ndef_parse_p2p(init ? sel : req); + if (tmp == NULL) { + wpa_printf(MSG_DEBUG, "P2P: Could not parse NDEF"); + return -1; + } + + ret = wpas_p2p_nfc_connection_handover(wpa_s, tmp, init, 0, + forced_freq); + wpabuf_free(tmp); + + return ret; +} + + +int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled) +{ + const u8 *if_addr; + int go_intent = wpa_s->conf->p2p_go_intent; + struct wpa_supplicant *iface; + + if (wpa_s->global->p2p == NULL) + return -1; + + if (!enabled) { + wpa_printf(MSG_DEBUG, "P2P: Disable use of own NFC Tag"); + for (iface = wpa_s->global->ifaces; iface; iface = iface->next) + { + if (!iface->ap_iface) + continue; + hostapd_wps_nfc_token_disable(iface->ap_iface->bss[0]); + } + p2p_set_authorized_oob_dev_pw_id(wpa_s->global->p2p, 0, + 0, NULL); + if (wpa_s->p2p_nfc_tag_enabled) + wpas_p2p_remove_pending_group_interface(wpa_s); + wpa_s->p2p_nfc_tag_enabled = 0; + return 0; + } + + if (wpa_s->global->p2p_disabled) + return -1; + + if (wpa_s->conf->wps_nfc_dh_pubkey == NULL || + wpa_s->conf->wps_nfc_dh_privkey == NULL || + wpa_s->conf->wps_nfc_dev_pw == NULL || + wpa_s->conf->wps_nfc_dev_pw_id < 0x10) { + wpa_printf(MSG_DEBUG, "P2P: NFC password token not configured " + "to allow static handover cases"); + return -1; + } + + wpa_printf(MSG_DEBUG, "P2P: Enable use of own NFC Tag"); + + wpa_s->p2p_oob_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id; + wpabuf_free(wpa_s->p2p_oob_dev_pw); + wpa_s->p2p_oob_dev_pw = wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw); + if (wpa_s->p2p_oob_dev_pw == NULL) + return -1; + wpa_s->p2p_peer_oob_pk_hash_known = 0; + + if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO || + wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT) { + /* + * P2P Group Interface present and the command came on group + * interface, so enable the token for the current interface. + */ + wpa_s->create_p2p_iface = 0; + } else { + wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s); + } + + if (wpa_s->create_p2p_iface) { + enum wpa_driver_if_type iftype; + /* 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_addr = wpa_s->pending_interface_addr; + } else + if_addr = wpa_s->own_addr; + + wpa_s->p2p_nfc_tag_enabled = enabled; + + for (iface = wpa_s->global->ifaces; iface; iface = iface->next) { + struct hostapd_data *hapd; + if (iface->ap_iface == NULL) continue; - if (ifs->wpa_state > WPA_SCANNING) { - wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search " - "delay due to concurrent operation on " - "interface %s", - P2P_CONCURRENT_SEARCH_DELAY, ifs->ifname); - return P2P_CONCURRENT_SEARCH_DELAY; + hapd = iface->ap_iface->bss[0]; + wpabuf_free(hapd->conf->wps_nfc_dh_pubkey); + hapd->conf->wps_nfc_dh_pubkey = + wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey); + wpabuf_free(hapd->conf->wps_nfc_dh_privkey); + hapd->conf->wps_nfc_dh_privkey = + wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey); + wpabuf_free(hapd->conf->wps_nfc_dev_pw); + hapd->conf->wps_nfc_dev_pw = + wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw); + hapd->conf->wps_nfc_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id; + + if (hostapd_wps_nfc_token_enable(iface->ap_iface->bss[0]) < 0) { + wpa_dbg(iface, MSG_DEBUG, + "P2P: Failed to enable NFC Tag for GO"); } } + p2p_set_authorized_oob_dev_pw_id( + wpa_s->global->p2p, wpa_s->conf->wps_nfc_dev_pw_id, go_intent, + if_addr); return 0; } + +#endif /* CONFIG_WPS_NFC */ + + +static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s, + struct wpa_used_freq_data *freqs, + unsigned int num) +{ + u8 curr_chan, cand, chan; + unsigned int i; + + curr_chan = p2p_get_listen_channel(wpa_s->global->p2p); + for (i = 0, cand = 0; i < num; i++) { + ieee80211_freq_to_chan(freqs[i].freq, &chan); + if (curr_chan == chan) { + cand = 0; + break; + } + + if (chan == 1 || chan == 6 || chan == 11) + cand = chan; + } + + if (cand) { + wpa_dbg(wpa_s, MSG_DEBUG, + "P2P: Update Listen channel to %u based on operating channel", + cand); + p2p_set_listen_channel(wpa_s->global->p2p, 81, cand, 0); + } +} + + +void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s) +{ + struct wpa_used_freq_data *freqs; + unsigned int num = wpa_s->num_multichan_concurrent; + + if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) + return; + + /* + * If possible, optimize the Listen channel to be a channel that is + * already used by one of the other interfaces. + */ + if (!wpa_s->conf->p2p_optimize_listen_chan) + return; + + if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED) + return; + + freqs = os_calloc(num, sizeof(struct wpa_used_freq_data)); + if (!freqs) + return; + + num = get_shared_radio_freqs_data(wpa_s, freqs, num); + + wpas_p2p_optimize_listen_channel(wpa_s, freqs, num); + os_free(freqs); +} + + +void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s) +{ + if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing " + "the management interface is being removed"); + wpas_p2p_deinit_global(wpa_s->global); + } +} + + +void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->ap_iface->bss) + wpa_s->ap_iface->bss[0]->p2p_group = NULL; + wpas_p2p_group_deinit(wpa_s); +} diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h index b6ecf14cf1feb..b7861786ca22d 100644 --- a/wpa_supplicant/p2p_supplicant.h +++ b/wpa_supplicant/p2p_supplicant.h @@ -13,37 +13,42 @@ enum p2p_wps_method; struct p2p_go_neg_results; enum p2p_send_action_result; struct p2p_peer_info; +struct p2p_channels; +struct wps_event_fail; +struct p2ps_provision; -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_add_p2pdev_interface(struct wpa_supplicant *wpa_s, + const char *conf_p2p_dev); +struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s, + const u8 *ssid, size_t ssid_len); +struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s, + const u8 *peer_dev_addr); 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 auto_join, int join, int auth, int go_intent, int freq, int persistent_id, - int pd, int ht40); -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 pd, int ht40, int vht); +int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, + int freq, struct wpa_ssid *ssid); int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, - int freq, int ht40); + int freq, int ht40, int vht); int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int addr_allocated, - int freq, int ht40); + int force_freq, int neg_freq, int ht40, + int vht, const struct p2p_channels *channels, + int connection_timeout); struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); -void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, - int registrar); enum wpas_p2p_prov_disc_use { WPAS_P2P_PD_FOR_GO_NEG, WPAS_P2P_PD_FOR_JOIN, - WPAS_P2P_PD_AUTO + WPAS_P2P_PD_AUTO, + WPAS_P2P_PD_FOR_ASP }; int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr, const char *config_method, - enum wpas_p2p_prov_disc_use use); + enum wpas_p2p_prov_disc_use use, + struct p2ps_provision *p2ps_prov); 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); @@ -53,37 +58,19 @@ 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, unsigned int search_delay); + const u8 *dev_id, unsigned int search_delay, + u8 seek_cnt, const char **seek_string, int freq); 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_listen_start(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, - int ssi_signal); -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_group_formation_failed(struct wpa_supplicant *wpa_s); u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst, const struct wpabuf *tlvs); +u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id, + const char *svc_str, const char *info_substr); u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 version, const char *query); u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s, @@ -102,13 +89,17 @@ 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_service_add_asp(struct wpa_supplicant *wpa_s, int auto_accept, + u32 adv_id, const char *adv_str, u8 svc_state, + u16 config_methods, const char *svc_info); +int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id); +int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id); 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 freq, - int ht40); + int ht40, int vht, int pref_freq); 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, @@ -119,25 +110,12 @@ int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid, u16 reason_code, const u8 *ie, size_t ie_len, int locally_generated); -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); @@ -146,6 +124,193 @@ void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s, int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s); int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, u8 channel); +int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s, + struct hostapd_hw_modes *mode, u8 channel); unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s); +void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, + const u8 *p2p_dev_addr, + const u8 *psk, size_t psk_len); +void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer, + int iface_addr); +struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s, + int ndef); +struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s, + int ndef, int tag); +int wpas_p2p_nfc_tag_process(struct wpa_supplicant *wpa_s, + const struct wpabuf *data, int forced_freq); +int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init, + const struct wpabuf *req, + const struct wpabuf *sel, int forced_freq); +int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled); +void wpas_p2p_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx); + +#ifdef CONFIG_P2P + +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_completed(struct wpa_supplicant *wpa_s); +void wpas_p2p_update_config(struct wpa_supplicant *wpa_s); +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, + int ssi_signal); +void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr, + int registrar); +void wpas_p2p_update_channel_list(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); +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_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); +void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s); +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); +int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s); +void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s); +void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s); +void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s); +void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s); +void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s); +int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s); +void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s, + struct wps_event_fail *fail); +int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname); + +#else /* CONFIG_P2P */ + +static inline int +wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline void wpas_p2p_deinit(struct wpa_supplicant *wpa_s) +{ +} + +static inline void wpas_p2p_completed(struct wpa_supplicant *wpa_s) +{ +} + +static inline void wpas_p2p_update_config(struct wpa_supplicant *wpa_s) +{ +} + +static inline 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, + int ssi_signal) +{ + return 0; +} + +static inline void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, int registrar) +{ +} + +static inline void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s) +{ +} + +static inline void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s, + int freq_24, int freq_5, + int freq_overall) +{ +} + +static inline 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) +{ +} + +static inline void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq, + unsigned int duration) +{ +} + +static inline void +wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq) +{ +} + +static inline void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s) +{ +} + +static inline void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s) +{ +} + +static inline void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s) +{ +} + +static inline int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s) +{ +} + +static inline void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s) +{ +} + +static inline void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s) +{ +} + +static inline void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s) +{ +} + +static inline void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ +} + +static inline int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s) +{ + return 0; +} + +static inline void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s, + struct wps_event_fail *fail) +{ +} + +static inline int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, + const char *ifname) +{ + return 0; +} + +#endif /* CONFIG_P2P */ #endif /* P2P_SUPPLICANT_H */ diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c index 3503e65e576bf..ed5708585be1d 100644 --- a/wpa_supplicant/preauth_test.c +++ b/wpa_supplicant/preauth_test.c @@ -27,9 +27,6 @@ #include "drivers/driver.h" -extern int wpa_debug_level; -extern int wpa_debug_show_keys; - struct wpa_driver_ops *wpa_drivers[] = { NULL }; @@ -309,7 +306,7 @@ int main(int argc, char *argv[]) } os_memset(&wpa_s, 0, sizeof(wpa_s)); - wpa_s.conf = wpa_config_read(argv[1]); + wpa_s.conf = wpa_config_read(argv[1], NULL); if (wpa_s.conf == NULL) { printf("Failed to parse configuration file '%s'.\n", argv[1]); return -1; diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index d2b671a9a503a..805891a88005c 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - Scanning - * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,6 +11,7 @@ #include "utils/common.h" #include "utils/eloop.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" #include "config.h" #include "wpa_supplicant_i.h" #include "driver_i.h" @@ -21,6 +22,7 @@ #include "notify.h" #include "bss.h" #include "scan.h" +#include "mesh.h" static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s) @@ -94,6 +96,10 @@ int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid = wpa_s->conf->ssid; int count = 0, disabled = 0; + + if (wpa_s->p2p_mgmt) + return 0; /* no normal network profiles on p2p_mgmt interface */ + while (ssid) { if (!wpas_network_disabled(wpa_s, ssid)) count++; @@ -140,71 +146,69 @@ static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s, } -static int int_array_len(const int *a) -{ - int i; - for (i = 0; a && a[i]; i++) - ; - return i; -} - - -static void int_array_concat(int **res, const int *a) +static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit) { - int reslen, alen, i; - int *n; - - reslen = int_array_len(*res); - alen = int_array_len(a); + struct wpa_supplicant *wpa_s = work->wpa_s; + struct wpa_driver_scan_params *params = work->ctx; + int ret; - n = os_realloc_array(*res, reslen + alen + 1, sizeof(int)); - if (n == NULL) { - os_free(*res); - *res = NULL; + if (deinit) { + if (!work->started) { + wpa_scan_free_params(params); + return; + } + wpa_supplicant_notify_scanning(wpa_s, 0); + wpas_notify_scan_done(wpa_s, 0); + wpa_s->scan_work = NULL; return; } - for (i = 0; i <= alen; i++) - n[reslen + i] = a[i]; - *res = n; -} - - -static int freq_cmp(const void *a, const void *b) -{ - int _a = *(int *) a; - int _b = *(int *) b; - - if (_a == 0) - return 1; - if (_b == 0) - return -1; - return _a - _b; -} + if (wpas_update_random_addr_disassoc(wpa_s) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "Failed to assign random MAC address for a scan"); + radio_work_done(work); + return; + } -static void int_array_sort_unique(int *a) -{ - int alen; - int i, j; + wpa_supplicant_notify_scanning(wpa_s, 1); - if (a == NULL) - return; + if (wpa_s->clear_driver_scan_cache) { + wpa_printf(MSG_DEBUG, + "Request driver to clear scan cache due to local BSS flush"); + params->only_new_results = 1; + } + ret = wpa_drv_scan(wpa_s, params); + wpa_scan_free_params(params); + work->ctx = NULL; + if (ret) { + int retry = wpa_s->last_scan_req != MANUAL_SCAN_REQ; - alen = int_array_len(a); - qsort(a, alen, sizeof(int), freq_cmp); + if (wpa_s->disconnected) + retry = 0; - i = 0; - j = 1; - while (a[i] && a[j]) { - if (a[i] == a[j]) { - j++; - continue; + wpa_supplicant_notify_scanning(wpa_s, 0); + wpas_notify_scan_done(wpa_s, 0); + if (wpa_s->wpa_state == WPA_SCANNING) + wpa_supplicant_set_state(wpa_s, + wpa_s->scan_prev_wpa_state); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=%d%s", + ret, retry ? " retry=1" : ""); + radio_work_done(work); + + if (retry) { + /* Restore scan_req since we will try to scan again */ + wpa_s->scan_req = wpa_s->last_scan_req; + wpa_supplicant_req_scan(wpa_s, 1, 0); } - a[++i] = a[j++]; + return; } - if (a[i]) - i++; - a[i] = 0; + + os_get_reltime(&wpa_s->scan_trigger_time); + wpa_s->scan_runs++; + wpa_s->normal_scans++; + wpa_s->own_scan_requested = 1; + wpa_s->clear_driver_scan_cache = 0; + wpa_s->scan_work = work; } @@ -217,20 +221,24 @@ static void int_array_sort_unique(int *a) int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params) { - int ret; + struct wpa_driver_scan_params *ctx; - wpa_supplicant_notify_scanning(wpa_s, 1); + if (wpa_s->scan_work) { + wpa_dbg(wpa_s, MSG_INFO, "Reject scan trigger since one is already pending"); + return -1; + } - ret = wpa_drv_scan(wpa_s, params); - if (ret) { - wpa_supplicant_notify_scanning(wpa_s, 0); - wpas_notify_scan_done(wpa_s, 0); - } else { - wpa_s->scan_runs++; - wpa_s->normal_scans++; + ctx = wpa_scan_clone_params(params); + if (ctx == NULL) + return -1; + + if (radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0) + { + wpa_scan_free_params(ctx); + return -1; } - return ret; + return 0; } @@ -258,10 +266,9 @@ wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx) } -static int -wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s, - struct wpa_driver_scan_params *params, - int interval) +int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params, + int interval) { int ret; @@ -276,7 +283,7 @@ wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s, } -static int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s) +int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s) { int ret; @@ -308,7 +315,7 @@ wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids) } if (count == 0) return NULL; - ssids = os_zalloc(count * sizeof(struct wpa_driver_scan_filter)); + ssids = os_calloc(count, sizeof(struct wpa_driver_scan_filter)); if (ssids == NULL) return NULL; @@ -336,7 +343,7 @@ static void wpa_supplicant_optimize_freqs( 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)); + params->freqs = os_calloc(2, sizeof(int)); if (params->freqs) params->freqs[0] = wpa_s->go_params->freq; } else if (wpa_s->p2p_in_provisioning < 8 && @@ -350,6 +357,32 @@ static void wpa_supplicant_optimize_freqs( } wpa_s->p2p_in_provisioning++; } + + if (params->freqs == NULL && wpa_s->p2p_in_invitation) { + /* + * Optimize scan based on GO information during persistent + * group reinvocation + */ + if (wpa_s->p2p_in_invitation < 5 && + wpa_s->p2p_invite_go_freq > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO preferred frequency %d MHz during invitation", + wpa_s->p2p_invite_go_freq); + params->freqs = os_calloc(2, sizeof(int)); + if (params->freqs) + params->freqs[0] = wpa_s->p2p_invite_go_freq; + } + wpa_s->p2p_in_invitation++; + if (wpa_s->p2p_in_invitation > 20) { + /* + * This should not really happen since the variable is + * cleared on group removal, but if it does happen, make + * sure we do not get stuck in special invitation scan + * mode. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Clear p2p_in_invitation"); + wpa_s->p2p_in_invitation = 0; + } + } #endif /* CONFIG_P2P */ #ifdef CONFIG_WPS @@ -360,18 +393,19 @@ static void wpa_supplicant_optimize_freqs( */ 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)); + params->freqs = os_calloc(2, sizeof(int)); if (params->freqs) params->freqs[0] = wpa_s->wps_freq; wpa_s->after_wps--; - } + } else if (wpa_s->after_wps) + wpa_s->after_wps--; if (params->freqs == NULL && wpa_s->known_wps_freq && wpa_s->wps_freq) { /* Optimize provisioning scan based on already known channel */ wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz", wpa_s->wps_freq); - params->freqs = os_zalloc(2 * sizeof(int)); + params->freqs = os_calloc(2, sizeof(int)); if (params->freqs) params->freqs[0] = wpa_s->wps_freq; wpa_s->known_wps_freq = 0; /* only do this once */ @@ -388,11 +422,17 @@ static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s, return; wpabuf_put_u8(buf, WLAN_EID_EXT_CAPAB); - wpabuf_put_u8(buf, 4); + wpabuf_put_u8(buf, 6); 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, 0x00); +#ifdef CONFIG_HS20 + wpabuf_put_u8(buf, 0x40); /* Bit 46 - WNM-Notification */ +#else /* CONFIG_HS20 */ + wpabuf_put_u8(buf, 0x00); +#endif /* CONFIG_HS20 */ wpabuf_put_u8(buf, WLAN_EID_INTERWORKING); wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 : @@ -444,8 +484,15 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) } #endif /* CONFIG_P2P */ + wpa_supplicant_mesh_add_scan_ie(wpa_s, &extra_ie); + #endif /* CONFIG_WPS */ +#ifdef CONFIG_HS20 + if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 7) == 0) + wpas_hs20_add_indication(extra_ie, -1); +#endif /* CONFIG_HS20 */ + return extra_ie; } @@ -474,59 +521,122 @@ static int non_p2p_network_enabled(struct wpa_supplicant *wpa_s) return 0; } +#endif /* CONFIG_P2P */ -/* - * Find the operating frequency of any other virtual interface that is using - * the same radio concurrently. - */ -static int shared_vif_oper_freq(struct wpa_supplicant *wpa_s) + +static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, + u16 num_modes, + enum hostapd_hw_mode mode) { - const char *rn, *rn2; - struct wpa_supplicant *ifs; - u8 bssid[ETH_ALEN]; + u16 i; - if (!wpa_s->driver->get_radio_name) - return -1; + for (i = 0; i < num_modes; i++) { + if (modes[i].mode == mode) + return &modes[i]; + } - rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv); - if (rn == NULL || rn[0] == '\0') - return -1; + return NULL; +} - 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) +static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s, + enum hostapd_hw_mode band, + struct wpa_driver_scan_params *params) +{ + /* Include only supported channels for the specified band */ + struct hostapd_hw_modes *mode; + int count, i; + + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band); + if (mode == NULL) { + /* No channels supported in this band - use empty list */ + params->freqs = os_zalloc(sizeof(int)); + return; + } + + params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); + if (params->freqs == NULL) + return; + for (count = 0, i = 0; i < mode->num_channels; i++) { + if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED) continue; + params->freqs[count++] = mode->channels[i].freq; + } +} + + +static void wpa_setband_scan_freqs(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params) +{ + if (wpa_s->hw.modes == NULL) + return; /* unknown what channels the driver supports */ + if (params->freqs) + return; /* already using a limited channel set */ + if (wpa_s->setband == WPA_SETBAND_5G) + wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211A, + params); + else if (wpa_s->setband == WPA_SETBAND_2G) + wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, + params); +} - if (ifs->current_ssid == NULL || ifs->assoc_freq == 0) + +static void wpa_set_scan_ssids(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params, + size_t max_ssids) +{ + unsigned int i; + struct wpa_ssid *ssid; + + for (i = 0; i < wpa_s->scan_id_count; i++) { + unsigned int j; + + ssid = wpa_config_get_network(wpa_s->conf, wpa_s->scan_id[i]); + if (!ssid || !ssid->scan_ssid) continue; - if (ifs->current_ssid->mode == WPAS_MODE_AP || - ifs->current_ssid->mode == WPAS_MODE_P2P_GO) - return ifs->current_ssid->frequency; - if (wpa_drv_get_bssid(ifs, bssid) == 0) - return ifs->assoc_freq; + for (j = 0; j < params->num_ssids; j++) { + if (params->ssids[j].ssid_len == ssid->ssid_len && + params->ssids[j].ssid && + os_memcmp(params->ssids[j].ssid, ssid->ssid, + ssid->ssid_len) == 0) + break; + } + if (j < params->num_ssids) + continue; /* already in the list */ + + if (params->num_ssids + 1 > max_ssids) { + wpa_printf(MSG_DEBUG, + "Over max scan SSIDs for manual request"); + break; + } + + wpa_printf(MSG_DEBUG, "Scan SSID (manual request): %s", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + params->ssids[params->num_ssids].ssid = ssid->ssid; + params->ssids[params->num_ssids].ssid_len = ssid->ssid_len; + params->num_ssids++; } - return 0; + wpa_s->scan_id_count = 0; } -#endif /* CONFIG_P2P */ - static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; struct wpa_ssid *ssid; - enum scan_req_type scan_req = NORMAL_SCAN_REQ; - int ret; + int ret, p2p_in_prog; struct wpabuf *extra_ie = NULL; struct wpa_driver_scan_params params; struct wpa_driver_scan_params *scan_params; size_t max_ssids; - enum wpa_states prev_state; + int connect_without_scan = 0; + + if (wpa_s->pno || wpa_s->pno_sched_pending) { + wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - PNO is in progress"); + return; + } if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled"); @@ -539,13 +649,20 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) return; } + if (wpa_s->scanning) { + /* + * If we are already in scanning state, we shall reschedule the + * the incoming scan request. + */ + wpa_dbg(wpa_s, MSG_DEBUG, "Already scanning - Reschedule the incoming scan req"); + wpa_supplicant_req_scan(wpa_s, 1, 0); + return; + } + if (!wpa_supplicant_enabled_networks(wpa_s) && wpa_s->scan_req == NORMAL_SCAN_REQ) { wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks - do not scan"); wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); -#ifdef CONFIG_P2P - wpa_s->sta_scan_pending = 0; -#endif /* CONFIG_P2P */ return; } @@ -562,22 +679,24 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) return; } -#ifdef CONFIG_P2P - if (wpas_p2p_in_progress(wpa_s)) { - if (wpa_s->sta_scan_pending && - wpas_p2p_in_progress(wpa_s) == 2 && - wpa_s->global->p2p_cb_on_scan_complete) { - wpa_dbg(wpa_s, MSG_DEBUG, "Process pending station " - "mode scan during P2P search"); - } else { - wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan " - "while P2P operation is in progress"); - wpa_s->sta_scan_pending = 1; - wpa_supplicant_req_scan(wpa_s, 5, 0); - return; + ssid = NULL; + if (wpa_s->scan_req != MANUAL_SCAN_REQ && + wpa_s->connect_without_scan) { + connect_without_scan = 1; + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid == wpa_s->connect_without_scan) + break; } } -#endif /* CONFIG_P2P */ + + p2p_in_prog = wpas_p2p_in_progress(wpa_s); + if (p2p_in_prog && p2p_in_prog != 2 && + (!ssid || + (ssid->mode != WPAS_MODE_AP && ssid->mode != WPAS_MODE_P2P_GO))) { + wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan while P2P operation is in progress"); + wpa_supplicant_req_scan(wpa_s, 5, 0); + return; + } if (wpa_s->conf->ap_scan == 2) max_ssids = 1; @@ -587,12 +706,22 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) max_ssids = WPAS_MAX_SCAN_SSIDS; } - scan_req = wpa_s->scan_req; + wpa_s->last_scan_req = wpa_s->scan_req; wpa_s->scan_req = NORMAL_SCAN_REQ; + if (connect_without_scan) { + 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; + } + } + os_memset(¶ms, 0, sizeof(params)); - prev_state = wpa_s->wpa_state; + wpa_s->scan_prev_wpa_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); @@ -605,30 +734,30 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) goto scan; } - if (scan_req != MANUAL_SCAN_REQ && 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; - } - } - #ifdef CONFIG_P2P if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) && - wpa_s->go_params) { - wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during " - "P2P group formation"); + wpa_s->go_params && !wpa_s->conf->passive_scan) { + wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during P2P group formation (p2p_in_provisioning=%d show_group_started=%d)", + wpa_s->p2p_in_provisioning, + wpa_s->show_group_started); params.ssids[0].ssid = wpa_s->go_params->ssid; params.ssids[0].ssid_len = wpa_s->go_params->ssid_len; params.num_ssids = 1; goto ssid_list_set; } + + if (wpa_s->p2p_in_invitation) { + if (wpa_s->current_ssid) { + wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during invitation"); + params.ssids[0].ssid = wpa_s->current_ssid->ssid; + params.ssids[0].ssid_len = + wpa_s->current_ssid->ssid_len; + params.num_ssids = 1; + } else { + wpa_printf(MSG_DEBUG, "P2P: No specific SSID known for scan during invitation"); + } + goto ssid_list_set; + } #endif /* CONFIG_P2P */ /* Find the starting point from which to continue scanning */ @@ -643,7 +772,8 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) } } - if (scan_req != MANUAL_SCAN_REQ && wpa_s->conf->ap_scan == 2) { + if (wpa_s->last_scan_req != MANUAL_SCAN_REQ && + wpa_s->conf->ap_scan == 2) { wpa_s->connect_without_scan = NULL; wpa_s->prev_scan_wildcard = 0; wpa_supplicant_assoc_try(wpa_s, ssid); @@ -654,6 +784,36 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) * wildcard SSID. */ ssid = NULL; + } else if (wpa_s->reattach && wpa_s->current_ssid != NULL) { + /* + * Perform single-channel single-SSID scan for + * reassociate-to-same-BSS operation. + */ + /* Setup SSID */ + ssid = wpa_s->current_ssid; + wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID", + ssid->ssid, ssid->ssid_len); + params.ssids[0].ssid = ssid->ssid; + params.ssids[0].ssid_len = ssid->ssid_len; + params.num_ssids = 1; + + /* + * Allocate memory for frequency array, allocate one extra + * slot for the zero-terminator. + */ + params.freqs = os_malloc(sizeof(int) * 2); + if (params.freqs == NULL) { + wpa_dbg(wpa_s, MSG_ERROR, "Memory allocation failed"); + return; + } + params.freqs[0] = wpa_s->assoc_freq; + params.freqs[1] = 0; + + /* + * Reset the reattach flag so that we fall back to full scan if + * this scan fails. + */ + wpa_s->reattach = 0; } else { struct wpa_ssid *start = ssid, *tssid; int freqs_set = 0; @@ -680,7 +840,13 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) ssid = wpa_s->conf->ssid; } - for (tssid = wpa_s->conf->ssid; tssid; tssid = tssid->next) { + if (wpa_s->scan_id_count && + wpa_s->last_scan_req == MANUAL_SCAN_REQ) + wpa_set_scan_ssids(wpa_s, ¶ms, max_ssids); + + for (tssid = wpa_s->conf->ssid; + wpa_s->last_scan_req != MANUAL_SCAN_REQ && tssid; + tssid = tssid->next) { if (wpas_network_disabled(wpa_s, tssid)) continue; if ((params.freqs || !freqs_set) && tssid->scan_freq) { @@ -721,6 +887,12 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in " "the scan request"); params.num_ssids++; + } else if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && + wpa_s->manual_scan_passive && params.num_ssids == 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "Use passive scan based on manual request"); + } else if (wpa_s->conf->passive_scan) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Use passive scan based on configuration"); } else { wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; params.num_ssids++; @@ -734,10 +906,19 @@ ssid_list_set: wpa_supplicant_optimize_freqs(wpa_s, ¶ms); extra_ie = wpa_supplicant_extra_ies(wpa_s); -#ifdef CONFIG_HS20 - if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 6) == 0) - wpas_hs20_add_indication(extra_ie); -#endif /* CONFIG_HS20 */ + if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && + wpa_s->manual_scan_only_new) { + wpa_printf(MSG_DEBUG, + "Request driver to clear scan cache due to manual only_new=1 scan"); + params.only_new_results = 1; + } + + if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs == NULL && + wpa_s->manual_scan_freqs) { + wpa_dbg(wpa_s, MSG_DEBUG, "Limit manual scan to specified channels"); + params.freqs = wpa_s->manual_scan_freqs; + wpa_s->manual_scan_freqs = NULL; + } if (params.freqs == NULL && wpa_s->next_scan_freqs) { wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously " @@ -746,6 +927,32 @@ ssid_list_set: } else os_free(wpa_s->next_scan_freqs); wpa_s->next_scan_freqs = NULL; + wpa_setband_scan_freqs(wpa_s, ¶ms); + + /* See if user specified frequencies. If so, scan only those. */ + if (wpa_s->conf->freq_list && !params.freqs) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Optimize scan based on conf->freq_list"); + int_array_concat(¶ms.freqs, wpa_s->conf->freq_list); + } + + /* Use current associated channel? */ + if (wpa_s->conf->scan_cur_freq && !params.freqs) { + unsigned int num = wpa_s->num_multichan_concurrent; + + params.freqs = os_calloc(num + 1, sizeof(int)); + if (params.freqs) { + num = get_shared_radio_freqs(wpa_s, params.freqs, num); + if (num > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the " + "current operating channels since " + "scan_cur_freq is enabled"); + } else { + os_free(params.freqs); + params.freqs = NULL; + } + } + } params.filter_ssids = wpa_supplicant_build_filter_ssids( wpa_s->conf, ¶ms.num_filter_ssids); @@ -755,7 +962,7 @@ ssid_list_set: } #ifdef CONFIG_P2P - if (wpa_s->p2p_in_provisioning || + if (wpa_s->p2p_in_provisioning || wpa_s->p2p_in_invitation || (wpa_s->show_group_started && wpa_s->go_params)) { /* * The interface may not yet be in P2P mode, so we have to @@ -765,6 +972,14 @@ ssid_list_set: } #endif /* CONFIG_P2P */ + if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) { + params.mac_addr_rand = 1; + if (wpa_s->mac_addr_scan) { + params.mac_addr = wpa_s->mac_addr_scan; + params.mac_addr_mask = wpa_s->mac_addr_scan + ETH_ALEN; + } + } + scan_params = ¶ms; scan: @@ -778,44 +993,80 @@ scan: * station interface when we are not configured to prefer station * connection and a concurrent operation is already in process. */ - if (wpa_s->scan_for_connection && scan_req == NORMAL_SCAN_REQ && + if (wpa_s->scan_for_connection && + wpa_s->last_scan_req == NORMAL_SCAN_REQ && !scan_params->freqs && !params.freqs && wpas_is_p2p_prioritized(wpa_s) && - !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT) && wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE && non_p2p_network_enabled(wpa_s)) { - int freq = shared_vif_oper_freq(wpa_s); - if (freq > 0) { - wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only the current " - "operating channel (%d MHz) since driver does " - "not support multi-channel concurrency", freq); - params.freqs = os_zalloc(sizeof(int) * 2); - if (params.freqs) - params.freqs[0] = freq; - scan_params->freqs = params.freqs; + unsigned int num = wpa_s->num_multichan_concurrent; + + params.freqs = os_calloc(num + 1, sizeof(int)); + if (params.freqs) { + num = get_shared_radio_freqs(wpa_s, params.freqs, num); + if (num > 0 && num == wpa_s->num_multichan_concurrent) { + wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the current operating channels since all channels are already used"); + } else { + os_free(params.freqs); + params.freqs = NULL; + } } } #endif /* CONFIG_P2P */ ret = wpa_supplicant_trigger_scan(wpa_s, scan_params); + if (ret && wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs && + !wpa_s->manual_scan_freqs) { + /* Restore manual_scan_freqs for the next attempt */ + wpa_s->manual_scan_freqs = params.freqs; + params.freqs = NULL; + } + wpabuf_free(extra_ie); os_free(params.freqs); os_free(params.filter_ssids); if (ret) { 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); + if (wpa_s->scan_prev_wpa_state != wpa_s->wpa_state) + wpa_supplicant_set_state(wpa_s, + wpa_s->scan_prev_wpa_state); /* Restore scan_req since we will try to scan again */ - wpa_s->scan_req = scan_req; + wpa_s->scan_req = wpa_s->last_scan_req; wpa_supplicant_req_scan(wpa_s, 1, 0); } else { wpa_s->scan_for_connection = 0; +#ifdef CONFIG_INTERWORKING + wpa_s->interworking_fast_assoc_tried = 0; +#endif /* CONFIG_INTERWORKING */ } } +void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec) +{ + struct os_reltime remaining, new_int; + int cancelled; + + cancelled = eloop_cancel_timeout_one(wpa_supplicant_scan, wpa_s, NULL, + &remaining); + + new_int.sec = sec; + new_int.usec = 0; + if (cancelled && os_reltime_before(&remaining, &new_int)) { + new_int.sec = remaining.sec; + new_int.usec = remaining.usec; + } + + if (cancelled) { + eloop_register_timeout(new_int.sec, new_int.usec, + wpa_supplicant_scan, wpa_s, NULL); + } + wpa_s->scan_interval = sec; +} + + /** * wpa_supplicant_req_scan - Schedule a scan for neighboring access points * @wpa_s: Pointer to wpa_supplicant data @@ -827,33 +1078,28 @@ scan: */ void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec) { - /* If there's at least one network that should be specifically scanned - * then don't cancel the scan and reschedule. Some drivers do - * background scanning which generates frequent scan results, and that - * causes the specific SSID scan to get continually pushed back and - * never happen, which causes hidden APs to never get probe-scanned. - */ - if (eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL) && - wpa_s->conf->ap_scan == 1) { - struct wpa_ssid *ssid = wpa_s->conf->ssid; + int res; - while (ssid) { - if (!wpas_network_disabled(wpa_s, ssid) && - ssid->scan_ssid) - break; - ssid = ssid->next; - } - if (ssid) { - wpa_dbg(wpa_s, MSG_DEBUG, "Not rescheduling scan to " - "ensure that specific SSID scans occur"); - return; - } + if (wpa_s->p2p_mgmt) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Ignore scan request (%d.%06d sec) on p2p_mgmt interface", + sec, usec); + return; } - 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); + res = eloop_deplete_timeout(sec, usec, wpa_supplicant_scan, wpa_s, + NULL); + if (res == 1) { + wpa_dbg(wpa_s, MSG_DEBUG, "Rescheduling scan request: %d.%06d sec", + sec, usec); + } else if (res == 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "Ignore new scan request for %d.%06d sec since an earlier request is scheduled to trigger sooner", + sec, usec); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d.%06d sec", + sec, usec); + eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL); + } } @@ -962,7 +1208,7 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) 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 * + params.filter_ssids = os_calloc(wpa_s->max_match_sets, sizeof(struct wpa_driver_scan_filter)); prev_state = wpa_s->wpa_state; @@ -989,7 +1235,9 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) if (!ssid || !wpa_s->prev_sched_ssid) { wpa_dbg(wpa_s, MSG_DEBUG, "Beginning of SSID list"); - + if (wpa_s->conf->sched_scan_interval) + wpa_s->sched_scan_interval = + wpa_s->conf->sched_scan_interval; if (wpa_s->sched_scan_interval == 0) wpa_s->sched_scan_interval = 10; wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2; @@ -1063,6 +1311,16 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) params.extra_ies_len = wpabuf_len(extra_ie); } + if (wpa_s->conf->filter_rssi) + params.filter_rssi = wpa_s->conf->filter_rssi; + + /* See if user specified frequencies. If so, scan only those. */ + if (wpa_s->conf->freq_list && !params.freqs) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Optimize scan based on conf->freq_list"); + int_array_concat(¶ms.freqs, wpa_s->conf->freq_list); + } + scan_params = ¶ms; scan: @@ -1076,6 +1334,17 @@ scan: wpa_s->sched_scan_interval); } + wpa_setband_scan_freqs(wpa_s, scan_params); + + if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) { + params.mac_addr_rand = 1; + if (wpa_s->mac_addr_sched_scan) { + params.mac_addr = wpa_s->mac_addr_sched_scan; + params.mac_addr_mask = wpa_s->mac_addr_sched_scan + + ETH_ALEN; + } + } + ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params, wpa_s->sched_scan_interval); wpabuf_free(extra_ie); @@ -1096,8 +1365,16 @@ scan: wpa_s->first_sched_scan = 0; wpa_s->sched_scan_timeout /= 2; wpa_s->sched_scan_interval *= 2; + if (wpa_s->sched_scan_timeout < wpa_s->sched_scan_interval) { + wpa_s->sched_scan_interval = 10; + wpa_s->sched_scan_timeout = max_sched_scan_ssids * 2; + } } + /* If there is no more ssids, start next time from the beginning */ + if (!ssid) + wpa_s->prev_sched_ssid = NULL; + return 0; } @@ -1117,6 +1394,23 @@ void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s) /** + * wpa_supplicant_cancel_delayed_sched_scan - Stop a delayed scheduled scan + * @wpa_s: Pointer to wpa_supplicant data + * + * This function is used to stop a delayed scheduled scan. + */ +void wpa_supplicant_cancel_delayed_sched_scan(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->sched_scan_supported) + return; + + wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling delayed sched scan"); + eloop_cancel_timeout(wpa_supplicant_delayed_sched_scan_timeout, + wpa_s, NULL); +} + + +/** * wpa_supplicant_cancel_sched_scan - Stop running scheduled scans * @wpa_s: Pointer to wpa_supplicant data * @@ -1234,6 +1528,43 @@ const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, /** + * wpa_scan_get_vendor_ie_beacon - Fetch vendor information from a scan result + * @res: Scan result entry + * @vendor_type: Vendor type (four octets starting the IE payload) + * Returns: Pointer to the information element (id field) or %NULL if not found + * + * This function returns the first matching information element in the scan + * result. + * + * This function is like wpa_scan_get_vendor_ie(), but uses IE buffer only + * from Beacon frames instead of either Beacon or Probe Response frames. + */ +const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res, + u32 vendor_type) +{ + const u8 *end, *pos; + + if (res->beacon_ie_len == 0) + 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])) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + + +/** * wpa_scan_get_vendor_ie_multi - Fetch vendor IE data from a scan result * @res: Scan result entry * @vendor_type: Vendor type (four octets starting the IE payload) @@ -1284,18 +1615,19 @@ struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, */ #define GREAT_SNR 30 +#define IS_5GHZ(n) (n > 4000) + /* 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; + int wpa_a, wpa_b; + int snr_a, snr_b, snr_a_full, snr_b_full; /* WPA/WPA2 support preferred */ wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL || @@ -1316,37 +1648,34 @@ static int wpa_scan_result_compar(const void *a, const void *b) (wb->caps & IEEE80211_CAP_PRIVACY) == 0) return -1; - 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); + if (wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) { + snr_a_full = wa->snr; + snr_a = MIN(wa->snr, GREAT_SNR); + snr_b_full = wb->snr; + snr_b = MIN(wa->snr, GREAT_SNR); } else { - /* Not suitable information to calculate SNR, so use level */ - snr_a = wa->level; - snr_b = wb->level; + /* Level is not in dBm, so we can't calculate + * SNR. Just use raw level (units unknown). */ + snr_a = snr_a_full = wa->level; + snr_b = snr_b_full = wb->level; } - /* best/max rate preferred if SNR close enough */ - if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) || + /* if SNR is close, decide by max rate or frequency band */ + 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 (wa->est_throughput != wb->est_throughput) + return wb->est_throughput - wa->est_throughput; 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) + if (snr_b_full == snr_a_full) return wb->qual - wa->qual; - return snr_b - snr_a; + return snr_b_full - snr_a_full; #undef MIN -#undef IS_5GHZ } @@ -1411,19 +1740,22 @@ static void dump_scan_res(struct wpa_scan_results *scan_res) for (i = 0; i < scan_res->num; i++) { struct wpa_scan_res *r = scan_res->res[i]; u8 *pos; - if ((r->flags & (WPA_SCAN_LEVEL_DBM | WPA_SCAN_NOISE_INVALID)) - == WPA_SCAN_LEVEL_DBM) { - int snr = r->level - r->noise; + if (r->flags & WPA_SCAN_LEVEL_DBM) { + int noise_valid = !(r->flags & WPA_SCAN_NOISE_INVALID); + wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d " - "noise=%d level=%d snr=%d%s flags=0x%x", + "noise=%d%s level=%d snr=%d%s flags=0x%x age=%u est=%u", MAC2STR(r->bssid), r->freq, r->qual, - r->noise, r->level, snr, - snr >= GREAT_SNR ? "*" : "", r->flags); + r->noise, noise_valid ? "" : "~", r->level, + r->snr, r->snr >= GREAT_SNR ? "*" : "", + r->flags, + r->age, r->est_throughput); } else { wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d " - "noise=%d level=%d flags=0x%x", + "noise=%d level=%d flags=0x%x age=%u est=%u", MAC2STR(r->bssid), r->freq, r->qual, - r->noise, r->level, r->flags); + r->noise, r->level, r->flags, r->age, + r->est_throughput); } pos = (u8 *) (r + 1); if (r->ie_len) @@ -1490,6 +1822,188 @@ static void filter_scan_res(struct wpa_supplicant *wpa_s, } +/* + * Noise floor values to use when we have signal strength + * measurements, but no noise floor measurments. These values were + * measured in an office environment with many APs. + */ +#define DEFAULT_NOISE_FLOOR_2GHZ (-89) +#define DEFAULT_NOISE_FLOOR_5GHZ (-92) + +static void scan_snr(struct wpa_scan_res *res) +{ + if (res->flags & WPA_SCAN_NOISE_INVALID) { + res->noise = IS_5GHZ(res->freq) ? + DEFAULT_NOISE_FLOOR_5GHZ : + DEFAULT_NOISE_FLOOR_2GHZ; + } + + if (res->flags & WPA_SCAN_LEVEL_DBM) { + res->snr = res->level - res->noise; + } else { + /* Level is not in dBm, so we can't calculate + * SNR. Just use raw level (units unknown). */ + res->snr = res->level; + } +} + + +static unsigned int max_ht20_rate(int snr) +{ + if (snr < 6) + return 6500; /* HT20 MCS0 */ + if (snr < 8) + return 13000; /* HT20 MCS1 */ + if (snr < 13) + return 19500; /* HT20 MCS2 */ + if (snr < 17) + return 26000; /* HT20 MCS3 */ + if (snr < 20) + return 39000; /* HT20 MCS4 */ + if (snr < 23) + return 52000; /* HT20 MCS5 */ + if (snr < 24) + return 58500; /* HT20 MCS6 */ + return 65000; /* HT20 MCS7 */ +} + + +static unsigned int max_ht40_rate(int snr) +{ + if (snr < 3) + return 13500; /* HT40 MCS0 */ + if (snr < 6) + return 27000; /* HT40 MCS1 */ + if (snr < 10) + return 40500; /* HT40 MCS2 */ + if (snr < 15) + return 54000; /* HT40 MCS3 */ + if (snr < 17) + return 81000; /* HT40 MCS4 */ + if (snr < 22) + return 108000; /* HT40 MCS5 */ + if (snr < 24) + return 121500; /* HT40 MCS6 */ + return 135000; /* HT40 MCS7 */ +} + + +static unsigned int max_vht80_rate(int snr) +{ + if (snr < 1) + return 0; + if (snr < 2) + return 29300; /* VHT80 MCS0 */ + if (snr < 5) + return 58500; /* VHT80 MCS1 */ + if (snr < 9) + return 87800; /* VHT80 MCS2 */ + if (snr < 11) + return 117000; /* VHT80 MCS3 */ + if (snr < 15) + return 175500; /* VHT80 MCS4 */ + if (snr < 16) + return 234000; /* VHT80 MCS5 */ + if (snr < 18) + return 263300; /* VHT80 MCS6 */ + if (snr < 20) + return 292500; /* VHT80 MCS7 */ + if (snr < 22) + return 351000; /* VHT80 MCS8 */ + return 390000; /* VHT80 MCS9 */ +} + + +static void scan_est_throughput(struct wpa_supplicant *wpa_s, + struct wpa_scan_res *res) +{ + enum local_hw_capab capab = wpa_s->hw_capab; + int rate; /* max legacy rate in 500 kb/s units */ + const u8 *ie; + unsigned int est, tmp; + int snr = res->snr; + + if (res->est_throughput) + return; + + /* Get maximum legacy rate */ + rate = wpa_scan_get_max_rate(res); + + /* Limit based on estimated SNR */ + if (rate > 1 * 2 && snr < 1) + rate = 1 * 2; + else if (rate > 2 * 2 && snr < 4) + rate = 2 * 2; + else if (rate > 6 * 2 && snr < 5) + rate = 6 * 2; + else if (rate > 9 * 2 && snr < 6) + rate = 9 * 2; + else if (rate > 12 * 2 && snr < 7) + rate = 12 * 2; + else if (rate > 18 * 2 && snr < 10) + rate = 18 * 2; + else if (rate > 24 * 2 && snr < 11) + rate = 24 * 2; + else if (rate > 36 * 2 && snr < 15) + rate = 36 * 2; + else if (rate > 48 * 2 && snr < 19) + rate = 48 * 2; + else if (rate > 54 * 2 && snr < 21) + rate = 54 * 2; + est = rate * 500; + + if (capab == CAPAB_HT || capab == CAPAB_HT40 || capab == CAPAB_VHT) { + ie = wpa_scan_get_ie(res, WLAN_EID_HT_CAP); + if (ie) { + tmp = max_ht20_rate(snr); + if (tmp > est) + est = tmp; + } + } + + if (capab == CAPAB_HT40 || capab == CAPAB_VHT) { + ie = wpa_scan_get_ie(res, WLAN_EID_HT_OPERATION); + if (ie && ie[1] >= 2 && + (ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) { + tmp = max_ht40_rate(snr); + if (tmp > est) + est = tmp; + } + } + + if (capab == CAPAB_VHT) { + /* Use +1 to assume VHT is always faster than HT */ + ie = wpa_scan_get_ie(res, WLAN_EID_VHT_CAP); + if (ie) { + tmp = max_ht20_rate(snr) + 1; + if (tmp > est) + est = tmp; + + ie = wpa_scan_get_ie(res, WLAN_EID_HT_OPERATION); + if (ie && ie[1] >= 2 && + (ie[3] & + HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) { + tmp = max_ht40_rate(snr) + 1; + if (tmp > est) + est = tmp; + } + + ie = wpa_scan_get_ie(res, WLAN_EID_VHT_OPERATION); + if (ie && ie[1] >= 1 && + (ie[2] & VHT_OPMODE_CHANNEL_WIDTH_MASK)) { + tmp = max_vht80_rate(snr) + 1; + if (tmp > est) + est = tmp; + } + } + } + + /* TODO: channel utilization and AP load (e.g., from AP Beacon) */ + + res->est_throughput = est; +} + + /** * wpa_supplicant_get_scan_results - Get scan results * @wpa_s: Pointer to wpa_supplicant data @@ -1514,10 +2028,24 @@ wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results"); return NULL; } + if (scan_res->fetch_time.sec == 0) { + /* + * Make sure we have a valid timestamp if the driver wrapper + * does not set this. + */ + os_get_reltime(&scan_res->fetch_time); + } filter_scan_res(wpa_s, scan_res); + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *scan_res_item = scan_res->res[i]; + + scan_snr(scan_res_item); + scan_est_throughput(wpa_s, scan_res_item); + } + #ifdef CONFIG_WPS - if (wpas_wps_in_progress(wpa_s)) { + if (wpas_wps_searching(wpa_s)) { wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS " "provisioning rules"); compar = wpa_scan_result_wps_compar; @@ -1530,7 +2058,8 @@ wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s, wpa_bss_update_start(wpa_s); for (i = 0; i < scan_res->num; i++) - wpa_bss_update_scan_res(wpa_s, scan_res->res[i]); + wpa_bss_update_scan_res(wpa_s, scan_res->res[i], + &scan_res->fetch_time); wpa_bss_update_end(wpa_s, info, new_scan); return scan_res; @@ -1559,3 +2088,341 @@ int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s) return 0; } + + +/** + * scan_only_handler - Reports scan results + */ +void scan_only_handler(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res) +{ + wpa_dbg(wpa_s, MSG_DEBUG, "Scan-only results received"); + if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && + wpa_s->manual_scan_use_id && wpa_s->own_scan_running) { + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u", + wpa_s->manual_scan_id); + wpa_s->manual_scan_use_id = 0; + } else { + wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS); + } + wpas_notify_scan_results(wpa_s); + wpas_notify_scan_done(wpa_s, 1); + if (wpa_s->scan_work) { + struct wpa_radio_work *work = wpa_s->scan_work; + wpa_s->scan_work = NULL; + radio_work_done(work); + } +} + + +int wpas_scan_scheduled(struct wpa_supplicant *wpa_s) +{ + return eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL); +} + + +struct wpa_driver_scan_params * +wpa_scan_clone_params(const struct wpa_driver_scan_params *src) +{ + struct wpa_driver_scan_params *params; + size_t i; + u8 *n; + + params = os_zalloc(sizeof(*params)); + if (params == NULL) + return NULL; + + for (i = 0; i < src->num_ssids; i++) { + if (src->ssids[i].ssid) { + n = os_malloc(src->ssids[i].ssid_len); + if (n == NULL) + goto failed; + os_memcpy(n, src->ssids[i].ssid, + src->ssids[i].ssid_len); + params->ssids[i].ssid = n; + params->ssids[i].ssid_len = src->ssids[i].ssid_len; + } + } + params->num_ssids = src->num_ssids; + + if (src->extra_ies) { + n = os_malloc(src->extra_ies_len); + if (n == NULL) + goto failed; + os_memcpy(n, src->extra_ies, src->extra_ies_len); + params->extra_ies = n; + params->extra_ies_len = src->extra_ies_len; + } + + if (src->freqs) { + int len = int_array_len(src->freqs); + params->freqs = os_malloc((len + 1) * sizeof(int)); + if (params->freqs == NULL) + goto failed; + os_memcpy(params->freqs, src->freqs, (len + 1) * sizeof(int)); + } + + if (src->filter_ssids) { + params->filter_ssids = os_malloc(sizeof(*params->filter_ssids) * + src->num_filter_ssids); + if (params->filter_ssids == NULL) + goto failed; + os_memcpy(params->filter_ssids, src->filter_ssids, + sizeof(*params->filter_ssids) * + src->num_filter_ssids); + params->num_filter_ssids = src->num_filter_ssids; + } + + params->filter_rssi = src->filter_rssi; + params->p2p_probe = src->p2p_probe; + params->only_new_results = src->only_new_results; + params->low_priority = src->low_priority; + + if (src->mac_addr_rand) { + params->mac_addr_rand = src->mac_addr_rand; + + if (src->mac_addr && src->mac_addr_mask) { + u8 *mac_addr; + + mac_addr = os_malloc(2 * ETH_ALEN); + if (!mac_addr) + goto failed; + + os_memcpy(mac_addr, src->mac_addr, ETH_ALEN); + os_memcpy(mac_addr + ETH_ALEN, src->mac_addr_mask, + ETH_ALEN); + params->mac_addr = mac_addr; + params->mac_addr_mask = mac_addr + ETH_ALEN; + } + } + return params; + +failed: + wpa_scan_free_params(params); + return NULL; +} + + +void wpa_scan_free_params(struct wpa_driver_scan_params *params) +{ + size_t i; + + if (params == NULL) + return; + + for (i = 0; i < params->num_ssids; i++) + os_free((u8 *) params->ssids[i].ssid); + os_free((u8 *) params->extra_ies); + os_free(params->freqs); + os_free(params->filter_ssids); + + /* + * Note: params->mac_addr_mask points to same memory allocation and + * must not be freed separately. + */ + os_free((u8 *) params->mac_addr); + + os_free(params); +} + + +int wpas_start_pno(struct wpa_supplicant *wpa_s) +{ + int ret, interval, prio; + size_t i, num_ssid, num_match_ssid; + struct wpa_ssid *ssid; + struct wpa_driver_scan_params params; + + if (!wpa_s->sched_scan_supported) + return -1; + + if (wpa_s->pno || wpa_s->pno_sched_pending) + return 0; + + if ((wpa_s->wpa_state > WPA_SCANNING) && + (wpa_s->wpa_state <= WPA_COMPLETED)) { + wpa_printf(MSG_ERROR, "PNO: In assoc process"); + return -EAGAIN; + } + + if (wpa_s->wpa_state == WPA_SCANNING) { + wpa_supplicant_cancel_scan(wpa_s); + if (wpa_s->sched_scanning) { + wpa_printf(MSG_DEBUG, "Schedule PNO on completion of " + "ongoing sched scan"); + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_s->pno_sched_pending = 1; + return 0; + } + } + + os_memset(¶ms, 0, sizeof(params)); + + num_ssid = num_match_ssid = 0; + ssid = wpa_s->conf->ssid; + while (ssid) { + if (!wpas_network_disabled(wpa_s, ssid)) { + num_match_ssid++; + if (ssid->scan_ssid) + num_ssid++; + } + ssid = ssid->next; + } + + if (num_match_ssid == 0) { + wpa_printf(MSG_DEBUG, "PNO: No configured SSIDs"); + return -1; + } + + if (num_match_ssid > num_ssid) { + params.num_ssids++; /* wildcard */ + num_ssid++; + } + + if (num_ssid > WPAS_MAX_SCAN_SSIDS) { + wpa_printf(MSG_DEBUG, "PNO: Use only the first %u SSIDs from " + "%u", WPAS_MAX_SCAN_SSIDS, (unsigned int) num_ssid); + num_ssid = WPAS_MAX_SCAN_SSIDS; + } + + if (num_match_ssid > wpa_s->max_match_sets) { + num_match_ssid = wpa_s->max_match_sets; + wpa_dbg(wpa_s, MSG_DEBUG, "PNO: Too many SSIDs to match"); + } + params.filter_ssids = os_calloc(num_match_ssid, + sizeof(struct wpa_driver_scan_filter)); + if (params.filter_ssids == NULL) + return -1; + + i = 0; + prio = 0; + ssid = wpa_s->conf->pssid[prio]; + while (ssid) { + if (!wpas_network_disabled(wpa_s, ssid)) { + if (ssid->scan_ssid && params.num_ssids < num_ssid) { + params.ssids[params.num_ssids].ssid = + ssid->ssid; + params.ssids[params.num_ssids].ssid_len = + ssid->ssid_len; + params.num_ssids++; + } + os_memcpy(params.filter_ssids[i].ssid, ssid->ssid, + ssid->ssid_len); + params.filter_ssids[i].ssid_len = ssid->ssid_len; + params.num_filter_ssids++; + i++; + if (i == num_match_ssid) + break; + } + if (ssid->pnext) + ssid = ssid->pnext; + else if (prio + 1 == wpa_s->conf->num_prio) + break; + else + ssid = wpa_s->conf->pssid[++prio]; + } + + if (wpa_s->conf->filter_rssi) + params.filter_rssi = wpa_s->conf->filter_rssi; + + interval = wpa_s->conf->sched_scan_interval ? + wpa_s->conf->sched_scan_interval : 10; + + if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) { + wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels"); + params.freqs = wpa_s->manual_sched_scan_freqs; + } + + if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) { + params.mac_addr_rand = 1; + if (wpa_s->mac_addr_pno) { + params.mac_addr = wpa_s->mac_addr_pno; + params.mac_addr_mask = wpa_s->mac_addr_pno + ETH_ALEN; + } + } + + ret = wpa_supplicant_start_sched_scan(wpa_s, ¶ms, interval); + os_free(params.filter_ssids); + if (ret == 0) + wpa_s->pno = 1; + else + wpa_msg(wpa_s, MSG_ERROR, "Failed to schedule PNO"); + return ret; +} + + +int wpas_stop_pno(struct wpa_supplicant *wpa_s) +{ + int ret = 0; + + if (!wpa_s->pno) + return 0; + + ret = wpa_supplicant_stop_sched_scan(wpa_s); + + wpa_s->pno = 0; + wpa_s->pno_sched_pending = 0; + + if (wpa_s->wpa_state == WPA_SCANNING) + wpa_supplicant_req_scan(wpa_s, 0, 0); + + return ret; +} + + +void wpas_mac_addr_rand_scan_clear(struct wpa_supplicant *wpa_s, + unsigned int type) +{ + type &= MAC_ADDR_RAND_ALL; + wpa_s->mac_addr_rand_enable &= ~type; + + if (type & MAC_ADDR_RAND_SCAN) { + os_free(wpa_s->mac_addr_scan); + wpa_s->mac_addr_scan = NULL; + } + + if (type & MAC_ADDR_RAND_SCHED_SCAN) { + os_free(wpa_s->mac_addr_sched_scan); + wpa_s->mac_addr_sched_scan = NULL; + } + + if (type & MAC_ADDR_RAND_PNO) { + os_free(wpa_s->mac_addr_pno); + wpa_s->mac_addr_pno = NULL; + } +} + + +int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s, + unsigned int type, const u8 *addr, + const u8 *mask) +{ + u8 *tmp = NULL; + + wpas_mac_addr_rand_scan_clear(wpa_s, type); + + if (addr) { + tmp = os_malloc(2 * ETH_ALEN); + if (!tmp) + return -1; + os_memcpy(tmp, addr, ETH_ALEN); + os_memcpy(tmp + ETH_ALEN, mask, ETH_ALEN); + } + + if (type == MAC_ADDR_RAND_SCAN) { + wpa_s->mac_addr_scan = tmp; + } else if (type == MAC_ADDR_RAND_SCHED_SCAN) { + wpa_s->mac_addr_sched_scan = tmp; + } else if (type == MAC_ADDR_RAND_PNO) { + wpa_s->mac_addr_pno = tmp; + } else { + wpa_printf(MSG_INFO, + "scan: Invalid MAC randomization type=0x%x", + type); + os_free(tmp); + return -1; + } + + wpa_s->mac_addr_rand_enable |= type; + return 0; +} diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h index 5096287af6a38..7650f5a250958 100644 --- a/wpa_supplicant/scan.h +++ b/wpa_supplicant/scan.h @@ -1,6 +1,6 @@ /* * WPA Supplicant - Scanning - * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -15,6 +15,7 @@ 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_delayed_sched_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); @@ -28,9 +29,30 @@ int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s); const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie); const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, u32 vendor_type); +const u8 * wpa_scan_get_vendor_ie_beacon(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); int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s, const u8 *bssid); +void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec); +void scan_only_handler(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res); +int wpas_scan_scheduled(struct wpa_supplicant *wpa_s); +int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params, + int interval); +int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s); +struct wpa_driver_scan_params * +wpa_scan_clone_params(const struct wpa_driver_scan_params *src); +void wpa_scan_free_params(struct wpa_driver_scan_params *params); +int wpas_start_pno(struct wpa_supplicant *wpa_s); +int wpas_stop_pno(struct wpa_supplicant *wpa_s); + +void wpas_mac_addr_rand_scan_clear(struct wpa_supplicant *wpa_s, + unsigned int type); +int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s, + unsigned int type, const u8 *addr, + const u8 *mask); #endif /* SCAN_H */ diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 77ad1d2e1e2c6..178811371409c 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -1,6 +1,6 @@ /* * wpa_supplicant - SME - * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,6 +14,7 @@ #include "common/ieee802_11_common.h" #include "eapol_supp/eapol_supp_sm.h" #include "common/wpa_common.h" +#include "common/sae.h" #include "rsn_supp/wpa.h" #include "rsn_supp/pmksa_cache.h" #include "config.h" @@ -41,20 +42,78 @@ static void sme_stop_sa_query(struct wpa_supplicant *wpa_s); #ifdef CONFIG_SAE -static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s) +static int index_within_array(const int *array, int idx) +{ + int i; + for (i = 0; i < idx; i++) { + if (array[i] <= 0) + return 0; + } + return 1; +} + + +static int sme_set_sae_group(struct wpa_supplicant *wpa_s) +{ + int *groups = wpa_s->conf->sae_groups; + int default_groups[] = { 19, 20, 21, 25, 26, 0 }; + + if (!groups || groups[0] <= 0) + groups = default_groups; + + /* Configuration may have changed, so validate current index */ + if (!index_within_array(groups, wpa_s->sme.sae_group_index)) + return -1; + + for (;;) { + int group = groups[wpa_s->sme.sae_group_index]; + if (group < 0) + break; + if (sae_set_group(&wpa_s->sme.sae, group) == 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d", + wpa_s->sme.sae.group); + return 0; + } + wpa_s->sme.sae_group_index++; + } + + return -1; +} + + +static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, + const u8 *bssid) { struct wpabuf *buf; + size_t len; + + if (ssid->passphrase == NULL) { + wpa_printf(MSG_DEBUG, "SAE: No password available"); + return NULL; + } + + if (sme_set_sae_group(wpa_s) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to select group"); + return NULL; + } - buf = wpabuf_alloc(4 + 2); + if (sae_prepare_commit(wpa_s->own_addr, bssid, + (u8 *) ssid->passphrase, + os_strlen(ssid->passphrase), + &wpa_s->sme.sae) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE"); + return NULL; + } + + len = wpa_s->sme.sae_token ? wpabuf_len(wpa_s->sme.sae_token) : 0; + buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + len); if (buf == NULL) return NULL; wpabuf_put_le16(buf, 1); /* Transaction seq# */ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); - wpabuf_put_le16(buf, 19); /* Finite Cyclic Group */ - /* TODO: Anti-Clogging Token (if requested) */ - /* TODO: Scalar */ - /* TODO: Element */ + sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token); return buf; } @@ -64,15 +123,13 @@ static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s) { struct wpabuf *buf; - buf = wpabuf_alloc(4 + 2); + buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN); if (buf == NULL) return NULL; wpabuf_put_le16(buf, 2); /* Transaction seq# */ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); - wpabuf_put_le16(buf, wpa_s->sme.sae_send_confirm); - wpa_s->sme.sae_send_confirm++; - /* TODO: Confirm */ + sae_write_confirm(&wpa_s->sme.sae, buf); return buf; } @@ -80,6 +137,60 @@ static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s) #endif /* CONFIG_SAE */ +/** + * sme_auth_handle_rrm - Handle RRM aspects of current authentication attempt + * @wpa_s: Pointer to wpa_supplicant data + * @bss: Pointer to the bss which is the target of authentication attempt + */ +static void sme_auth_handle_rrm(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss) +{ + const u8 rrm_ie_len = 5; + u8 *pos; + const u8 *rrm_ie; + + wpa_s->rrm.rrm_used = 0; + + wpa_printf(MSG_DEBUG, + "RRM: Determining whether RRM can be used - device support: 0x%x", + wpa_s->drv_rrm_flags); + + rrm_ie = wpa_bss_get_ie(bss, WLAN_EID_RRM_ENABLED_CAPABILITIES); + if (!rrm_ie || !(bss->caps & IEEE80211_CAP_RRM)) { + wpa_printf(MSG_DEBUG, "RRM: No RRM in network"); + return; + } + + if (!(wpa_s->drv_rrm_flags & + WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) || + !(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) { + wpa_printf(MSG_DEBUG, + "RRM: Insufficient RRM support in driver - do not use RRM"); + return; + } + + if (sizeof(wpa_s->sme.assoc_req_ie) < + wpa_s->sme.assoc_req_ie_len + rrm_ie_len + 2) { + wpa_printf(MSG_INFO, + "RRM: Unable to use RRM, no room for RRM IE"); + return; + } + + wpa_printf(MSG_DEBUG, "RRM: Adding RRM IE to Association Request"); + pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len; + os_memset(pos, 0, 2 + rrm_ie_len); + *pos++ = WLAN_EID_RRM_ENABLED_CAPABILITIES; + *pos++ = rrm_ie_len; + + /* Set supported capabilites flags */ + if (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION) + *pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT; + + wpa_s->sme.assoc_req_ie_len += rrm_ie_len + 2; + wpa_s->rrm.rrm_used = 1; +} + + static void sme_send_authentication(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid, int start) @@ -94,15 +205,19 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, #endif /* CONFIG_IEEE80211R */ int i, bssid_changed; struct wpabuf *resp = NULL; - u8 ext_capab[10]; + u8 ext_capab[18]; int ext_capab_len; + int skip_auth; if (bss == NULL) { wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for " "the network"); + wpas_connect_work_done(wpa_s); return; } + skip_auth = wpa_s->conf->reassoc_same_bss_optim && + wpa_s->reassoc_same_bss; wpa_s->current_bss = bss; os_memset(¶ms, 0, sizeof(params)); @@ -141,17 +256,22 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, "0x%x", params.auth_alg); } #ifdef CONFIG_SAE + wpa_s->sme.sae_pmksa_caching = 0; if (wpa_key_mgmt_sae(ssid->key_mgmt)) { const u8 *rsn; struct wpa_ie_data ied; rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); - if (rsn && - wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0) { - if (wpa_key_mgmt_sae(ied.key_mgmt)) { - wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg"); - params.auth_alg = WPA_AUTH_ALG_SAE; - } + if (!rsn) { + wpa_dbg(wpa_s, MSG_DEBUG, + "SAE enabled, but target BSS does not advertise RSN"); + } else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 && + wpa_key_mgmt_sae(ied.key_mgmt)) { + wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg"); + params.auth_alg = WPA_AUTH_ALG_SAE; + } else { + wpa_dbg(wpa_s, MSG_DEBUG, + "SAE enabled, but target BSS does not advertise SAE AKM for RSN"); } } #endif /* CONFIG_SAE */ @@ -180,13 +300,14 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, wpa_s->current_ssid, try_opportunistic) == 0) - eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1); + eapol_sm_notify_pmkid_attempt(wpa_s->eapol); wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie); if (wpa_supplicant_set_suites(wpa_s, bss, ssid, wpa_s->sme.assoc_req_ie, &wpa_s->sme.assoc_req_ie_len)) { wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA " "key management and encryption suites"); + wpas_connect_work_done(wpa_s); return; } } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && @@ -206,6 +327,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA " "key management and encryption suites (no " "scan results)"); + wpas_connect_work_done(wpa_s); return; } #ifdef CONFIG_WPS @@ -265,8 +387,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W - wpa_s->sme.mfp = ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? - wpa_s->conf->pmf : ssid->ieee80211w; + wpa_s->sme.mfp = wpas_get_ssid_pmf(wpa_s, ssid); if (wpa_s->sme.mfp != NO_MGMT_FRAME_PROTECTION) { const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); struct wpa_ie_data _ie; @@ -296,21 +417,29 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, #endif /* CONFIG_P2P */ #ifdef CONFIG_HS20 - if (wpa_s->conf->hs20) { + if (is_hs20_network(wpa_s, ssid, bss)) { struct wpabuf *hs20; hs20 = wpabuf_alloc(20); if (hs20) { - wpas_hs20_add_indication(hs20); - os_memcpy(wpa_s->sme.assoc_req_ie + - wpa_s->sme.assoc_req_ie_len, - wpabuf_head(hs20), wpabuf_len(hs20)); - wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20); + int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid); + size_t len; + + wpas_hs20_add_indication(hs20, pps_mo_id); + len = sizeof(wpa_s->sme.assoc_req_ie) - + wpa_s->sme.assoc_req_ie_len; + if (wpabuf_len(hs20) <= len) { + os_memcpy(wpa_s->sme.assoc_req_ie + + wpa_s->sme.assoc_req_ie_len, + wpabuf_head(hs20), wpabuf_len(hs20)); + wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20); + } wpabuf_free(hs20); } } #endif /* CONFIG_HS20 */ - ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab); + ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, + sizeof(ext_capab)); if (ext_capab_len > 0) { u8 *pos = wpa_s->sme.assoc_req_ie; if (wpa_s->sme.assoc_req_ie_len > 0 && pos[0] == WLAN_EID_RSN) @@ -322,17 +451,45 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, os_memcpy(pos, ext_capab, ext_capab_len); } + if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) { + struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]; + size_t len; + + len = sizeof(wpa_s->sme.assoc_req_ie) - + wpa_s->sme.assoc_req_ie_len; + if (wpabuf_len(buf) <= len) { + os_memcpy(wpa_s->sme.assoc_req_ie + + wpa_s->sme.assoc_req_ie_len, + wpabuf_head(buf), wpabuf_len(buf)); + wpa_s->sme.assoc_req_ie_len += wpabuf_len(buf); + } + } + + sme_auth_handle_rrm(wpa_s, bss); + #ifdef CONFIG_SAE - if (params.auth_alg == WPA_AUTH_ALG_SAE) { + if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE && + pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0) == 0) + { + wpa_dbg(wpa_s, MSG_DEBUG, + "PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication"); + params.auth_alg = WPA_AUTH_ALG_OPEN; + wpa_s->sme.sae_pmksa_caching = 1; + } + + if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE) { if (start) - resp = sme_auth_build_sae_commit(wpa_s); + resp = sme_auth_build_sae_commit(wpa_s, ssid, + bss->bssid); else resp = sme_auth_build_sae_confirm(wpa_s); - if (resp == NULL) + if (resp == NULL) { + wpas_connection_failed(wpa_s, bss->bssid); return; + } params.sae_data = wpabuf_head(resp); params.sae_data_len = wpabuf_len(resp); - wpa_s->sme.sae_state = start ? SME_SAE_COMMIT : SME_SAE_CONFIRM; + wpa_s->sme.sae.state = start ? SAE_COMMITTED : SAE_CONFIRMED; } #endif /* CONFIG_SAE */ @@ -352,6 +509,41 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, if (old_ssid != wpa_s->current_ssid) wpas_notify_network_changed(wpa_s); +#ifdef CONFIG_P2P + /* + * If multi-channel concurrency is not supported, check for any + * frequency conflict. In case of any frequency conflict, remove the + * least prioritized connection. + */ + if (wpa_s->num_multichan_concurrent < 2) { + int freq, num; + num = get_shared_radio_freqs(wpa_s, &freq, 1); + if (num > 0 && freq > 0 && freq != params.freq) { + wpa_printf(MSG_DEBUG, + "Conflicting frequency found (%d != %d)", + freq, params.freq); + if (wpas_p2p_handle_frequency_conflicts(wpa_s, + params.freq, + ssid) < 0) { + wpas_connection_failed(wpa_s, bss->bssid); + wpa_supplicant_mark_disassoc(wpa_s); + wpabuf_free(resp); + wpas_connect_work_done(wpa_s); + return; + } + } + } +#endif /* CONFIG_P2P */ + + if (skip_auth) { + wpa_msg(wpa_s, MSG_DEBUG, + "SME: Skip authentication step on reassoc-to-same-BSS"); + wpabuf_free(resp); + sme_associate(wpa_s, ssid->mode, bss->bssid, WLAN_AUTH_OPEN); + return; + } + + wpa_s->sme.auth_alg = params.auth_alg; if (wpa_drv_authenticate(wpa_s, ¶ms) < 0) { wpa_msg(wpa_s, MSG_INFO, "SME: Authentication request to the " @@ -359,6 +551,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, wpas_connection_failed(wpa_s, bss->bssid); wpa_supplicant_mark_disassoc(wpa_s); wpabuf_free(resp); + wpas_connect_work_done(wpa_s); return; } @@ -374,78 +567,170 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, } -void sme_authenticate(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss, struct wpa_ssid *ssid) +static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit) { - wpa_s->sme.sae_state = SME_SAE_INIT; - wpa_s->sme.sae_send_confirm = 0; - sme_send_authentication(wpa_s, bss, ssid, 1); -} - + struct wpa_connect_work *cwork = work->ctx; + struct wpa_supplicant *wpa_s = work->wpa_s; -#ifdef CONFIG_SAE + if (deinit) { + if (work->started) + wpa_s->connect_work = NULL; -static int sme_sae_process_commit(struct wpa_supplicant *wpa_s, const u8 *data, - size_t len) -{ - /* Check Finite Cyclic Group */ - if (len < 2) - return -1; - if (WPA_GET_LE16(data) != 19) { - wpa_printf(MSG_DEBUG, "SAE: Unsupported Finite Cyclic Group %u", - WPA_GET_LE16(data)); - return -1; + wpas_connect_work_free(cwork); + return; } - /* TODO */ + wpa_s->connect_work = work; - return 0; + if (cwork->bss_removed || + !wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: BSS/SSID entry for authentication not valid anymore - drop connection attempt"); + wpas_connect_work_done(wpa_s); + return; + } + + sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1); } -static int sme_sae_process_confirm(struct wpa_supplicant *wpa_s, const u8 *data, - size_t len) +void sme_authenticate(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, struct wpa_ssid *ssid) { - u16 rc; + struct wpa_connect_work *cwork; - if (len < 2) - return -1; - rc = WPA_GET_LE16(data); - wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", rc); + if (bss == NULL || ssid == NULL) + return; + if (wpa_s->connect_work) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reject sme_authenticate() call since connect_work exist"); + return; + } - /* TODO */ - return 0; + if (radio_work_pending(wpa_s, "sme-connect")) { + /* + * The previous sme-connect work might no longer be valid due to + * the fact that the BSS list was updated. In addition, it makes + * sense to adhere to the 'newer' decision. + */ + wpa_dbg(wpa_s, MSG_DEBUG, + "SME: Remove previous pending sme-connect"); + radio_remove_works(wpa_s, "sme-connect", 0); + } + + cwork = os_zalloc(sizeof(*cwork)); + if (cwork == NULL) + return; + cwork->bss = bss; + cwork->ssid = ssid; + cwork->sme = 1; + +#ifdef CONFIG_SAE + wpa_s->sme.sae.state = SAE_NOTHING; + wpa_s->sme.sae.send_confirm = 0; + wpa_s->sme.sae_group_index = 0; +#endif /* CONFIG_SAE */ + + if (radio_add_work(wpa_s, bss->freq, "sme-connect", 1, + sme_auth_start_cb, cwork) < 0) + wpas_connect_work_free(cwork); } +#ifdef CONFIG_SAE + static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, u16 status_code, const u8 *data, size_t len) { + int *groups; + wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE authentication transaction %u " "status code %u", auth_transaction, status_code); - wpa_hexdump(MSG_DEBUG, "SME: SAE fields", data, len); + + if (auth_transaction == 1 && + status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ && + wpa_s->sme.sae.state == SAE_COMMITTED && + wpa_s->current_bss && wpa_s->current_ssid) { + int default_groups[] = { 19, 20, 21, 25, 26, 0 }; + u16 group; + + groups = wpa_s->conf->sae_groups; + if (!groups || groups[0] <= 0) + groups = default_groups; + + if (len < sizeof(le16)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "SME: Too short SAE anti-clogging token request"); + return -1; + } + group = WPA_GET_LE16(data); + wpa_dbg(wpa_s, MSG_DEBUG, + "SME: SAE anti-clogging token requested (group %u)", + group); + if (sae_group_allowed(&wpa_s->sme.sae, groups, group) != + WLAN_STATUS_SUCCESS) { + wpa_dbg(wpa_s, MSG_ERROR, + "SME: SAE group %u of anti-clogging request is invalid", + group); + return -1; + } + wpabuf_free(wpa_s->sme.sae_token); + wpa_s->sme.sae_token = wpabuf_alloc_copy(data + sizeof(le16), + len - sizeof(le16)); + sme_send_authentication(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, 1); + return 0; + } + + if (auth_transaction == 1 && + status_code == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED && + wpa_s->sme.sae.state == SAE_COMMITTED && + wpa_s->current_bss && wpa_s->current_ssid) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE group not supported"); + wpa_s->sme.sae_group_index++; + if (sme_set_sae_group(wpa_s) < 0) + return -1; /* no other groups enabled */ + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Try next enabled SAE group"); + sme_send_authentication(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, 1); + return 0; + } if (status_code != WLAN_STATUS_SUCCESS) return -1; if (auth_transaction == 1) { + groups = wpa_s->conf->sae_groups; + wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit"); if (wpa_s->current_bss == NULL || wpa_s->current_ssid == NULL) return -1; - if (wpa_s->sme.sae_state != SME_SAE_COMMIT) + if (wpa_s->sme.sae.state != SAE_COMMITTED) + return -1; + if (groups && groups[0] <= 0) + groups = NULL; + if (sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL, + groups) != WLAN_STATUS_SUCCESS) return -1; - if (sme_sae_process_commit(wpa_s, data, len) < 0) + + if (sae_process_commit(&wpa_s->sme.sae) < 0) { + wpa_printf(MSG_DEBUG, "SAE: Failed to process peer " + "commit"); return -1; + } + + wpabuf_free(wpa_s->sme.sae_token); + wpa_s->sme.sae_token = NULL; sme_send_authentication(wpa_s, wpa_s->current_bss, wpa_s->current_ssid, 0); return 0; } else if (auth_transaction == 2) { wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm"); - if (wpa_s->sme.sae_state != SME_SAE_CONFIRM) + if (wpa_s->sme.sae.state != SAE_CONFIRMED) return -1; - if (sme_sae_process_confirm(wpa_s, data, len) < 0) + if (sae_check_confirm(&wpa_s->sme.sae, data, len) < 0) return -1; + wpa_s->sme.sae.state = SAE_ACCEPTED; + sae_clear_temp_data(&wpa_s->sme.sae); return 1; } @@ -499,6 +784,11 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) } if (res != 1) return; + + wpa_printf(MSG_DEBUG, "SME: SAE completed - setting PMK for " + "4-way handshake"); + wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN, + wpa_s->pending_bssid); } #endif /* CONFIG_SAE */ @@ -515,6 +805,8 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) return; } + wpas_connect_work_done(wpa_s); + switch (data->auth.auth_type) { case WLAN_AUTH_OPEN: wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_SHARED; @@ -562,19 +854,25 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, struct ieee80211_ht_capabilities htcaps; struct ieee80211_ht_capabilities htcaps_mask; #endif /* CONFIG_HT_OVERRIDES */ +#ifdef CONFIG_VHT_OVERRIDES + struct ieee80211_vht_capabilities vhtcaps; + struct ieee80211_vht_capabilities vhtcaps_mask; +#endif /* CONFIG_VHT_OVERRIDES */ os_memset(¶ms, 0, sizeof(params)); params.bssid = bssid; params.ssid = wpa_s->sme.ssid; params.ssid_len = wpa_s->sme.ssid_len; - params.freq = wpa_s->sme.freq; + params.freq.freq = wpa_s->sme.freq; params.bg_scan_period = wpa_s->current_ssid ? wpa_s->current_ssid->bg_scan_period : -1; 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); + params.pairwise_suite = wpa_s->pairwise_cipher; + params.group_suite = wpa_s->group_cipher; + params.key_mgmt_suite = wpa_s->key_mgmt; + params.wpa_proto = wpa_s->wpa_proto; #ifdef CONFIG_HT_OVERRIDES os_memset(&htcaps, 0, sizeof(htcaps)); os_memset(&htcaps_mask, 0, sizeof(htcaps_mask)); @@ -582,6 +880,13 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, params.htcaps_mask = (u8 *) &htcaps_mask; wpa_supplicant_apply_ht_overrides(wpa_s, wpa_s->current_ssid, ¶ms); #endif /* CONFIG_HT_OVERRIDES */ +#ifdef CONFIG_VHT_OVERRIDES + os_memset(&vhtcaps, 0, sizeof(vhtcaps)); + os_memset(&vhtcaps_mask, 0, sizeof(vhtcaps_mask)); + params.vhtcaps = &vhtcaps; + params.vhtcaps_mask = &vhtcaps_mask; + wpa_supplicant_apply_vht_overrides(wpa_s, wpa_s->current_ssid, ¶ms); +#endif /* CONFIG_VHT_OVERRIDES */ #ifdef CONFIG_IEEE80211R if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) { params.wpa_ie = wpa_s->sme.ft_ies; @@ -590,13 +895,14 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, #endif /* CONFIG_IEEE80211R */ params.mode = mode; params.mgmt_frame_protection = wpa_s->sme.mfp; + params.rrm_used = wpa_s->rrm.rrm_used; if (wpa_s->sme.prev_bssid_set) params.prev_bssid = wpa_s->sme.prev_bssid; wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR " (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid), params.ssid ? wpa_ssid_txt(params.ssid, params.ssid_len) : "", - params.freq); + params.freq.freq); wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING); @@ -614,6 +920,10 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, 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 if (elems.osen) { + params.wpa_proto = WPA_PROTO_OSEN; + wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.osen - 2, + elems.osen_len + 2); } else wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); if (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group) @@ -693,6 +1003,27 @@ void sme_event_assoc_reject(struct wpa_supplicant *wpa_s, eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); +#ifdef CONFIG_SAE + if (wpa_s->sme.sae_pmksa_caching && wpa_s->current_ssid && + wpa_key_mgmt_sae(wpa_s->current_ssid->key_mgmt)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "PMKSA caching attempt rejected - drop PMKSA cache entry and fall back to SAE authentication"); + wpa_sm_aborted_cached(wpa_s->wpa); + wpa_sm_pmksa_cache_flush(wpa_s->wpa, wpa_s->current_ssid); + if (wpa_s->current_bss) { + struct wpa_bss *bss = wpa_s->current_bss; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid, + WLAN_REASON_DEAUTH_LEAVING); + wpas_connect_work_done(wpa_s); + wpa_supplicant_mark_disassoc(wpa_s); + wpa_supplicant_connect(wpa_s, bss, ssid); + return; + } + } +#endif /* CONFIG_SAE */ + /* * For now, unconditionally terminate the previous authentication. In * theory, this should not be needed, but mac80211 gets quite confused @@ -723,7 +1054,7 @@ void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s, void sme_event_disassoc(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) + struct disassoc_info *info) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: Disassociation event received"); if (wpa_s->sme.prev_bssid_set) { @@ -793,6 +1124,21 @@ void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s, } +void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s) +{ + wpa_s->sme.prev_bssid_set = 0; +#ifdef CONFIG_SAE + wpabuf_free(wpa_s->sme.sae_token); + wpa_s->sme.sae_token = NULL; + sae_clear_data(&wpa_s->sme.sae); +#endif /* CONFIG_SAE */ +#ifdef CONFIG_IEEE80211R + if (wpa_s->sme.ft_ies) + sme_update_ft_ies(wpa_s, NULL, NULL, 0); +#endif /* CONFIG_IEEE80211R */ +} + + void sme_deinit(struct wpa_supplicant *wpa_s) { os_free(wpa_s->sme.ft_ies); @@ -801,6 +1147,7 @@ void sme_deinit(struct wpa_supplicant *wpa_s) #ifdef CONFIG_IEEE80211W sme_stop_sa_query(wpa_s); #endif /* CONFIG_IEEE80211W */ + sme_clear_on_disassoc(wpa_s); eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL); eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL); @@ -816,8 +1163,11 @@ static void sme_send_2040_bss_coex(struct wpa_supplicant *wpa_s, struct ieee80211_2040_intol_chan_report *ic_report; struct wpabuf *buf; - wpa_printf(MSG_DEBUG, "SME: Send 20/40 BSS Coexistence to " MACSTR, - MAC2STR(wpa_s->bssid)); + wpa_printf(MSG_DEBUG, "SME: Send 20/40 BSS Coexistence to " MACSTR + " (num_channels=%u num_intol=%u)", + MAC2STR(wpa_s->bssid), num_channels, num_intol); + wpa_hexdump(MSG_DEBUG, "SME: 20/40 BSS Intolerant Channels", + chan_list, num_channels); buf = wpabuf_alloc(2 + /* action.category + action_code */ sizeof(struct ieee80211_2040_bss_coex_ie) + @@ -855,39 +1205,6 @@ static void sme_send_2040_bss_coex(struct wpa_supplicant *wpa_s, } -/** - * enum wpas_band - Frequency band - * @WPAS_BAND_2GHZ: 2.4 GHz ISM band - * @WPAS_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz) - */ -enum wpas_band { - WPAS_BAND_2GHZ, - WPAS_BAND_5GHZ, - WPAS_BAND_INVALID -}; - -/** - * freq_to_channel - Convert frequency into channel info - * @channel: Buffer for returning channel number - * Returns: Band (2 or 5 GHz) - */ -static enum wpas_band freq_to_channel(int freq, u8 *channel) -{ - enum wpas_band band = (freq <= 2484) ? WPAS_BAND_2GHZ : WPAS_BAND_5GHZ; - u8 chan = 0; - - if (freq >= 2412 && freq <= 2472) - chan = (freq - 2407) / 5; - else if (freq == 2484) - chan = 14; - else if (freq >= 5180 && freq <= 5805) - chan = (freq - 5000) / 5; - - *channel = chan; - return band; -} - - int sme_proc_obss_scan(struct wpa_supplicant *wpa_s) { struct wpa_bss *bss; @@ -924,13 +1241,22 @@ int sme_proc_obss_scan(struct wpa_supplicant *wpa_s) dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { /* Skip other band bss */ - if (freq_to_channel(bss->freq, &channel) != WPAS_BAND_2GHZ) + enum hostapd_hw_mode mode; + mode = ieee80211_freq_to_chan(bss->freq, &channel); + if (mode != HOSTAPD_MODE_IEEE80211G && + mode != HOSTAPD_MODE_IEEE80211B) continue; ie = wpa_bss_get_ie(bss, WLAN_EID_HT_CAP); ht_cap = (ie && (ie[1] == 26)) ? WPA_GET_LE16(ie + 2) : 0; + wpa_printf(MSG_DEBUG, "SME OBSS scan BSS " MACSTR + " freq=%u chan=%u ht_cap=0x%x", + MAC2STR(bss->bssid), bss->freq, channel, ht_cap); if (!ht_cap || (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)) { + if (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT) + num_intol++; + /* Check whether the channel is already considered */ for (i = 0; i < num_channels; i++) { if (channel == chan_list[i]) @@ -939,9 +1265,6 @@ int sme_proc_obss_scan(struct wpa_supplicant *wpa_s) if (i != num_channels) continue; - if (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT) - num_intol++; - chan_list[num_channels++] = channel; } } @@ -966,28 +1289,72 @@ static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes, } -static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s, - enum hostapd_hw_mode band, - struct wpa_driver_scan_params *params) +static void wpa_obss_scan_freqs_list(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params) { - /* Include only supported channels for the specified band */ + /* Include only affected channels */ struct hostapd_hw_modes *mode; int count, i; + int start, end; - mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band); + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, + HOSTAPD_MODE_IEEE80211G); if (mode == NULL) { /* No channels supported in this band - use empty list */ params->freqs = os_zalloc(sizeof(int)); return; } + if (wpa_s->sme.ht_sec_chan == HT_SEC_CHAN_UNKNOWN && + wpa_s->current_bss) { + const u8 *ie; + + ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_HT_OPERATION); + if (ie && ie[1] >= 2) { + u8 o; + + o = ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; + if (o == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) + wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_ABOVE; + else if (o == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) + wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_BELOW; + } + } + + start = wpa_s->assoc_freq - 10; + end = wpa_s->assoc_freq + 10; + switch (wpa_s->sme.ht_sec_chan) { + case HT_SEC_CHAN_UNKNOWN: + /* HT40+ possible on channels 1..9 */ + if (wpa_s->assoc_freq <= 2452) + start -= 20; + /* HT40- possible on channels 5-13 */ + if (wpa_s->assoc_freq >= 2432) + end += 20; + break; + case HT_SEC_CHAN_ABOVE: + end += 20; + break; + case HT_SEC_CHAN_BELOW: + start -= 20; + break; + } + wpa_printf(MSG_DEBUG, + "OBSS: assoc_freq %d possible affected range %d-%d", + wpa_s->assoc_freq, start, end); + params->freqs = os_calloc(mode->num_channels + 1, sizeof(int)); if (params->freqs == NULL) return; for (count = 0, i = 0; i < mode->num_channels; i++) { + int freq; + if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED) continue; - params->freqs[count++] = mode->channels[i].freq; + freq = mode->channels[i].freq; + if (freq - 10 >= end || freq + 10 <= start) + continue; /* not affected */ + params->freqs[count++] = freq; } } @@ -1003,7 +1370,8 @@ static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx) } os_memset(¶ms, 0, sizeof(params)); - wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, ¶ms); + wpa_obss_scan_freqs_list(wpa_s, ¶ms); + params.low_priority = 1; wpa_printf(MSG_DEBUG, "SME OBSS: Request an OBSS scan"); if (wpa_supplicant_trigger_scan(wpa_s, ¶ms)) @@ -1027,6 +1395,7 @@ void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable) eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL); wpa_s->sme.sched_obss_scan = 0; + wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_UNKNOWN; if (!enable) return; @@ -1090,9 +1459,9 @@ 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); + struct os_reltime now, passed; + os_get_reltime(&now); + os_reltime_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"); @@ -1142,13 +1511,16 @@ static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx) return; if (wpa_s->sme.sa_query_count == 0) { /* Starting a new SA Query procedure */ - os_get_time(&wpa_s->sme.sa_query_start); + os_get_reltime(&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); + if (os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) { + wpa_printf(MSG_DEBUG, "Could not generate SA Query ID"); + return; + } timeout = sa_query_retry_timeout; sec = ((timeout / 1000) * 1024) / 1000; @@ -1181,15 +1553,12 @@ void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *da, u16 reason_code) { struct wpa_ssid *ssid; + struct os_reltime now; - 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 == MGMT_FRAME_PROTECTION_DEFAULT ? - wpa_s->conf->pmf : ssid->ieee80211w) == NO_MGMT_FRAME_PROTECTION) + if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION) return; if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) return; @@ -1199,6 +1568,12 @@ void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa, if (wpa_s->sme.sa_query_count > 0) return; + os_get_reltime(&now); + if (wpa_s->sme.last_unprot_disconnect.sec && + !os_reltime_expired(&now, &wpa_s->sme.last_unprot_disconnect, 10)) + return; /* limit SA Query procedure frequency */ + wpa_s->sme.last_unprot_disconnect = now; + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Unprotected disconnect dropped - " "possible AP/STA state mismatch - trigger SA Query"); sme_start_sa_query(wpa_s); diff --git a/wpa_supplicant/sme.h b/wpa_supplicant/sme.h index a7cc507e67bd8..fd5c3b4e1ed83 100644 --- a/wpa_supplicant/sme.h +++ b/wpa_supplicant/sme.h @@ -25,7 +25,7 @@ void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s, 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); + struct disassoc_info *info); 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, @@ -33,6 +33,7 @@ void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa, 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_clear_on_disassoc(struct wpa_supplicant *wpa_s); void sme_deinit(struct wpa_supplicant *wpa_s); int sme_proc_obss_scan(struct wpa_supplicant *wpa_s); @@ -74,7 +75,7 @@ static inline void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s, } static inline void sme_event_disassoc(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) + struct disassoc_info *info) { } @@ -94,6 +95,10 @@ sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s, { } +static inline void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s) +{ +} + static inline void sme_deinit(struct wpa_supplicant *wpa_s) { } diff --git a/wpa_supplicant/tests/test_wpa.c b/wpa_supplicant/tests/test_wpa.c index 0d659adaf361e..39971f285de34 100644 --- a/wpa_supplicant/tests/test_wpa.c +++ b/wpa_supplicant/tests/test_wpa.c @@ -14,11 +14,7 @@ #include "../config.h" #include "rsn_supp/wpa.h" #include "rsn_supp/wpa_ie.h" -#include "../hostapd/wpa.h" - - -extern int wpa_debug_level; -extern int wpa_debug_show_keys; +#include "ap/wpa_auth.h" struct wpa { @@ -298,7 +294,7 @@ static int auth_init_group(struct wpa *wpa) static int auth_init(struct wpa *wpa) { - wpa->auth = wpa_auth_sta_init(wpa->auth_group, wpa->supp_addr); + wpa->auth = wpa_auth_sta_init(wpa->auth_group, wpa->supp_addr, NULL); if (wpa->auth == NULL) { wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed"); return -1; diff --git a/wpa_supplicant/todo.txt b/wpa_supplicant/todo.txt index b84cccc95e75e..4c9f98e9c7aba 100644 --- a/wpa_supplicant/todo.txt +++ b/wpa_supplicant/todo.txt @@ -5,8 +5,6 @@ To do: authentication has been completed (cache scard data based on serial#(?) and try to optimize next connection if the same card is present for next auth) -- on disconnect event, could try to associate with another AP if one is - present in scan results; would need to update scan results periodically.. - if driver/hw is not WPA2 capable, must remove WPA_PROTO_RSN flag from ssid->proto fields to avoid detecting downgrade attacks when the driver is not reporting RSN IE, but msg 3/4 has one @@ -24,14 +22,12 @@ To do: RFC 3748 Sect. 4.2 - test compilation with gcc -W options (more warnings?) (Done once; number of unused function arguments still present) -- add proper support for using dot11RSNAConfigSATimeout -- ctrl_iface: get/set/remove blob +- ctrl_iface: get/remove blob - use doc/docbook/*.sgml and docbook2{txt,html,pdf} to replace README and web pages including the same information.. i.e., have this information only in one page; how to build a PDF file with all the SGML included? - EAP-POTP/RSA SecurID profile (RFC 4793) - document wpa_gui build and consider adding it to 'make install' -- test madwifi with pairwise=TKIP group=WEP104 - consider merging hostapd and wpa_supplicant PMKSA cache implementations - consider redesigning pending EAP requests (identity/password/otp from ctrl_iface) by moving the retrying of the previous request into EAP @@ -57,14 +53,11 @@ To do: - try to work around race in configuring PTK and sending msg 4/4 (some NDIS drivers with ndiswrapper end up not being able to complete 4-way handshake in some cases; extra delay before setting the key seems to help) -- add wpa_secure_memzero() macro and secure implementation (volatile u8*) to - clear memory; this would be used to clear temporary buffers containing - private data (e.g., keys); the macro can be defined to NOP in order to save - space (i.e., no code should depend on the macro doing something) - make sure that TLS session cache is not shared between EAP types or if it is, that the cache entries are bound to only one EAP type; e.g., cache entry created with EAP-TLS must not be allowed to do fast re-auth with EAP-TTLS -- consider moving eap_tls_build_ack() call into eap_tls_process_helper() +- consider moving eap_peer_tls_build_ack() call into + eap_peer_tls_process_helper() (it seems to be called always if helper returns 1) * could need to modify eap_{ttls,peap,fast}_decrypt to do same - add support for fetching full user cert chain from Windows certificate diff --git a/wpa_supplicant/wifi_display.c b/wpa_supplicant/wifi_display.c index 92ca5360e296f..c363b21b92b16 100644 --- a/wpa_supplicant/wifi_display.c +++ b/wpa_supplicant/wifi_display.c @@ -16,6 +16,9 @@ #include "wifi_display.h" +#define WIFI_DISPLAY_SUBELEM_HEADER_LEN 3 + + int wifi_display_init(struct wpa_global *global) { global->wifi_display = 1; @@ -33,11 +36,42 @@ void wifi_display_deinit(struct wpa_global *global) } +struct wpabuf * wifi_display_get_wfd_ie(struct wpa_global *global) +{ + struct wpabuf *ie; + size_t len; + int i; + + if (global->p2p == NULL) + return NULL; + + len = 0; + for (i = 0; i < MAX_WFD_SUBELEMS; i++) { + if (global->wfd_subelem[i]) + len += wpabuf_len(global->wfd_subelem[i]); + } + + ie = wpabuf_alloc(len); + if (ie == NULL) + return NULL; + + for (i = 0; i < MAX_WFD_SUBELEMS; i++) { + if (global->wfd_subelem[i]) + wpabuf_put_buf(ie, global->wfd_subelem[i]); + } + + return ie; +} + + static int wifi_display_update_wfd_ie(struct wpa_global *global) { struct wpabuf *ie, *buf; size_t len, plen; + if (global->p2p == NULL) + return 0; + wpa_printf(MSG_DEBUG, "WFD: Update WFD IE"); if (!global->wifi_display) { @@ -199,15 +233,31 @@ int wifi_display_subelem_set(struct wpa_global *global, char *cmd) if (pos == NULL) return -1; *pos++ = '\0'; - subelem = atoi(cmd); - if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS) - return -1; len = os_strlen(pos); if (len & 1) return -1; len /= 2; + if (os_strcmp(cmd, "all") == 0) { + int res; + + e = wpabuf_alloc(len); + if (e == NULL) + return -1; + if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) { + wpabuf_free(e); + return -1; + } + res = wifi_display_subelem_set_from_ies(global, e); + wpabuf_free(e); + return res; + } + + subelem = atoi(cmd); + if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS) + return -1; + if (len == 0) { /* Clear subelement */ e = NULL; @@ -232,11 +282,78 @@ int wifi_display_subelem_set(struct wpa_global *global, char *cmd) } +int wifi_display_subelem_set_from_ies(struct wpa_global *global, + struct wpabuf *ie) +{ + int subelements[MAX_WFD_SUBELEMS] = {}; + const u8 *pos, *end; + unsigned int len, subelem; + struct wpabuf *e; + + wpa_printf(MSG_DEBUG, "WFD IEs set: %p - %lu", + ie, ie ? (unsigned long) wpabuf_len(ie) : 0); + + if (ie == NULL || wpabuf_len(ie) < 6) + return -1; + + pos = wpabuf_head(ie); + end = pos + wpabuf_len(ie); + + while (end > pos) { + if (pos + 3 > end) + break; + + len = WPA_GET_BE16(pos + 1) + 3; + + wpa_printf(MSG_DEBUG, "WFD Sub-Element ID %d - len %d", + *pos, len - 3); + + if (len > (unsigned int) (end - pos)) + break; + + subelem = *pos; + if (subelem < MAX_WFD_SUBELEMS && subelements[subelem] == 0) { + e = wpabuf_alloc_copy(pos, len); + if (e == NULL) + return -1; + + wpabuf_free(global->wfd_subelem[subelem]); + global->wfd_subelem[subelem] = e; + subelements[subelem] = 1; + } + + pos += len; + } + + for (subelem = 0; subelem < MAX_WFD_SUBELEMS; subelem++) { + if (subelements[subelem] == 0) { + wpabuf_free(global->wfd_subelem[subelem]); + global->wfd_subelem[subelem] = NULL; + } + } + + return wifi_display_update_wfd_ie(global); +} + + int wifi_display_subelem_get(struct wpa_global *global, char *cmd, char *buf, size_t buflen) { int subelem; + if (os_strcmp(cmd, "all") == 0) { + struct wpabuf *ie; + int res; + + ie = wifi_display_get_wfd_ie(global); + if (ie == NULL) + return 0; + res = wpa_snprintf_hex(buf, buflen, wpabuf_head(ie), + wpabuf_len(ie)); + wpabuf_free(ie); + return res; + } + subelem = atoi(cmd); if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS) return -1; @@ -249,3 +366,53 @@ int wifi_display_subelem_get(struct wpa_global *global, char *cmd, 1, wpabuf_len(global->wfd_subelem[subelem]) - 1); } + + +char * wifi_display_subelem_hex(const struct wpabuf *wfd_subelems, u8 id) +{ + char *subelem = NULL; + const u8 *buf; + size_t buflen; + size_t i = 0; + u16 elen; + + if (!wfd_subelems) + return NULL; + + buf = wpabuf_head_u8(wfd_subelems); + if (!buf) + return NULL; + + buflen = wpabuf_len(wfd_subelems); + + while (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN < buflen) { + elen = WPA_GET_BE16(buf + i + 1); + if (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN + elen > buflen) + break; /* truncated subelement */ + + if (buf[i] == id) { + /* + * Limit explicitly to an arbitrary length to avoid + * unnecessarily large allocations. In practice, this + * is limited to maximum frame length anyway, so the + * maximum memory allocation here is not really that + * large. Anyway, the Wi-Fi Display subelements that + * are fetched with this function are even shorter. + */ + if (elen > 1000) + break; + subelem = os_zalloc(2 * elen + 1); + if (!subelem) + return NULL; + wpa_snprintf_hex(subelem, 2 * elen + 1, + buf + i + + WIFI_DISPLAY_SUBELEM_HEADER_LEN, + elen); + break; + } + + i += elen + WIFI_DISPLAY_SUBELEM_HEADER_LEN; + } + + return subelem; +} diff --git a/wpa_supplicant/wifi_display.h b/wpa_supplicant/wifi_display.h index b75d4f2dbfef5..0966bdb93befc 100644 --- a/wpa_supplicant/wifi_display.h +++ b/wpa_supplicant/wifi_display.h @@ -13,8 +13,12 @@ int wifi_display_init(struct wpa_global *global); void wifi_display_deinit(struct wpa_global *global); void wifi_display_enable(struct wpa_global *global, int enabled); +struct wpabuf *wifi_display_get_wfd_ie(struct wpa_global *global); int wifi_display_subelem_set(struct wpa_global *global, char *cmd); +int wifi_display_subelem_set_from_ies(struct wpa_global *global, + struct wpabuf *ie); int wifi_display_subelem_get(struct wpa_global *global, char *cmd, char *buf, size_t buflen); +char * wifi_display_subelem_hex(const struct wpabuf *wfd_subelems, u8 id); #endif /* WIFI_DISPLAY_H */ diff --git a/wpa_supplicant/wmm_ac.c b/wpa_supplicant/wmm_ac.c new file mode 100644 index 0000000000000..5625d36638b5c --- /dev/null +++ b/wpa_supplicant/wmm_ac.c @@ -0,0 +1,995 @@ +/* + * Wi-Fi Multimedia Admission Control (WMM-AC) + * Copyright(c) 2014, Intel Mobile Communication GmbH. + * Copyright(c) 2014, Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "utils/common.h" +#include "utils/list.h" +#include "utils/eloop.h" +#include "common/ieee802_11_common.h" +#include "wpa_supplicant_i.h" +#include "bss.h" +#include "driver_i.h" +#include "wmm_ac.h" + +static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx); + +static const enum wmm_ac up_to_ac[8] = { + WMM_AC_BK, + WMM_AC_BE, + WMM_AC_BE, + WMM_AC_BK, + WMM_AC_VI, + WMM_AC_VI, + WMM_AC_VO, + WMM_AC_VO +}; + + +static inline u8 wmm_ac_get_tsid(const struct wmm_tspec_element *tspec) +{ + return (tspec->ts_info[0] >> 1) & 0x0f; +} + + +static u8 wmm_ac_get_direction(const struct wmm_tspec_element *tspec) +{ + return (tspec->ts_info[0] >> 5) & 0x03; +} + + +static u8 wmm_ac_get_user_priority(const struct wmm_tspec_element *tspec) +{ + return (tspec->ts_info[1] >> 3) & 0x07; +} + + +static u8 wmm_ac_direction_to_idx(u8 direction) +{ + switch (direction) { + case WMM_AC_DIR_UPLINK: + return TS_DIR_IDX_UPLINK; + case WMM_AC_DIR_DOWNLINK: + return TS_DIR_IDX_DOWNLINK; + case WMM_AC_DIR_BIDIRECTIONAL: + return TS_DIR_IDX_BIDI; + default: + wpa_printf(MSG_ERROR, "Invalid direction: %d", direction); + return WMM_AC_DIR_UPLINK; + } +} + + +static int wmm_ac_add_ts(struct wpa_supplicant *wpa_s, const u8 *addr, + const struct wmm_tspec_element *tspec) +{ + struct wmm_tspec_element *_tspec; + int ret; + u16 admitted_time = le_to_host16(tspec->medium_time); + u8 up = wmm_ac_get_user_priority(tspec); + u8 ac = up_to_ac[up]; + u8 dir = wmm_ac_get_direction(tspec); + u8 tsid = wmm_ac_get_tsid(tspec); + enum ts_dir_idx idx = wmm_ac_direction_to_idx(dir); + + /* should have been verified before, but double-check here */ + if (wpa_s->tspecs[ac][idx]) { + wpa_printf(MSG_ERROR, + "WMM AC: tspec (ac=%d, dir=%d) already exists!", + ac, dir); + return -1; + } + + /* copy tspec */ + _tspec = os_malloc(sizeof(*_tspec)); + if (!_tspec) + return -1; + + /* store the admitted TSPEC */ + os_memcpy(_tspec, tspec, sizeof(*_tspec)); + + if (dir != WMM_AC_DIR_DOWNLINK) { + ret = wpa_drv_add_ts(wpa_s, tsid, addr, up, admitted_time); + wpa_printf(MSG_DEBUG, + "WMM AC: Add TS: addr=" MACSTR + " TSID=%u admitted time=%u, ret=%d", + MAC2STR(addr), tsid, admitted_time, ret); + if (ret < 0) { + os_free(_tspec); + return -1; + } + } + + wpa_s->tspecs[ac][idx] = _tspec; + + wpa_printf(MSG_DEBUG, "Traffic stream was created successfully"); + + wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_ADDED + "tsid=%d addr=" MACSTR " admitted_time=%d", + tsid, MAC2STR(addr), admitted_time); + + return 0; +} + + +static void wmm_ac_del_ts_idx(struct wpa_supplicant *wpa_s, u8 ac, + enum ts_dir_idx dir) +{ + struct wmm_tspec_element *tspec = wpa_s->tspecs[ac][dir]; + u8 tsid; + + if (!tspec) + return; + + tsid = wmm_ac_get_tsid(tspec); + wpa_printf(MSG_DEBUG, "WMM AC: Del TS ac=%d tsid=%d", ac, tsid); + + /* update the driver in case of uplink/bidi */ + if (wmm_ac_get_direction(tspec) != WMM_AC_DIR_DOWNLINK) + wpa_drv_del_ts(wpa_s, tsid, wpa_s->bssid); + + wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REMOVED + "tsid=%d addr=" MACSTR, tsid, MAC2STR(wpa_s->bssid)); + + os_free(wpa_s->tspecs[ac][dir]); + wpa_s->tspecs[ac][dir] = NULL; +} + + +static void wmm_ac_del_req(struct wpa_supplicant *wpa_s, int failed) +{ + struct wmm_ac_addts_request *req = wpa_s->addts_request; + + if (!req) + return; + + if (failed) + wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED + "tsid=%u", wmm_ac_get_tsid(&req->tspec)); + + eloop_cancel_timeout(wmm_ac_addts_req_timeout, wpa_s, req); + wpa_s->addts_request = NULL; + os_free(req); +} + + +static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wmm_ac_addts_request *addts_req = timeout_ctx; + + wpa_printf(MSG_DEBUG, + "Timeout getting ADDTS response (tsid=%d up=%d)", + wmm_ac_get_tsid(&addts_req->tspec), + wmm_ac_get_user_priority(&addts_req->tspec)); + + wmm_ac_del_req(wpa_s, 1); +} + + +static int wmm_ac_send_addts_request(struct wpa_supplicant *wpa_s, + const struct wmm_ac_addts_request *req) +{ + struct wpabuf *buf; + int ret; + + wpa_printf(MSG_DEBUG, "Sending ADDTS Request to " MACSTR, + MAC2STR(req->address)); + + /* category + action code + dialog token + status + sizeof(tspec) */ + buf = wpabuf_alloc(4 + sizeof(req->tspec)); + if (!buf) { + wpa_printf(MSG_ERROR, "WMM AC: Allocation error"); + return -1; + } + + wpabuf_put_u8(buf, WLAN_ACTION_WMM); + wpabuf_put_u8(buf, WMM_ACTION_CODE_ADDTS_REQ); + wpabuf_put_u8(buf, req->dialog_token); + wpabuf_put_u8(buf, 0); /* status code */ + wpabuf_put_data(buf, &req->tspec, sizeof(req->tspec)); + + ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, req->address, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0); + if (ret) { + wpa_printf(MSG_WARNING, + "WMM AC: Failed to send ADDTS Request"); + } + + wpabuf_free(buf); + return ret; +} + + +static int wmm_ac_send_delts(struct wpa_supplicant *wpa_s, + const struct wmm_tspec_element *tspec, + const u8 *address) +{ + struct wpabuf *buf; + int ret; + + /* category + action code + dialog token + status + sizeof(tspec) */ + buf = wpabuf_alloc(4 + sizeof(*tspec)); + if (!buf) + return -1; + + wpa_printf(MSG_DEBUG, "Sending DELTS to " MACSTR, MAC2STR(address)); + + /* category + action code + dialog token + status + sizeof(tspec) */ + wpabuf_put_u8(buf, WLAN_ACTION_WMM); + wpabuf_put_u8(buf, WMM_ACTION_CODE_DELTS); + wpabuf_put_u8(buf, 0); /* Dialog Token (not used) */ + wpabuf_put_u8(buf, 0); /* Status Code (not used) */ + wpabuf_put_data(buf, tspec, sizeof(*tspec)); + + ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, address, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0); + if (ret) + wpa_printf(MSG_WARNING, "Failed to send DELTS frame"); + + wpabuf_free(buf); + return ret; +} + + +/* return the AC using the given TSPEC tid */ +static int wmm_ac_find_tsid(struct wpa_supplicant *wpa_s, u8 tsid, + enum ts_dir_idx *dir) +{ + int ac; + enum ts_dir_idx idx; + + for (ac = 0; ac < WMM_AC_NUM; ac++) { + for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { + if (wpa_s->tspecs[ac][idx] && + wmm_ac_get_tsid(wpa_s->tspecs[ac][idx]) == tsid) { + if (dir) + *dir = idx; + return ac; + } + } + } + + return -1; +} + + +static struct wmm_ac_addts_request * +wmm_ac_build_addts_req(struct wpa_supplicant *wpa_s, + const struct wmm_ac_ts_setup_params *params, + const u8 *address) +{ + struct wmm_ac_addts_request *addts_req; + struct wmm_tspec_element *tspec; + u8 ac = up_to_ac[params->user_priority]; + u8 uapsd = wpa_s->wmm_ac_assoc_info->ac_params[ac].uapsd; + + addts_req = os_zalloc(sizeof(*addts_req)); + if (!addts_req) + return NULL; + + tspec = &addts_req->tspec; + os_memcpy(addts_req->address, address, ETH_ALEN); + + /* The dialog token cannot be zero */ + if (++wpa_s->wmm_ac_last_dialog_token == 0) + wpa_s->wmm_ac_last_dialog_token++; + + addts_req->dialog_token = wpa_s->wmm_ac_last_dialog_token; + tspec->eid = WLAN_EID_VENDOR_SPECIFIC; + tspec->length = sizeof(*tspec) - 2; /* reduce eid and length */ + tspec->oui[0] = 0x00; + tspec->oui[1] = 0x50; + tspec->oui[2] = 0xf2; + tspec->oui_type = WMM_OUI_TYPE; + tspec->oui_subtype = WMM_OUI_SUBTYPE_TSPEC_ELEMENT; + tspec->version = WMM_VERSION; + + tspec->ts_info[0] = params->tsid << 1; + tspec->ts_info[0] |= params->direction << 5; + tspec->ts_info[0] |= WMM_AC_ACCESS_POLICY_EDCA << 7; + tspec->ts_info[1] = uapsd << 2; + tspec->ts_info[1] |= params->user_priority << 3; + tspec->ts_info[2] = 0; + + tspec->nominal_msdu_size = host_to_le16(params->nominal_msdu_size); + if (params->fixed_nominal_msdu) + tspec->nominal_msdu_size |= + host_to_le16(WMM_AC_FIXED_MSDU_SIZE); + + tspec->mean_data_rate = host_to_le32(params->mean_data_rate); + tspec->minimum_phy_rate = host_to_le32(params->minimum_phy_rate); + tspec->surplus_bandwidth_allowance = + host_to_le16(params->surplus_bandwidth_allowance); + + return addts_req; +} + + +static int param_in_range(const char *name, long value, + long min_val, long max_val) +{ + if (value < min_val || (max_val >= 0 && value > max_val)) { + wpa_printf(MSG_DEBUG, + "WMM AC: param %s (%ld) is out of range (%ld-%ld)", + name, value, min_val, max_val); + return 0; + } + + return 1; +} + + +static int wmm_ac_should_replace_ts(struct wpa_supplicant *wpa_s, + u8 tsid, u8 ac, u8 dir) +{ + enum ts_dir_idx idx; + int cur_ac, existing_ts = 0, replace_ts = 0; + + cur_ac = wmm_ac_find_tsid(wpa_s, tsid, &idx); + if (cur_ac >= 0) { + if (cur_ac != ac) { + wpa_printf(MSG_DEBUG, + "WMM AC: TSID %i already exists on different ac (%d)", + tsid, cur_ac); + return -1; + } + + /* same tsid - this tspec will replace the current one */ + replace_ts |= BIT(idx); + } + + for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { + if (wpa_s->tspecs[ac][idx]) + existing_ts |= BIT(idx); + } + + switch (dir) { + case WMM_AC_DIR_UPLINK: + /* replace existing uplink/bidi tspecs */ + replace_ts |= existing_ts & (BIT(TS_DIR_IDX_UPLINK) | + BIT(TS_DIR_IDX_BIDI)); + break; + case WMM_AC_DIR_DOWNLINK: + /* replace existing downlink/bidi tspecs */ + replace_ts |= existing_ts & (BIT(TS_DIR_IDX_DOWNLINK) | + BIT(TS_DIR_IDX_BIDI)); + break; + case WMM_AC_DIR_BIDIRECTIONAL: + /* replace all existing tspecs */ + replace_ts |= existing_ts; + break; + default: + return -1; + } + + return replace_ts; +} + + +static int wmm_ac_ts_req_is_valid(struct wpa_supplicant *wpa_s, + const struct wmm_ac_ts_setup_params *params) +{ + enum wmm_ac req_ac; + +#define PARAM_IN_RANGE(field, min_value, max_value) \ + param_in_range(#field, params->field, min_value, max_value) + + if (!PARAM_IN_RANGE(tsid, 0, WMM_AC_MAX_TID) || + !PARAM_IN_RANGE(user_priority, 0, WMM_AC_MAX_USER_PRIORITY) || + !PARAM_IN_RANGE(nominal_msdu_size, 1, WMM_AC_MAX_NOMINAL_MSDU) || + !PARAM_IN_RANGE(mean_data_rate, 1, -1) || + !PARAM_IN_RANGE(minimum_phy_rate, 1, -1) || + !PARAM_IN_RANGE(surplus_bandwidth_allowance, WMM_AC_MIN_SBA_UNITY, + -1)) + return 0; +#undef PARAM_IN_RANGE + + if (!(params->direction == WMM_TSPEC_DIRECTION_UPLINK || + params->direction == WMM_TSPEC_DIRECTION_DOWNLINK || + params->direction == WMM_TSPEC_DIRECTION_BI_DIRECTIONAL)) { + wpa_printf(MSG_DEBUG, "WMM AC: invalid TS direction: %d", + params->direction); + return 0; + } + + req_ac = up_to_ac[params->user_priority]; + + /* Requested accesss category must have acm */ + if (!wpa_s->wmm_ac_assoc_info->ac_params[req_ac].acm) { + wpa_printf(MSG_DEBUG, "WMM AC: AC %d is not ACM", req_ac); + return 0; + } + + if (wmm_ac_should_replace_ts(wpa_s, params->tsid, req_ac, + params->direction) < 0) + return 0; + + return 1; +} + + +static struct wmm_ac_assoc_data * +wmm_ac_process_param_elem(struct wpa_supplicant *wpa_s, const u8 *ies, + size_t ies_len) +{ + struct ieee802_11_elems elems; + struct wmm_parameter_element *wmm_params; + struct wmm_ac_assoc_data *assoc_data; + int i; + + /* Parsing WMM Parameter Element */ + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, "WMM AC: could not parse assoc ies"); + return NULL; + } + + if (!elems.wmm) { + wpa_printf(MSG_DEBUG, "WMM AC: No WMM IE"); + return NULL; + } + + if (elems.wmm_len != sizeof(*wmm_params)) { + wpa_printf(MSG_DEBUG, "WMM AC: Invalid WMM ie length"); + return NULL; + } + + wmm_params = (struct wmm_parameter_element *)(elems.wmm); + + assoc_data = os_zalloc(sizeof(*assoc_data)); + if (!assoc_data) + return NULL; + + for (i = 0; i < WMM_AC_NUM; i++) + assoc_data->ac_params[i].acm = + !!(wmm_params->ac[i].aci_aifsn & WMM_AC_ACM); + + wpa_printf(MSG_DEBUG, + "WMM AC: AC mandatory: AC_BE=%u AC_BK=%u AC_VI=%u AC_VO=%u", + assoc_data->ac_params[WMM_AC_BE].acm, + assoc_data->ac_params[WMM_AC_BK].acm, + assoc_data->ac_params[WMM_AC_VI].acm, + assoc_data->ac_params[WMM_AC_VO].acm); + + return assoc_data; +} + + +static int wmm_ac_init(struct wpa_supplicant *wpa_s, const u8 *ies, + size_t ies_len, const struct wmm_params *wmm_params) +{ + struct wmm_ac_assoc_data *assoc_data; + u8 ac; + + if (wpa_s->wmm_ac_assoc_info) { + wpa_printf(MSG_ERROR, "WMM AC: Already initialized"); + return -1; + } + + if (!ies) { + wpa_printf(MSG_ERROR, "WMM AC: Missing IEs"); + return -1; + } + + if (!(wmm_params->info_bitmap & WMM_PARAMS_UAPSD_QUEUES_INFO)) { + wpa_printf(MSG_DEBUG, "WMM AC: Missing U-APSD configuration"); + return -1; + } + + os_memset(wpa_s->tspecs, 0, sizeof(wpa_s->tspecs)); + wpa_s->wmm_ac_last_dialog_token = 0; + wpa_s->addts_request = NULL; + + assoc_data = wmm_ac_process_param_elem(wpa_s, ies, ies_len); + if (!assoc_data) + return -1; + + wpa_printf(MSG_DEBUG, "WMM AC: U-APSD queues=0x%x", + wmm_params->uapsd_queues); + + for (ac = 0; ac < WMM_AC_NUM; ac++) { + assoc_data->ac_params[ac].uapsd = + !!(wmm_params->uapsd_queues & BIT(ac)); + } + + wpa_s->wmm_ac_assoc_info = assoc_data; + return 0; +} + + +static void wmm_ac_del_ts(struct wpa_supplicant *wpa_s, u8 ac, int dir_bitmap) +{ + enum ts_dir_idx idx; + + for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { + if (!(dir_bitmap & BIT(idx))) + continue; + + wmm_ac_del_ts_idx(wpa_s, ac, idx); + } +} + + +static void wmm_ac_deinit(struct wpa_supplicant *wpa_s) +{ + int i; + + for (i = 0; i < WMM_AC_NUM; i++) + wmm_ac_del_ts(wpa_s, i, TS_DIR_IDX_ALL); + + /* delete pending add_ts requset */ + wmm_ac_del_req(wpa_s, 1); + + os_free(wpa_s->wmm_ac_assoc_info); + wpa_s->wmm_ac_assoc_info = NULL; +} + + +void wmm_ac_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *ies, + size_t ies_len, const struct wmm_params *wmm_params) +{ + if (wmm_ac_init(wpa_s, ies, ies_len, wmm_params)) + return; + + wpa_printf(MSG_DEBUG, + "WMM AC: Valid WMM association, WMM AC is enabled"); +} + + +void wmm_ac_notify_disassoc(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->wmm_ac_assoc_info) + return; + + wmm_ac_deinit(wpa_s); + wpa_printf(MSG_DEBUG, "WMM AC: WMM AC is disabled"); +} + + +int wpas_wmm_ac_delts(struct wpa_supplicant *wpa_s, u8 tsid) +{ + struct wmm_tspec_element tspec; + int ac; + enum ts_dir_idx dir; + + if (!wpa_s->wmm_ac_assoc_info) { + wpa_printf(MSG_DEBUG, + "WMM AC: Failed to delete TS, WMM AC is disabled"); + return -1; + } + + ac = wmm_ac_find_tsid(wpa_s, tsid, &dir); + if (ac < 0) { + wpa_printf(MSG_DEBUG, "WMM AC: TS does not exist"); + return -1; + } + + tspec = *wpa_s->tspecs[ac][dir]; + + wmm_ac_del_ts_idx(wpa_s, ac, dir); + + wmm_ac_send_delts(wpa_s, &tspec, wpa_s->bssid); + + return 0; +} + + +int wpas_wmm_ac_addts(struct wpa_supplicant *wpa_s, + struct wmm_ac_ts_setup_params *params) +{ + struct wmm_ac_addts_request *addts_req; + + if (!wpa_s->wmm_ac_assoc_info) { + wpa_printf(MSG_DEBUG, + "WMM AC: Cannot add TS - missing assoc data"); + return -1; + } + + if (wpa_s->addts_request) { + wpa_printf(MSG_DEBUG, + "WMM AC: can't add TS - ADDTS request is already pending"); + return -1; + } + + /* + * we can setup downlink TS even without driver support. + * however, we need driver support for the other directions. + */ + if (params->direction != WMM_AC_DIR_DOWNLINK && + !wpa_s->wmm_ac_supported) { + wpa_printf(MSG_DEBUG, + "Cannot set uplink/bidi TS without driver support"); + return -1; + } + + if (!wmm_ac_ts_req_is_valid(wpa_s, params)) + return -1; + + wpa_printf(MSG_DEBUG, "WMM AC: TS setup request (addr=" MACSTR + " tsid=%u user priority=%u direction=%d)", + MAC2STR(wpa_s->bssid), params->tsid, + params->user_priority, params->direction); + + addts_req = wmm_ac_build_addts_req(wpa_s, params, wpa_s->bssid); + if (!addts_req) + return -1; + + if (wmm_ac_send_addts_request(wpa_s, addts_req)) + goto err; + + /* save as pending and set ADDTS resp timeout to 1 second */ + wpa_s->addts_request = addts_req; + eloop_register_timeout(1, 0, wmm_ac_addts_req_timeout, + wpa_s, addts_req); + return 0; +err: + os_free(addts_req); + return -1; +} + + +static void wmm_ac_handle_delts(struct wpa_supplicant *wpa_s, const u8 *sa, + const struct wmm_tspec_element *tspec) +{ + int ac; + u8 tsid; + enum ts_dir_idx idx; + + tsid = wmm_ac_get_tsid(tspec); + + wpa_printf(MSG_DEBUG, + "WMM AC: DELTS frame has been received TSID=%u addr=" + MACSTR, tsid, MAC2STR(sa)); + + ac = wmm_ac_find_tsid(wpa_s, tsid, &idx); + if (ac < 0) { + wpa_printf(MSG_DEBUG, + "WMM AC: Ignoring DELTS frame - TSID does not exist"); + return; + } + + wmm_ac_del_ts_idx(wpa_s, ac, idx); + + wpa_printf(MSG_DEBUG, + "TS was deleted successfully (tsid=%u address=" MACSTR ")", + tsid, MAC2STR(sa)); +} + + +static void wmm_ac_handle_addts_resp(struct wpa_supplicant *wpa_s, const u8 *sa, + const u8 resp_dialog_token, const u8 status_code, + const struct wmm_tspec_element *tspec) +{ + struct wmm_ac_addts_request *req = wpa_s->addts_request; + u8 ac, tsid, up, dir; + int replace_tspecs; + + tsid = wmm_ac_get_tsid(tspec); + dir = wmm_ac_get_direction(tspec); + up = wmm_ac_get_user_priority(tspec); + ac = up_to_ac[up]; + + /* make sure we have a matching addts request */ + if (!req || req->dialog_token != resp_dialog_token) { + wpa_printf(MSG_DEBUG, + "WMM AC: no req with dialog=%u, ignoring frame", + resp_dialog_token); + return; + } + + /* make sure the params are the same */ + if (os_memcmp(req->address, sa, ETH_ALEN) != 0 || + tsid != wmm_ac_get_tsid(&req->tspec) || + up != wmm_ac_get_user_priority(&req->tspec) || + dir != wmm_ac_get_direction(&req->tspec)) { + wpa_printf(MSG_DEBUG, + "WMM AC: ADDTS params do not match, ignoring frame"); + return; + } + + /* delete pending request */ + wmm_ac_del_req(wpa_s, 0); + + wpa_printf(MSG_DEBUG, + "ADDTS response status=%d tsid=%u up=%u direction=%u", + status_code, tsid, up, dir); + + if (status_code != WMM_ADDTS_STATUS_ADMISSION_ACCEPTED) { + wpa_printf(MSG_INFO, "WMM AC: ADDTS request was rejected"); + goto err_msg; + } + + replace_tspecs = wmm_ac_should_replace_ts(wpa_s, tsid, ac, dir); + if (replace_tspecs < 0) + goto err_delts; + + wpa_printf(MSG_DEBUG, "ts idx replace bitmap: 0x%x", replace_tspecs); + + /* when replacing tspecs - delete first */ + wmm_ac_del_ts(wpa_s, ac, replace_tspecs); + + /* Creating a new traffic stream */ + wpa_printf(MSG_DEBUG, + "WMM AC: adding a new TS with TSID=%u address="MACSTR + " medium time=%u access category=%d dir=%d ", + tsid, MAC2STR(sa), + le_to_host16(tspec->medium_time), ac, dir); + + if (wmm_ac_add_ts(wpa_s, sa, tspec)) + goto err_delts; + + return; + +err_delts: + /* ask the ap to delete the tspec */ + wmm_ac_send_delts(wpa_s, tspec, sa); +err_msg: + wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED "tsid=%u", + tsid); +} + + +void wmm_ac_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, + const u8 *sa, const u8 *data, size_t len) +{ + u8 action; + u8 dialog_token; + u8 status_code; + struct ieee802_11_elems elems; + struct wmm_tspec_element *tspec; + + if (wpa_s->wmm_ac_assoc_info == NULL) { + wpa_printf(MSG_DEBUG, + "WMM AC: WMM AC is disabled, ignoring action frame"); + return; + } + + action = data[0]; + + if (action != WMM_ACTION_CODE_ADDTS_RESP && + action != WMM_ACTION_CODE_DELTS) { + wpa_printf(MSG_DEBUG, + "WMM AC: Unknown action (%d), ignoring action frame", + action); + return; + } + + /* WMM AC action frame */ + if (os_memcmp(da, wpa_s->own_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "WMM AC: frame destination addr="MACSTR + " is other than ours, ignoring frame", MAC2STR(da)); + return; + } + + if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "WMM AC: ignore frame with sa " MACSTR + " different other than our bssid", MAC2STR(da)); + return; + } + + if (len < 2 + sizeof(struct wmm_tspec_element)) { + wpa_printf(MSG_DEBUG, + "WMM AC: Short ADDTS response ignored (len=%lu)", + (unsigned long) len); + return; + } + + data++; + len--; + dialog_token = data[0]; + status_code = data[1]; + + if (ieee802_11_parse_elems(data + 2, len - 2, &elems, 1) != ParseOK) { + wpa_printf(MSG_DEBUG, + "WMM AC: Could not parse WMM AC action from " MACSTR, + MAC2STR(sa)); + return; + } + + /* the struct also contains the type and value, so decrease it */ + if (elems.wmm_tspec_len != sizeof(struct wmm_tspec_element) - 2) { + wpa_printf(MSG_DEBUG, "WMM AC: missing or wrong length TSPEC"); + return; + } + + tspec = (struct wmm_tspec_element *)(elems.wmm_tspec - 2); + + wpa_printf(MSG_DEBUG, "WMM AC: RX WMM AC Action from " MACSTR, + MAC2STR(sa)); + wpa_hexdump(MSG_MSGDUMP, "WMM AC: WMM AC Action content", data, len); + + switch (action) { + case WMM_ACTION_CODE_ADDTS_RESP: + wmm_ac_handle_addts_resp(wpa_s, sa, dialog_token, status_code, + tspec); + break; + case WMM_ACTION_CODE_DELTS: + wmm_ac_handle_delts(wpa_s, sa, tspec); + break; + default: + break; + } +} + + +static const char * get_ac_str(u8 ac) +{ + switch (ac) { + case WMM_AC_BE: + return "BE"; + case WMM_AC_BK: + return "BK"; + case WMM_AC_VI: + return "VI"; + case WMM_AC_VO: + return "VO"; + default: + return "N/A"; + } +} + + +static const char * get_direction_str(u8 direction) +{ + switch (direction) { + case WMM_AC_DIR_DOWNLINK: + return "Downlink"; + case WMM_AC_DIR_UPLINK: + return "Uplink"; + case WMM_AC_DIR_BIDIRECTIONAL: + return "Bi-directional"; + default: + return "N/A"; + } +} + + +int wpas_wmm_ac_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen) +{ + struct wmm_ac_assoc_data *assoc_info = wpa_s->wmm_ac_assoc_info; + enum ts_dir_idx idx; + int pos = 0; + u8 ac, up; + + if (!assoc_info) { + return wpa_scnprintf(buf, buflen - pos, + "Not associated to a WMM AP, WMM AC is Disabled\n"); + } + + pos += wpa_scnprintf(buf + pos, buflen - pos, "WMM AC is Enabled\n"); + + for (ac = 0; ac < WMM_AC_NUM; ac++) { + int ts_count = 0; + + pos += wpa_scnprintf(buf + pos, buflen - pos, + "%s: acm=%d uapsd=%d\n", + get_ac_str(ac), + assoc_info->ac_params[ac].acm, + assoc_info->ac_params[ac].uapsd); + + for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) { + struct wmm_tspec_element *tspec; + u8 dir, tsid; + const char *dir_str; + + tspec = wpa_s->tspecs[ac][idx]; + if (!tspec) + continue; + + ts_count++; + + dir = wmm_ac_get_direction(tspec); + dir_str = get_direction_str(dir); + tsid = wmm_ac_get_tsid(tspec); + up = wmm_ac_get_user_priority(tspec); + + pos += wpa_scnprintf(buf + pos, buflen - pos, + "\tTSID=%u UP=%u\n" + "\tAddress = "MACSTR"\n" + "\tWMM AC dir = %s\n" + "\tTotal admitted time = %u\n\n", + tsid, up, + MAC2STR(wpa_s->bssid), + dir_str, + le_to_host16(tspec->medium_time)); + } + + if (!ts_count) { + pos += wpa_scnprintf(buf + pos, buflen - pos, + "\t(No Traffic Stream)\n\n"); + } + } + + return pos; +} + + +static u8 wmm_ac_get_tspecs_count(struct wpa_supplicant *wpa_s) +{ + int ac, dir, tspecs_count = 0; + + for (ac = 0; ac < WMM_AC_NUM; ac++) { + for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) { + if (wpa_s->tspecs[ac][dir]) + tspecs_count++; + } + } + + return tspecs_count; +} + + +void wmm_ac_save_tspecs(struct wpa_supplicant *wpa_s) +{ + int ac, dir, tspecs_count; + + wpa_printf(MSG_DEBUG, "WMM AC: Save last configured tspecs"); + + if (!wpa_s->wmm_ac_assoc_info) + return; + + tspecs_count = wmm_ac_get_tspecs_count(wpa_s); + if (!tspecs_count) { + wpa_printf(MSG_DEBUG, "WMM AC: No configured TSPECs"); + return; + } + + wpa_printf(MSG_DEBUG, "WMM AC: Saving tspecs"); + + wmm_ac_clear_saved_tspecs(wpa_s); + wpa_s->last_tspecs = os_calloc(tspecs_count, + sizeof(*wpa_s->last_tspecs)); + if (!wpa_s->last_tspecs) { + wpa_printf(MSG_ERROR, "WMM AC: Failed to save tspecs!"); + return; + } + + for (ac = 0; ac < WMM_AC_NUM; ac++) { + for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) { + if (!wpa_s->tspecs[ac][dir]) + continue; + + wpa_s->last_tspecs[wpa_s->last_tspecs_count++] = + *wpa_s->tspecs[ac][dir]; + } + } + + wpa_printf(MSG_DEBUG, "WMM AC: Successfully saved %d TSPECs", + wpa_s->last_tspecs_count); +} + + +void wmm_ac_clear_saved_tspecs(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->last_tspecs) { + wpa_printf(MSG_DEBUG, "WMM AC: Clear saved tspecs"); + os_free(wpa_s->last_tspecs); + wpa_s->last_tspecs = NULL; + wpa_s->last_tspecs_count = 0; + } +} + + +int wmm_ac_restore_tspecs(struct wpa_supplicant *wpa_s) +{ + unsigned int i; + + if (!wpa_s->wmm_ac_assoc_info || !wpa_s->last_tspecs_count) + return 0; + + wpa_printf(MSG_DEBUG, "WMM AC: Restore %u saved tspecs", + wpa_s->last_tspecs_count); + + for (i = 0; i < wpa_s->last_tspecs_count; i++) + wmm_ac_add_ts(wpa_s, wpa_s->bssid, &wpa_s->last_tspecs[i]); + + return 0; +} diff --git a/wpa_supplicant/wmm_ac.h b/wpa_supplicant/wmm_ac.h new file mode 100644 index 0000000000000..5171b1683ef7b --- /dev/null +++ b/wpa_supplicant/wmm_ac.h @@ -0,0 +1,176 @@ +/* + * Wi-Fi Multimedia Admission Control (WMM-AC) + * Copyright(c) 2014, Intel Mobile Communication GmbH. + * Copyright(c) 2014, Intel Corporation. All rights reserved. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WMM_AC_H +#define WMM_AC_H + +#include "common/ieee802_11_defs.h" +#include "drivers/driver.h" + +struct wpa_supplicant; + +#define WMM_AC_ACCESS_POLICY_EDCA 1 +#define WMM_AC_FIXED_MSDU_SIZE BIT(15) + +#define WMM_AC_MAX_TID 7 +#define WMM_AC_MAX_USER_PRIORITY 7 +#define WMM_AC_MIN_SBA_UNITY 0x2000 +#define WMM_AC_MAX_NOMINAL_MSDU 32767 + +/** + * struct wmm_ac_assoc_data - WMM Admission Control Association Data + * + * This struct will store any relevant WMM association data needed by WMM AC. + * In case there is a valid WMM association, an instance of this struct will be + * created. In case there is no instance of this struct, the station is not + * associated to a valid WMM BSS and hence, WMM AC will not be used. + */ +struct wmm_ac_assoc_data { + struct { + /* + * acm - Admission Control Mandatory + * In case an access category is ACM, the traffic will have + * to be admitted by WMM-AC's admission mechanism before use. + */ + unsigned int acm:1; + + /* + * uapsd_queues - Unscheduled Automatic Power Save Delivery + * queues. + * Indicates whether ACs are configured for U-APSD (or legacy + * PS). Storing this value is necessary in order to set the + * Power Save Bit (PSB) in ADDTS request Action frames (if not + * given). + */ + unsigned int uapsd:1; + } ac_params[WMM_AC_NUM]; +}; + +/** + * wmm_ac_dir - WMM Admission Control Direction + */ +enum wmm_ac_dir { + WMM_AC_DIR_UPLINK = 0, + WMM_AC_DIR_DOWNLINK = 1, + WMM_AC_DIR_BIDIRECTIONAL = 3 +}; + +/** + * ts_dir_idx - indices of internally saved tspecs + * + * we can have multiple tspecs (downlink + uplink) per ac. + * save them in array, and use the enum to directly access + * the respective tspec slot (according to the direction). + */ +enum ts_dir_idx { + TS_DIR_IDX_UPLINK, + TS_DIR_IDX_DOWNLINK, + TS_DIR_IDX_BIDI, + + TS_DIR_IDX_COUNT +}; +#define TS_DIR_IDX_ALL (BIT(TS_DIR_IDX_COUNT) - 1) + +/** + * struct wmm_ac_addts_request - ADDTS Request Information + * + * The last sent ADDTS request(s) will be saved as element(s) of this struct in + * order to be compared with the received ADDTS response in ADDTS response + * action frame handling and should be stored until that point. + * In case a new traffic stream will be created/replaced/updated, only its + * relevant traffic stream information will be stored as a wmm_ac_ts struct. + */ +struct wmm_ac_addts_request { + /* + * dialog token - Used to link the recived ADDTS response with this + * saved ADDTS request when ADDTS response is being handled + */ + u8 dialog_token; + + /* + * address - The alleged traffic stream's receiver/transmitter address + * Address and TID are used to identify the TS (TID is contained in + * TSPEC) + */ + u8 address[ETH_ALEN]; + + /* + * tspec - Traffic Stream Specification, will be used to compare the + * sent TSPEC in ADDTS request to the received TSPEC in ADDTS response + * and act accordingly in ADDTS response handling + */ + struct wmm_tspec_element tspec; +}; + + +/** + * struct wmm_ac_ts_setup_params - TS setup parameters + * + * This struct holds parameters which should be provided + * to wmm_ac_ts_setup in order to setup a traffic stream + */ +struct wmm_ac_ts_setup_params { + /* + * tsid - Traffic ID + * TID and address are used to identify the TS + */ + int tsid; + + /* + * direction - Traffic Stream's direction + */ + enum wmm_ac_dir direction; + + /* + * user_priority - Traffic Stream's user priority + */ + int user_priority; + + /* + * nominal_msdu_size - Nominal MAC service data unit size + */ + int nominal_msdu_size; + + /* + * fixed_nominal_msdu - Whether the size is fixed + * 0 = Nominal MSDU size is not fixed + * 1 = Nominal MSDU size is fixed + */ + int fixed_nominal_msdu; + + /* + * surplus_bandwidth_allowance - Specifies excess time allocation + */ + int mean_data_rate; + + /* + * minimum_phy_rate - Specifies the minimum supported PHY rate in bps + */ + int minimum_phy_rate; + + /* + * surplus_bandwidth_allowance - Specifies excess time allocation + */ + int surplus_bandwidth_allowance; +}; + +void wmm_ac_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *ies, + size_t ies_len, const struct wmm_params *wmm_params); +void wmm_ac_notify_disassoc(struct wpa_supplicant *wpa_s); +int wpas_wmm_ac_addts(struct wpa_supplicant *wpa_s, + struct wmm_ac_ts_setup_params *params); +int wpas_wmm_ac_delts(struct wpa_supplicant *wpa_s, u8 tsid); +void wmm_ac_rx_action(struct wpa_supplicant *wpa_s, const u8 *da, + const u8 *sa, const u8 *data, size_t len); +int wpas_wmm_ac_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen); +void wmm_ac_save_tspecs(struct wpa_supplicant *wpa_s); +void wmm_ac_clear_saved_tspecs(struct wpa_supplicant *wpa_s); +int wmm_ac_restore_tspecs(struct wpa_supplicant *wpa_s); + +#endif /* WMM_AC_H */ diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 4d9e4533e64ef..954de67c2aa38 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/wpa_supplicant/wnm_sta.c @@ -1,6 +1,6 @@ /* * wpa_supplicant - WNM - * Copyright (c) 2011-2012, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,12 +10,19 @@ #include "utils/common.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "common/wpa_ctrl.h" #include "rsn_supp/wpa.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "scan.h" +#include "ctrl_iface.h" +#include "bss.h" +#include "wnm_sta.h" +#include "hs20_supplicant.h" #define MAX_TFS_IE_LEN 1024 +#define WNM_MAX_NEIGHBOR_REPORT 10 /* get the TFS IE from driver */ @@ -176,7 +183,7 @@ static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s, /* Install GTK/IGTK */ /* point to key data field */ - ptr = (u8 *) frm + 1 + 1 + 2; + ptr = (u8 *) frm + 1 + 2; end = ptr + key_len_total; wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total); @@ -229,23 +236,29 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, const u8 *frm, int len) { /* - * Action [1] | Diaglog Token [1] | Key Data Len [2] | Key Data | + * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data | * WNM-Sleep Mode IE | TFS Response IE */ - u8 *pos = (u8 *) frm; /* point to action field */ - u16 key_len_total = le_to_host16(*((u16 *)(frm+2))); + u8 *pos = (u8 *) frm; /* point to payload after the action field */ + u16 key_len_total; struct wnm_sleep_element *wnmsleep_ie = NULL; /* multiple TFS Resp IE (assuming consecutive) */ u8 *tfsresp_ie_start = NULL; u8 *tfsresp_ie_end = NULL; + size_t left; - wpa_printf(MSG_DEBUG, "action=%d token = %d key_len_total = %d", - frm[0], frm[1], key_len_total); - pos += 4 + key_len_total; - if (pos > frm + len) { + if (len < 3) + return; + key_len_total = WPA_GET_LE16(frm + 1); + + wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d", + frm[0], key_len_total); + left = len - 3; + if (key_len_total > left) { wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field"); return; } + pos += 3 + key_len_total; while (pos - frm < len) { u8 ie_len = *(pos + 1); if (pos + 2 + ie_len > frm + len) { @@ -294,17 +307,253 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, } -static void wnm_send_bss_transition_mgmt_resp(struct wpa_supplicant *wpa_s, - u8 dialog_token, u8 status, - u8 delay, const u8 *target_bssid) +void wnm_deallocate_memory(struct wpa_supplicant *wpa_s) +{ + int i; + + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot); + os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid); + } + + wpa_s->wnm_num_neighbor_report = 0; + os_free(wpa_s->wnm_neighbor_report_elements); + wpa_s->wnm_neighbor_report_elements = NULL; +} + + +static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep, + u8 id, u8 elen, const u8 *pos) +{ + switch (id) { + case WNM_NEIGHBOR_TSF: + if (elen < 2 + 2) { + wpa_printf(MSG_DEBUG, "WNM: Too short TSF"); + break; + } + rep->tsf_offset = WPA_GET_LE16(pos); + rep->beacon_int = WPA_GET_LE16(pos + 2); + rep->tsf_present = 1; + break; + case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING: + if (elen < 2) { + wpa_printf(MSG_DEBUG, "WNM: Too short condensed " + "country string"); + break; + } + os_memcpy(rep->country, pos, 2); + rep->country_present = 1; + break; + case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE: + if (elen < 1) { + wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition " + "candidate"); + break; + } + rep->preference = pos[0]; + rep->preference_present = 1; + break; + case WNM_NEIGHBOR_BSS_TERMINATION_DURATION: + rep->bss_term_tsf = WPA_GET_LE64(pos); + rep->bss_term_dur = WPA_GET_LE16(pos + 8); + rep->bss_term_present = 1; + break; + case WNM_NEIGHBOR_BEARING: + if (elen < 8) { + wpa_printf(MSG_DEBUG, "WNM: Too short neighbor " + "bearing"); + break; + } + rep->bearing = WPA_GET_LE16(pos); + rep->distance = WPA_GET_LE32(pos + 2); + rep->rel_height = WPA_GET_LE16(pos + 2 + 4); + rep->bearing_present = 1; + break; + case WNM_NEIGHBOR_MEASUREMENT_PILOT: + if (elen < 1) { + wpa_printf(MSG_DEBUG, "WNM: Too short measurement " + "pilot"); + break; + } + os_free(rep->meas_pilot); + rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot)); + if (rep->meas_pilot == NULL) + break; + rep->meas_pilot->measurement_pilot = pos[0]; + rep->meas_pilot->subelem_len = elen - 1; + os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1); + break; + case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES: + if (elen < 5) { + wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled " + "capabilities"); + break; + } + os_memcpy(rep->rm_capab, pos, 5); + rep->rm_capab_present = 1; + break; + case WNM_NEIGHBOR_MULTIPLE_BSSID: + if (elen < 1) { + wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID"); + break; + } + os_free(rep->mul_bssid); + rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid)); + if (rep->mul_bssid == NULL) + break; + rep->mul_bssid->max_bssid_indicator = pos[0]; + rep->mul_bssid->subelem_len = elen - 1; + os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1); + break; + } +} + + +static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan) +{ + struct wpa_bss *bss = wpa_s->current_bss; + const char *country = NULL; + + if (bss) { + const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY); + + if (elem && elem[1] >= 2) + country = (const char *) (elem + 2); + } + + return ieee80211_chan_to_freq(country, op_class, chan); +} + + +static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s, + const u8 *pos, u8 len, + struct neighbor_report *rep) +{ + u8 left = len; + + if (left < 13) { + wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report"); + return; + } + + os_memcpy(rep->bssid, pos, ETH_ALEN); + rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN); + rep->regulatory_class = *(pos + 10); + rep->channel_number = *(pos + 11); + rep->phy_type = *(pos + 12); + + pos += 13; + left -= 13; + + while (left >= 2) { + u8 id, elen; + + id = *pos++; + elen = *pos++; + wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen); + left -= 2; + if (elen > left) { + wpa_printf(MSG_DEBUG, + "WNM: Truncated neighbor report subelement"); + break; + } + wnm_parse_neighbor_report_elem(rep, id, elen, pos); + left -= elen; + pos += elen; + } + + rep->freq = wnm_nei_get_chan(wpa_s, rep->regulatory_class, + rep->channel_number); +} + + +static struct wpa_bss * +compare_scan_neighbor_results(struct wpa_supplicant *wpa_s) +{ + + u8 i; + struct wpa_bss *bss = wpa_s->current_bss; + struct wpa_bss *target; + + if (!bss) + return 0; + + wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d", + MAC2STR(wpa_s->bssid), bss->level); + + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + struct neighbor_report *nei; + + nei = &wpa_s->wnm_neighbor_report_elements[i]; + if (nei->preference_present && nei->preference == 0) { + wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR, + MAC2STR(nei->bssid)); + continue; + } + + target = wpa_bss_get_bssid(wpa_s, nei->bssid); + if (!target) { + wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR + " (pref %d) not found in scan results", + MAC2STR(nei->bssid), + nei->preference_present ? nei->preference : + -1); + continue; + } + + if (bss->ssid_len != target->ssid_len || + os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) { + /* + * TODO: Could consider allowing transition to another + * ESS if PMF was enabled for the association. + */ + wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR + " (pref %d) in different ESS", + MAC2STR(nei->bssid), + nei->preference_present ? nei->preference : + -1); + continue; + } + + if (target->level < bss->level && target->level < -80) { + wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR + " (pref %d) does not have sufficient signal level (%d)", + MAC2STR(nei->bssid), + nei->preference_present ? nei->preference : + -1, + target->level); + continue; + } + + wpa_printf(MSG_DEBUG, + "WNM: Found an acceptable preferred transition candidate BSS " + MACSTR " (RSSI %d)", + MAC2STR(nei->bssid), target->level); + return target; + } + + return NULL; +} + + +static void wnm_send_bss_transition_mgmt_resp( + struct wpa_supplicant *wpa_s, u8 dialog_token, + enum bss_trans_mgmt_status_code status, u8 delay, + const u8 *target_bssid) { u8 buf[1000], *pos; struct ieee80211_mgmt *mgmt; size_t len; + int res; wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response " "to " MACSTR " dialog_token=%u status=%u delay=%d", MAC2STR(wpa_s->bssid), dialog_token, status, delay); + if (!wpa_s->current_bss) { + wpa_printf(MSG_DEBUG, + "WNM: Current BSS not known - drop response"); + return; + } mgmt = (struct ieee80211_mgmt *) buf; os_memset(&buf, 0, sizeof(buf)); @@ -322,13 +571,215 @@ static void wnm_send_bss_transition_mgmt_resp(struct wpa_supplicant *wpa_s, if (target_bssid) { os_memcpy(pos, target_bssid, ETH_ALEN); pos += ETH_ALEN; + } else if (status == WNM_BSS_TM_ACCEPT) { + /* + * P802.11-REVmc clarifies that the Target BSSID field is always + * present when status code is zero, so use a fake value here if + * no BSSID is yet known. + */ + os_memset(pos, 0, ETH_ALEN); + pos += ETH_ALEN; } len = pos - (u8 *) &mgmt->u.action.category; - wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, - wpa_s->own_addr, wpa_s->bssid, - &mgmt->u.action.category, len, 0); + res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + &mgmt->u.action.category, len, 0); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "WNM: Failed to send BSS Transition Management Response"); + } +} + + +int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail) +{ + struct wpa_bss *bss; + struct wpa_ssid *ssid = wpa_s->current_ssid; + enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED; + + if (!wpa_s->wnm_neighbor_report_elements) + return 0; + + if (os_reltime_before(&wpa_s->wnm_cand_valid_until, + &wpa_s->scan_trigger_time)) { + wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it"); + wnm_deallocate_memory(wpa_s); + return 0; + } + + if (!wpa_s->current_bss || + os_memcmp(wpa_s->wnm_cand_from_bss, wpa_s->current_bss->bssid, + ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it"); + return 0; + } + + /* Compare the Neighbor Report and scan results */ + bss = compare_scan_neighbor_results(wpa_s); + if (!bss) { + wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found"); + status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES; + goto send_bss_resp_fail; + } + + /* Associate to the network */ + /* Send the BSS Management Response - Accept */ + if (wpa_s->wnm_reply) { + wpa_s->wnm_reply = 0; + wnm_send_bss_transition_mgmt_resp(wpa_s, + wpa_s->wnm_dialog_token, + WNM_BSS_TM_ACCEPT, + 0, bss->bssid); + } + + if (bss == wpa_s->current_bss) { + wpa_printf(MSG_DEBUG, + "WNM: Already associated with the preferred candidate"); + return 1; + } + + wpa_s->reassociate = 1; + wpa_supplicant_connect(wpa_s, bss, ssid); + wnm_deallocate_memory(wpa_s); + return 1; + +send_bss_resp_fail: + if (!reply_on_fail) + return 0; + + /* Send reject response for all the failures */ + + if (wpa_s->wnm_reply) { + wpa_s->wnm_reply = 0; + wnm_send_bss_transition_mgmt_resp(wpa_s, + wpa_s->wnm_dialog_token, + status, 0, NULL); + } + wnm_deallocate_memory(wpa_s); + + return 0; +} + + +static int cand_pref_compar(const void *a, const void *b) +{ + const struct neighbor_report *aa = a; + const struct neighbor_report *bb = b; + + if (!aa->preference_present && !bb->preference_present) + return 0; + if (!aa->preference_present) + return 1; + if (!bb->preference_present) + return -1; + if (bb->preference > aa->preference) + return 1; + if (bb->preference < aa->preference) + return -1; + return 0; +} + + +static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->wnm_neighbor_report_elements) + return; + qsort(wpa_s->wnm_neighbor_report_elements, + wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report), + cand_pref_compar); +} + + +static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s) +{ + unsigned int i; + + wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List"); + if (!wpa_s->wnm_neighbor_report_elements) + return; + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + struct neighbor_report *nei; + + nei = &wpa_s->wnm_neighbor_report_elements[i]; + wpa_printf(MSG_DEBUG, "%u: " MACSTR + " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d", + i, MAC2STR(nei->bssid), nei->bssid_info, + nei->regulatory_class, + nei->channel_number, nei->phy_type, + nei->preference_present ? nei->preference : -1, + nei->freq); + } +} + + +static int chan_supported(struct wpa_supplicant *wpa_s, int freq) +{ + unsigned int i; + + for (i = 0; i < wpa_s->hw.num_modes; i++) { + struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i]; + int j; + + for (j = 0; j < mode->num_channels; j++) { + struct hostapd_channel_data *chan; + + chan = &mode->channels[j]; + if (chan->freq == freq && + !(chan->flag & HOSTAPD_CHAN_DISABLED)) + return 1; + } + } + + return 0; +} + + +static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s) +{ + int *freqs; + int num_freqs = 0; + unsigned int i; + + if (!wpa_s->wnm_neighbor_report_elements) + return; + + if (wpa_s->hw.modes == NULL) + return; + + os_free(wpa_s->next_scan_freqs); + wpa_s->next_scan_freqs = NULL; + + freqs = os_calloc(wpa_s->wnm_num_neighbor_report + 1, sizeof(int)); + if (freqs == NULL) + return; + + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + struct neighbor_report *nei; + + nei = &wpa_s->wnm_neighbor_report_elements[i]; + if (nei->freq <= 0) { + wpa_printf(MSG_DEBUG, + "WNM: Unknown neighbor operating frequency for " + MACSTR " - scan all channels", + MAC2STR(nei->bssid)); + os_free(freqs); + return; + } + if (chan_supported(wpa_s, nei->freq)) + add_freq(freqs, &num_freqs, nei->freq); + } + + if (num_freqs == 0) { + os_free(freqs); + return; + } + + wpa_printf(MSG_DEBUG, + "WNM: Scan %d frequencies based on transition candidate list", + num_freqs); + wpa_s->next_scan_freqs = freqs; } @@ -336,26 +787,43 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, const u8 *pos, const u8 *end, int reply) { - u8 dialog_token; - u8 mode; - u16 disassoc_timer; + unsigned int beacon_int; + u8 valid_int; if (pos + 5 > end) return; - dialog_token = pos[0]; - mode = pos[1]; - disassoc_timer = WPA_GET_LE16(pos + 2); + if (wpa_s->current_bss) + beacon_int = wpa_s->current_bss->beacon_int; + else + beacon_int = 100; /* best guess */ + + wpa_s->wnm_dialog_token = pos[0]; + wpa_s->wnm_mode = pos[1]; + wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2); + valid_int = pos[4]; + wpa_s->wnm_reply = reply; wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: " "dialog_token=%u request_mode=0x%x " "disassoc_timer=%u validity_interval=%u", - dialog_token, mode, disassoc_timer, pos[4]); + wpa_s->wnm_dialog_token, wpa_s->wnm_mode, + wpa_s->wnm_dissoc_timer, valid_int); + pos += 5; - if (mode & 0x08) + + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) { + if (pos + 12 > end) { + wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request"); + return; + } + os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12); pos += 12; /* BSS Termination Duration */ - if (mode & 0x10) { + } + + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) { char url[256]; + if (pos + 1 > end || pos + 1 + pos[0] > end) { wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition " "Management Request (URL)"); @@ -363,14 +831,17 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, } 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); + pos += 1 + pos[0]; + + wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s", + wpa_sm_pmf_enabled(wpa_s->wpa), + wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url); } - if (mode & 0x04) { + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - " - "Disassociation Timer %u", disassoc_timer); - if (disassoc_timer && !wpa_s->scanning) { + "Disassociation Timer %u", wpa_s->wnm_dissoc_timer); + if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) { /* TODO: mark current BSS less preferred for * selection */ wpa_printf(MSG_DEBUG, "Trying to find another BSS"); @@ -378,32 +849,282 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, } } - if (reply) { - /* TODO: add support for reporting Accept */ - wnm_send_bss_transition_mgmt_resp(wpa_s, dialog_token, - 1 /* Reject - unspecified */, - 0, NULL); + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) { + unsigned int valid_ms; + + wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available"); + wnm_deallocate_memory(wpa_s); + wpa_s->wnm_neighbor_report_elements = os_calloc( + WNM_MAX_NEIGHBOR_REPORT, + sizeof(struct neighbor_report)); + if (wpa_s->wnm_neighbor_report_elements == NULL) + return; + + while (pos + 2 <= end && + wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) + { + u8 tag = *pos++; + u8 len = *pos++; + + wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", + tag); + if (pos + len > end) { + wpa_printf(MSG_DEBUG, "WNM: Truncated request"); + return; + } + if (tag == WLAN_EID_NEIGHBOR_REPORT) { + struct neighbor_report *rep; + rep = &wpa_s->wnm_neighbor_report_elements[ + wpa_s->wnm_num_neighbor_report]; + wnm_parse_neighbor_report(wpa_s, pos, len, rep); + } + + pos += len; + wpa_s->wnm_num_neighbor_report++; + } + wnm_sort_cand_list(wpa_s); + wnm_dump_cand_list(wpa_s); + valid_ms = valid_int * beacon_int * 128 / 125; + wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms", + valid_ms); + os_get_reltime(&wpa_s->wnm_cand_valid_until); + wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000; + wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000; + wpa_s->wnm_cand_valid_until.sec += + wpa_s->wnm_cand_valid_until.usec / 1000000; + wpa_s->wnm_cand_valid_until.usec %= 1000000; + os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN); + + if (wpa_s->last_scan_res_used > 0) { + struct os_reltime now; + + os_get_reltime(&now); + if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) { + wpa_printf(MSG_DEBUG, + "WNM: Try to use recent scan results"); + if (wnm_scan_process(wpa_s, 0) > 0) + return; + wpa_printf(MSG_DEBUG, + "WNM: No match in previous scan results - try a new scan"); + } + } + + wnm_set_scan_freqs(wpa_s); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } else if (reply) { + enum bss_trans_mgmt_status_code status; + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) + status = WNM_BSS_TM_ACCEPT; + else { + wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates"); + status = WNM_BSS_TM_REJECT_UNSPECIFIED; + } + wnm_send_bss_transition_mgmt_resp(wpa_s, + wpa_s->wnm_dialog_token, + status, 0, NULL); + } +} + + +int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, + u8 query_reason) +{ + u8 buf[1000], *pos; + struct ieee80211_mgmt *mgmt; + size_t len; + int ret; + + wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to " + MACSTR " query_reason=%u", + MAC2STR(wpa_s->bssid), query_reason); + + mgmt = (struct ieee80211_mgmt *) buf; + os_memset(&buf, 0, sizeof(buf)); + 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_WNM; + mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY; + mgmt->u.action.u.bss_tm_query.dialog_token = 1; + mgmt->u.action.u.bss_tm_query.query_reason = query_reason; + pos = mgmt->u.action.u.bss_tm_query.variable; + + len = pos - (u8 *) &mgmt->u.action.category; + + ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + &mgmt->u.action.category, len, 0); + + return ret; +} + + +static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *data, + int len) +{ + const u8 *pos, *end, *next; + u8 ie, ie_len; + + pos = data; + end = data + len; + + while (pos + 1 < end) { + ie = *pos++; + ie_len = *pos++; + wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u", + ie, ie_len); + if (ie_len > end - pos) { + wpa_printf(MSG_DEBUG, "WNM: Not enough room for " + "subelement"); + break; + } + next = pos + ie_len; + if (ie_len < 4) { + pos = next; + continue; + } + wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u", + WPA_GET_BE24(pos), pos[3]); + +#ifdef CONFIG_HS20 + if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 && + WPA_GET_BE24(pos) == OUI_WFA && + pos[3] == HS20_WNM_SUB_REM_NEEDED) { + /* Subscription Remediation subelement */ + const u8 *ie_end; + u8 url_len; + char *url; + u8 osu_method; + + wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation " + "subelement"); + ie_end = pos + ie_len; + pos += 4; + url_len = *pos++; + if (url_len == 0) { + wpa_printf(MSG_DEBUG, "WNM: No Server URL included"); + url = NULL; + osu_method = 1; + } else { + if (pos + url_len + 1 > ie_end) { + wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)", + url_len, + (int) (ie_end - pos)); + break; + } + url = os_malloc(url_len + 1); + if (url == NULL) + break; + os_memcpy(url, pos, url_len); + url[url_len] = '\0'; + osu_method = pos[url_len]; + } + hs20_rx_subscription_remediation(wpa_s, url, + osu_method); + os_free(url); + pos = next; + continue; + } + + if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 && + WPA_GET_BE24(pos) == OUI_WFA && + pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) { + const u8 *ie_end; + u8 url_len; + char *url; + u8 code; + u16 reauth_delay; + + ie_end = pos + ie_len; + pos += 4; + code = *pos++; + reauth_delay = WPA_GET_LE16(pos); + pos += 2; + url_len = *pos++; + wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication " + "Imminent - Reason Code %u " + "Re-Auth Delay %u URL Length %u", + code, reauth_delay, url_len); + if (pos + url_len > ie_end) + break; + url = os_malloc(url_len + 1); + if (url == NULL) + break; + os_memcpy(url, pos, url_len); + url[url_len] = '\0'; + hs20_rx_deauth_imminent_notice(wpa_s, code, + reauth_delay, url); + os_free(url); + pos = next; + continue; + } +#endif /* CONFIG_HS20 */ + + pos = next; + } +} + + +static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *frm, int len) +{ + const u8 *pos, *end; + u8 dialog_token, type; + + /* Dialog Token [1] | Type [1] | Subelements */ + + if (len < 2 || sa == NULL) + return; + end = frm + len; + pos = frm; + dialog_token = *pos++; + type = *pos++; + + wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request " + "(dialog_token %u type %u sa " MACSTR ")", + dialog_token, type, MAC2STR(sa)); + wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements", + pos, end - pos); + + if (wpa_s->wpa_state != WPA_COMPLETED || + os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not " + "from our AP - ignore it"); + return; + } + + switch (type) { + case 1: + ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos); + break; + default: + wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown " + "WNM-Notification type %u", type); + break; } } void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, - struct rx_action *action) + const struct ieee80211_mgmt *mgmt, size_t len) { const u8 *pos, *end; u8 act; - if (action->data == NULL || action->len == 0) + if (len < IEEE80211_HDRLEN + 2) return; - pos = action->data; - end = pos + action->len; + pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1; act = *pos++; + end = ((const u8 *) mgmt) + len; wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR, - act, MAC2STR(action->sa)); + act, MAC2STR(mgmt->sa)); if (wpa_s->wpa_state < WPA_ASSOCIATED || - os_memcmp(action->sa, wpa_s->bssid, ETH_ALEN) != 0) { + os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action " "frame"); return; @@ -412,12 +1133,16 @@ void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, switch (act) { case WNM_BSS_TRANS_MGMT_REQ: ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end, - !(action->da[0] & 0x01)); + !(mgmt->da[0] & 0x01)); break; case WNM_SLEEP_MODE_RESP: - ieee802_11_rx_wnmsleep_resp(wpa_s, action->data, action->len); + ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos); + break; + case WNM_NOTIFICATION_REQ: + ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos); break; default: + wpa_printf(MSG_ERROR, "WNM: Unknown request"); break; } } diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h index 3f9d88b714a91..8de434807f190 100644 --- a/wpa_supplicant/wnm_sta.h +++ b/wpa_supplicant/wnm_sta.h @@ -9,13 +9,69 @@ #ifndef WNM_STA_H #define WNM_STA_H -struct rx_action; -struct wpa_supplicant; +struct measurement_pilot { + u8 measurement_pilot; + u8 subelem_len; + u8 subelems[255]; +}; + +struct multiple_bssid { + u8 max_bssid_indicator; + u8 subelem_len; + u8 subelems[255]; +}; + +struct neighbor_report { + u8 bssid[ETH_ALEN]; + u32 bssid_info; + u8 regulatory_class; + u8 channel_number; + u8 phy_type; + u8 preference; /* valid if preference_present=1 */ + u16 tsf_offset; /* valid if tsf_present=1 */ + u16 beacon_int; /* valid if tsf_present=1 */ + char country[2]; /* valid if country_present=1 */ + u8 rm_capab[5]; /* valid if rm_capab_present=1 */ + u16 bearing; /* valid if bearing_present=1 */ + u16 rel_height; /* valid if bearing_present=1 */ + u32 distance; /* valid if bearing_present=1 */ + u64 bss_term_tsf; /* valid if bss_term_present=1 */ + u16 bss_term_dur; /* valid if bss_term_present=1 */ + unsigned int preference_present:1; + unsigned int tsf_present:1; + unsigned int country_present:1; + unsigned int rm_capab_present:1; + unsigned int bearing_present:1; + unsigned int bss_term_present:1; + struct measurement_pilot *meas_pilot; + struct multiple_bssid *mul_bssid; + int freq; +}; + int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, u8 action, u16 intval, struct wpabuf *tfs_req); void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, - struct rx_action *action); + const struct ieee80211_mgmt *mgmt, size_t len); + +int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, + u8 query_reason); +void wnm_deallocate_memory(struct wpa_supplicant *wpa_s); + + +#ifdef CONFIG_WNM + +int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail); + +#else /* CONFIG_WNM */ + +static inline int wnm_scan_process(struct wpa_supplicant *wpa_s, + int reply_on_fail) +{ + return 0; +} + +#endif /* CONFIG_WNM */ #endif /* WNM_STA_H */ diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 0c6ef5e8ad125..5a0af0dc9bba3 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-2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -28,7 +28,7 @@ static const char *wpa_cli_version = "wpa_cli v" VERSION_STR "\n" -"Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi> and contributors"; +"Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> and contributors"; static const char *wpa_cli_license = @@ -70,7 +70,7 @@ static struct wpa_ctrl *ctrl_conn; static struct wpa_ctrl *mon_conn; static int wpa_cli_quit = 0; static int wpa_cli_attached = 0; -static int wpa_cli_connected = 0; +static int wpa_cli_connected = -1; static int wpa_cli_last_id = 0; #ifndef CONFIG_CTRL_IFACE_DIR #define CONFIG_CTRL_IFACE_DIR "/var/run/wpa_supplicant" @@ -81,6 +81,7 @@ static const char *pid_file = NULL; static const char *action_file = NULL; static int ping_interval = 5; static int interactive = 0; +static char *ifname_prefix = NULL; struct cli_txt_entry { struct dl_list list; @@ -90,6 +91,7 @@ struct cli_txt_entry { 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 DEFINE_DL_LIST(ifnames); /* struct cli_txt_entry */ static void print_help(const char *cmd); @@ -173,11 +175,9 @@ static void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt) end = os_strchr(txt, ' '); if (end == NULL) end = txt + os_strlen(txt); - buf = os_malloc(end - txt + 1); + buf = dup_binstr(txt, end - txt); if (buf == NULL) return; - os_memcpy(buf, txt, end - txt); - buf[end - txt] = '\0'; cli_txt_list_del(txt_list, buf); os_free(buf); } @@ -223,11 +223,9 @@ static int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt) end = os_strchr(txt, ' '); if (end == NULL) end = txt + os_strlen(txt); - buf = os_malloc(end - txt + 1); + buf = dup_binstr(txt, end - txt); 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; @@ -335,7 +333,7 @@ static int wpa_cli_open_connection(const char *ifname, int attach) return -1; res = os_snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname); - if (res < 0 || res >= flen) { + if (os_snprintf_error(flen, res)) { os_free(cfile); return -1; } @@ -400,7 +398,7 @@ static void wpa_cli_msg_cb(char *msg, size_t len) static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print) { - char buf[2048]; + char buf[4096]; size_t len; int ret; @@ -408,6 +406,12 @@ static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print) printf("Not connected to wpa_supplicant - command dropped.\n"); return -1; } + if (ifname_prefix) { + os_snprintf(buf, sizeof(buf), "IFNAME=%s %s", + ifname_prefix, cmd); + buf[sizeof(buf) - 1] = '\0'; + cmd = buf; + } len = sizeof(buf) - 1; ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, wpa_cli_msg_cb); @@ -444,13 +448,13 @@ static int write_cmd(char *buf, size_t buflen, const char *cmd, int argc, end = buf + buflen; res = os_snprintf(pos, end - pos, "%s", cmd); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) goto fail; pos += res; for (i = 0; i < argc; i++) { res = os_snprintf(pos, end - pos, " %s", argv[i]); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) goto fail; pos += res; } @@ -467,7 +471,7 @@ fail: static int wpa_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd, int min_args, int argc, char *argv[]) { - char buf[256]; + char buf[4096]; if (argc < min_args) { printf("Invalid %s command - at least %d argument%s " "required.\n", cmd, min_args, @@ -492,6 +496,8 @@ static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[]) return wpa_ctrl_command(ctrl, "STATUS-VERBOSE"); if (argc > 0 && os_strcmp(argv[0], "wps") == 0) return wpa_ctrl_command(ctrl, "STATUS-WPS"); + if (argc > 0 && os_strcmp(argv[0], "driver") == 0) + return wpa_ctrl_command(ctrl, "STATUS-DRIVER"); return wpa_ctrl_command(ctrl, "STATUS"); } @@ -526,6 +532,13 @@ static int wpa_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_pmksa_flush(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PMKSA_FLUSH"); +} + + static int wpa_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) { print_help(argc > 0 ? argv[0] : NULL); @@ -564,52 +577,90 @@ static int wpa_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[]) } -static void wpa_cli_show_variables(void) -{ - printf("set variables:\n" - " EAPOL::heldPeriod (EAPOL state machine held period, " - "in seconds)\n" - " EAPOL::authPeriod (EAPOL state machine authentication " - "period, in seconds)\n" - " EAPOL::startPeriod (EAPOL state machine start period, in " - "seconds)\n" - " EAPOL::maxStart (EAPOL state machine maximum start " - "attempts)\n"); - printf(" dot11RSNAConfigPMKLifetime (WPA/WPA2 PMK lifetime in " - "seconds)\n" - " dot11RSNAConfigPMKReauthThreshold (WPA/WPA2 reauthentication" - " threshold\n\tpercentage)\n" - " dot11RSNAConfigSATimeout (WPA/WPA2 timeout for completing " - "security\n\tassociation in seconds)\n"); -} - - static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[256]; int res; - if (argc == 0) { - wpa_cli_show_variables(); - return 0; + if (argc == 1) { + res = os_snprintf(cmd, sizeof(cmd), "SET %s ", argv[0]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long SET command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); } - if (argc != 1 && argc != 2) { - printf("Invalid SET command: needs two arguments (variable " - "name and value)\n"); - return -1; - } + return wpa_cli_cmd(ctrl, "SET", 2, argc, argv); +} - 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; + +static char ** wpa_cli_complete_set(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + const char *fields[] = { + /* runtime values */ + "EAPOL::heldPeriod", "EAPOL::authPeriod", "EAPOL::startPeriod", + "EAPOL::maxStart", "dot11RSNAConfigPMKLifetime", + "dot11RSNAConfigPMKReauthThreshold", "dot11RSNAConfigSATimeout", + "wps_fragment_size", "wps_version_number", "ampdu", + "tdls_testing", "tdls_disabled", "pno", "radio_disabled", + "uapsd", "ps", "wifi_display", "bssid_filter", "disallow_aps", + "no_keep_alive", + /* global configuration parameters */ + "eapol_version", "ap_scan", "disable_scan_offload", + "fast_reauth", "opensc_engine_path", "pkcs11_engine_path", + "pkcs11_module_path", "openssl_ciphers", + "pcsc_reader", "pcsc_pin", + "driver_param", "dot11RSNAConfigPMKLifetime", + "dot11RSNAConfigPMKReauthThreshold", + "dot11RSNAConfigSATimeout", + "update_config", "load_dynamic_eap", "uuid", "device_name", + "manufacturer", "model_name", "model_number", "serial_number", + "device_type", "os_version", "config_methods", + "wps_cred_processing", "wps_vendor_ext_m1", "sec_device_type", + "p2p_listen_reg_class", "p2p_listen_channel", + "p2p_oper_reg_class", "p2p_oper_channel", + "p2p_go_intent", "p2p_ssid_postfix", "persistent_reconnect", + "p2p_intra_bss", "p2p_group_idle", "p2p_pref_chan", + "p2p_no_go_freq", + "p2p_go_ht40", "p2p_disabled", "p2p_no_group_iface", + "p2p_go_vht", + "p2p_ignore_shared_freq", "country", "bss_max_count", + "bss_expiration_age", "bss_expiration_scan_count", + "filter_ssids", "filter_rssi", "max_num_sta", + "disassoc_low_ack", "hs20", "interworking", "hessid", + "access_network_type", "pbc_in_m1", "autoscan", + "wps_nfc_dev_pw_id", "wps_nfc_dh_pubkey", "wps_nfc_dh_privkey", + "wps_nfc_dev_pw", "ext_password_backend", + "p2p_go_max_inactivity", "auto_interworking", "okc", "pmf", + "sae_groups", "dtim_period", "beacon_int", "ap_vendor_elements", + "ignore_old_scan_res", "freq_list", "external_sim", + "tdls_external_control", "p2p_search_delay" + }; + int i, num_fields = ARRAY_SIZE(fields); + + if (arg == 1) { + char **res = os_calloc(num_fields + 1, sizeof(char *)); + if (res == NULL) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(fields[i]); + if (res[i] == NULL) + return res; + } + return res; } - return wpa_ctrl_command(ctrl, cmd); + + if (arg > 1 && os_strncasecmp(str, "set bssid_filter ", 17) == 0) + return cli_txt_list_array(&bsses); + + return NULL; +} + +static int wpa_cli_cmd_dump(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DUMP"); } @@ -638,6 +689,12 @@ static int wpa_cli_cmd_reassociate(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_reattach(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "REATTACH"); +} + + static int wpa_cli_cmd_preauthenticate(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -681,7 +738,7 @@ static int wpa_cli_cmd_bss_flush(struct wpa_ctrl *ctrl, int argc, char *argv[]) res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH 0"); else res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long BSS_FLUSH command.\n"); return -1; } @@ -744,6 +801,13 @@ static int wpa_cli_cmd_wps_nfc(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "WPS_NFC_CONFIG_TOKEN", 1, argc, argv); +} + + static int wpa_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -791,55 +855,10 @@ static int wpa_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl, int argc, } -static int wpa_cli_cmd_nfc_rx_handover_req(struct wpa_ctrl *ctrl, int argc, - char *argv[]) -{ - int ret; - char *buf; - size_t buflen; - - if (argc != 1) { - printf("Invalid 'nfc_rx_handover_req' command - one argument " - "is required.\n"); - return -1; - } - - buflen = 21 + os_strlen(argv[0]); - buf = os_malloc(buflen); - if (buf == NULL) - return -1; - os_snprintf(buf, buflen, "NFC_RX_HANDOVER_REQ %s", argv[0]); - - ret = wpa_ctrl_command(ctrl, buf); - os_free(buf); - - return ret; -} - - -static int wpa_cli_cmd_nfc_rx_handover_sel(struct wpa_ctrl *ctrl, int argc, +static int wpa_cli_cmd_nfc_report_handover(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - int ret; - char *buf; - size_t buflen; - - if (argc != 1) { - printf("Invalid 'nfc_rx_handover_sel' command - one argument " - "is required.\n"); - return -1; - } - - buflen = 21 + os_strlen(argv[0]); - buf = os_malloc(buflen); - if (buf == NULL) - return -1; - os_snprintf(buf, buflen, "NFC_RX_HANDOVER_SEL %s", argv[0]); - - ret = wpa_ctrl_command(ctrl, buf); - os_free(buf); - - return ret; + return wpa_cli_cmd(ctrl, "NFC_REPORT_HANDOVER", 4, argc, argv); } #endif /* CONFIG_WPS_NFC */ @@ -894,7 +913,7 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[]) return -1; } - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long WPS_REG command.\n"); return -1; } @@ -1019,7 +1038,7 @@ static int wpa_cli_cmd_wps_er_config(struct wpa_ctrl *ctrl, int argc, return -1; } - if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long WPS_ER_CONFIG command.\n"); return -1; } @@ -1071,14 +1090,14 @@ static int wpa_cli_cmd_identity(struct wpa_ctrl *ctrl, int argc, char *argv[]) pos = cmd; ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "IDENTITY-%s:%s", argv[0], argv[1]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long IDENTITY command.\n"); return -1; } pos += ret; for (i = 2; i < argc; i++) { ret = os_snprintf(pos, end - pos, " %s", argv[i]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long IDENTITY command.\n"); return -1; } @@ -1104,14 +1123,14 @@ static int wpa_cli_cmd_password(struct wpa_ctrl *ctrl, int argc, char *argv[]) pos = cmd; ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSWORD-%s:%s", argv[0], argv[1]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long PASSWORD command.\n"); return -1; } pos += ret; for (i = 2; i < argc; i++) { ret = os_snprintf(pos, end - pos, " %s", argv[i]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long PASSWORD command.\n"); return -1; } @@ -1138,14 +1157,14 @@ static int wpa_cli_cmd_new_password(struct wpa_ctrl *ctrl, int argc, pos = cmd; ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "NEW_PASSWORD-%s:%s", argv[0], argv[1]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long NEW_PASSWORD command.\n"); return -1; } pos += ret; for (i = 2; i < argc; i++) { ret = os_snprintf(pos, end - pos, " %s", argv[i]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long NEW_PASSWORD command.\n"); return -1; } @@ -1171,14 +1190,14 @@ static int wpa_cli_cmd_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) pos = cmd; ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PIN-%s:%s", argv[0], argv[1]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long PIN command.\n"); return -1; } pos += ret; for (i = 2; i < argc; i++) { ret = os_snprintf(pos, end - pos, " %s", argv[i]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long PIN command.\n"); return -1; } @@ -1203,14 +1222,14 @@ static int wpa_cli_cmd_otp(struct wpa_ctrl *ctrl, int argc, char *argv[]) pos = cmd; ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "OTP-%s:%s", argv[0], argv[1]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long OTP command.\n"); return -1; } pos += ret; for (i = 2; i < argc; i++) { ret = os_snprintf(pos, end - pos, " %s", argv[i]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long OTP command.\n"); return -1; } @@ -1221,6 +1240,38 @@ static int wpa_cli_cmd_otp(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_sim(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256], *pos, *end; + int i, ret; + + if (argc < 2) { + printf("Invalid SIM command: needs two arguments " + "(network id and SIM operation response)\n"); + return -1; + } + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "SIM-%s:%s", + argv[0], argv[1]); + if (os_snprintf_error(end - pos, ret)) { + printf("Too long SIM command.\n"); + return -1; + } + pos += ret; + for (i = 2; i < argc; i++) { + ret = os_snprintf(pos, end - pos, " %s", argv[i]); + if (os_snprintf_error(end - pos, ret)) { + printf("Too long SIM command.\n"); + return -1; + } + pos += ret; + } + return wpa_ctrl_command(ctrl, cmd); +} + + static int wpa_cli_cmd_passphrase(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1237,14 +1288,14 @@ static int wpa_cli_cmd_passphrase(struct wpa_ctrl *ctrl, int argc, pos = cmd; ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSPHRASE-%s:%s", argv[0], argv[1]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long PASSPHRASE command.\n"); return -1; } pos += ret; for (i = 2; i < argc; i++) { ret = os_snprintf(pos, end - pos, " %s", argv[i]); - if (ret < 0 || ret >= end - pos) { + if (os_snprintf_error(end - pos, ret)) { printf("Too long PASSPHRASE command.\n"); return -1; } @@ -1378,6 +1429,24 @@ static int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_dup_network(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + if (argc == 0) { + wpa_cli_show_network_variables(); + return 0; + } + + if (argc < 3) { + printf("Invalid DUP_NETWORK command: needs three arguments\n" + "(src netid, dest netid, and variable name)\n"); + return -1; + } + + return wpa_cli_cmd(ctrl, "DUP_NETWORK", 3, argc, argv); +} + + static int wpa_cli_cmd_list_creds(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1410,6 +1479,18 @@ static int wpa_cli_cmd_set_cred(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_get_cred(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc != 2) { + printf("Invalid GET_CRED command: needs two arguments\n" + "(cred id, variable name)\n"); + return -1; + } + + return wpa_cli_cmd(ctrl, "GET_CRED", 2, argc, argv); +} + + static int wpa_cli_cmd_disconnect(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1433,7 +1514,7 @@ static int wpa_cli_cmd_save_config(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_scan(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - return wpa_ctrl_command(ctrl, "SCAN"); + return wpa_cli_cmd(ctrl, "SCAN", 0, argc, argv); } @@ -1501,8 +1582,12 @@ static int wpa_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, char *argv[]) wpa_cli_close_connection(); os_free(ctrl_ifname); ctrl_ifname = os_strdup(argv[0]); + if (!ctrl_ifname) { + printf("Failed to allocate memory\n"); + return 0; + } - if (wpa_cli_open_connection(ctrl_ifname, 1)) { + if (wpa_cli_open_connection(ctrl_ifname, 1) == 0) { printf("Connected to interface '%s.\n", ctrl_ifname); } else { printf("Could not connect to interface '%s' - re-trying\n", @@ -1550,7 +1635,7 @@ static int wpa_cli_cmd_interface_add(struct wpa_ctrl *ctrl, int argc, argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : "", argc > 3 ? argv[3] : "", argc > 4 ? argv[4] : "", argc > 5 ? argv[5] : ""); - if (res < 0 || (size_t) res >= sizeof(cmd)) + if (os_snprintf_error(sizeof(cmd), res)) return -1; cmd[sizeof(cmd) - 1] = '\0'; return wpa_ctrl_command(ctrl, cmd); @@ -1640,6 +1725,13 @@ static int wpa_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc, { return wpa_cli_cmd(ctrl, "DISASSOCIATE", 1, argc, argv); } + +static int wpa_cli_cmd_chanswitch(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "CHAN_SWITCH", 2, argc, argv); +} + #endif /* CONFIG_AP */ @@ -1655,10 +1747,12 @@ static int wpa_cli_cmd_resume(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +#ifdef CONFIG_TESTING_OPTIONS static int wpa_cli_cmd_drop_sa(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_ctrl_command(ctrl, "DROP_SA"); } +#endif /* CONFIG_TESTING_OPTIONS */ static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[]) @@ -1667,6 +1761,31 @@ static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +#ifdef CONFIG_MESH + +static int wpa_cli_cmd_mesh_interface_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "MESH_INTERFACE_ADD", 0, argc, argv); +} + + +static int wpa_cli_cmd_mesh_group_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "MESH_GROUP_ADD", 1, argc, argv); +} + + +static int wpa_cli_cmd_mesh_group_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "MESH_GROUP_REMOVE", 1, argc, argv); +} + +#endif /* CONFIG_MESH */ + + #ifdef CONFIG_P2P static int wpa_cli_cmd_p2p_find(struct wpa_ctrl *ctrl, int argc, char *argv[]) @@ -1711,6 +1830,20 @@ static int wpa_cli_cmd_p2p_stop_find(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_p2p_asp_provision(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_ASP_PROVISION", 3, argc, argv); +} + + +static int wpa_cli_cmd_p2p_asp_provision_resp(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_ASP_PROVISION_RESP", 2, argc, argv); +} + + static int wpa_cli_cmd_p2p_connect(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1795,11 +1928,9 @@ static int wpa_cli_cmd_p2p_serv_disc_req(struct wpa_ctrl *ctrl, int argc, { char cmd[4096]; - if (argc != 2 && argc != 4) { + if (argc < 2) { printf("Invalid P2P_SERV_DISC_REQ command: needs two " - "arguments (address and TLVs) or four arguments " - "(address, \"upnp\", version, search target " - "(SSDP ST:)\n"); + "or more arguments (address and TLVs)\n"); return -1; } @@ -1830,7 +1961,7 @@ static int wpa_cli_cmd_p2p_serv_disc_resp(struct wpa_ctrl *ctrl, int argc, 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)) + if (os_snprintf_error(sizeof(cmd), res)) return -1; cmd[sizeof(cmd) - 1] = '\0'; return wpa_ctrl_command(ctrl, cmd); @@ -1861,27 +1992,25 @@ static int wpa_cli_cmd_p2p_service_flush(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_p2p_service_add(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[4096]; - int res; + if (argc < 3) { + printf("Invalid P2P_SERVICE_ADD command: needs 3-6 arguments\n"); + return -1; + } + + return wpa_cli_cmd(ctrl, "P2P_SERVICE_ADD", 3, argc, argv); +} + - if (argc != 3 && argc != 4) { - printf("Invalid P2P_SERVICE_ADD command: needs three or four " +static int wpa_cli_cmd_p2p_service_rep(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + if (argc < 5 || argc > 6) { + printf("Invalid P2P_SERVICE_REP command: needs 5-6 " "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); + return wpa_cli_cmd(ctrl, "P2P_SERVICE_REP", 5, argc, argv); } @@ -1905,7 +2034,7 @@ static int wpa_cli_cmd_p2p_service_del(struct wpa_ctrl *ctrl, int argc, res = os_snprintf(cmd, sizeof(cmd), "P2P_SERVICE_DEL %s %s", argv[0], argv[1]); - if (res < 0 || (size_t) res >= sizeof(cmd)) + if (os_snprintf_error(sizeof(cmd), res)) return -1; cmd[sizeof(cmd) - 1] = '\0'; return wpa_ctrl_command(ctrl, cmd); @@ -2008,6 +2137,50 @@ static int wpa_cli_cmd_p2p_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static char ** wpa_cli_complete_p2p_set(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + const char *fields[] = { + "discoverability", + "managed", + "listen_channel", + "ssid_postfix", + "noa", + "ps", + "oppps", + "ctwindow", + "disabled", + "conc_pref", + "force_long_sd", + "peer_filter", + "cross_connect", + "go_apsd", + "client_apsd", + "disallow_freq", + "disc_int", + "per_sta_psk", + }; + int i, num_fields = ARRAY_SIZE(fields); + + if (arg == 1) { + char **res = os_calloc(num_fields + 1, sizeof(char *)); + if (res == NULL) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(fields[i]); + if (res[i] == NULL) + return res; + } + return res; + } + + if (arg == 2 && os_strncasecmp(str, "p2p_set peer_filter ", 20) == 0) + return cli_txt_list_array(&p2p_peers); + + return NULL; +} + + static int wpa_cli_cmd_p2p_flush(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_ctrl_command(ctrl, "P2P_FLUSH"); @@ -2058,6 +2231,13 @@ static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc, return wpa_cli_cmd(ctrl, "P2P_EXT_LISTEN", 0, argc, argv); } + +static int wpa_cli_cmd_p2p_remove_client(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "P2P_REMOVE_CLIENT", 1, argc, argv); +} + #endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY @@ -2076,7 +2256,7 @@ static int wpa_cli_cmd_wfd_subelem_set(struct wpa_ctrl *ctrl, int argc, res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_SET %s %s", argv[0], argc > 1 ? argv[1] : ""); - if (res < 0 || (size_t) res >= sizeof(cmd)) + if (os_snprintf_error(sizeof(cmd), res)) return -1; cmd[sizeof(cmd) - 1] = '\0'; return wpa_ctrl_command(ctrl, cmd); @@ -2097,7 +2277,7 @@ static int wpa_cli_cmd_wfd_subelem_get(struct wpa_ctrl *ctrl, int argc, res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_GET %s", argv[0]); - if (res < 0 || (size_t) res >= sizeof(cmd)) + if (os_snprintf_error(sizeof(cmd), res)) return -1; cmd[sizeof(cmd) - 1] = '\0'; return wpa_ctrl_command(ctrl, cmd); @@ -2134,6 +2314,13 @@ static int wpa_cli_cmd_interworking_connect(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_interworking_add_network(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "INTERWORKING_ADD_NETWORK", 1, argc, argv); +} + + static int wpa_cli_cmd_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_cli_cmd(ctrl, "ANQP_GET", 2, argc, argv); @@ -2182,6 +2369,37 @@ static int wpa_cli_cmd_get_nai_home_realm_list(struct wpa_ctrl *ctrl, int argc, return wpa_ctrl_command(ctrl, cmd); } + +static int wpa_cli_cmd_hs20_icon_request(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[512]; + + if (argc < 2) { + printf("Command needs two arguments (dst mac addr and " + "icon name)\n"); + return -1; + } + + if (write_cmd(cmd, sizeof(cmd), "HS20_ICON_REQUEST", argc, argv) < 0) + return -1; + + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_fetch_osu(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "FETCH_OSU"); +} + + +static int wpa_cli_cmd_cancel_fetch_osu(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "CANCEL_FETCH_OSU"); +} + #endif /* CONFIG_HS20 */ @@ -2213,6 +2431,41 @@ static int wpa_cli_cmd_tdls_teardown(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_wmm_ac_addts(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "WMM_AC_ADDTS", 3, argc, argv); +} + + +static int wpa_cli_cmd_wmm_ac_delts(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "WMM_AC_DELTS", 1, argc, argv); +} + + +static int wpa_cli_cmd_wmm_ac_status(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "WMM_AC_STATUS"); +} + + +static int wpa_cli_cmd_tdls_chan_switch(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "TDLS_CHAN_SWITCH", 2, argc, argv); +} + + +static int wpa_cli_cmd_tdls_cancel_chan_switch(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "TDLS_CANCEL_CHAN_SWITCH", 1, argc, argv); +} + + static int wpa_cli_cmd_signal_poll(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -2254,6 +2507,12 @@ static int wpa_cli_cmd_wnm_sleep(struct wpa_ctrl *ctrl, int argc, char *argv[]) return wpa_cli_cmd(ctrl, "WNM_SLEEP", 0, argc, argv); } + +static int wpa_cli_cmd_wnm_bss_query(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "WNM_BSS_QUERY", 1, argc, argv); +} + #endif /* CONFIG_WNM */ @@ -2265,6 +2524,52 @@ static int wpa_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +#ifdef ANDROID +static int wpa_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DRIVER", 1, argc, argv); +} +#endif /* ANDROID */ + + +static int wpa_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "VENDOR", 1, argc, argv); +} + + +static int wpa_cli_cmd_flush(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "FLUSH"); +} + + +static int wpa_cli_cmd_radio_work(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "RADIO_WORK", 1, argc, argv); +} + + +static int wpa_cli_cmd_neighbor_rep_request(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "NEIGHBOR_REP_REQUEST", 0, argc, argv); +} + + +static int wpa_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "ERP_FLUSH"); +} + + +static int wpa_cli_cmd_mac_rand_scan(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "MAC_RAND_SCAN", 1, argc, argv); +} + + enum wpa_cli_cmd_flags { cli_cmd_flag_none = 0x00, cli_cmd_flag_sensitive = 0x01 @@ -2312,10 +2617,13 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "quit", wpa_cli_cmd_quit, NULL, cli_cmd_flag_none, "= exit wpa_cli" }, - { "set", wpa_cli_cmd_set, NULL, + { "set", wpa_cli_cmd_set, wpa_cli_complete_set, cli_cmd_flag_none, "= set variables (shows list of variables when run without " "arguments)" }, + { "dump", wpa_cli_cmd_dump, NULL, + cli_cmd_flag_none, + "= dump config variables" }, { "get", wpa_cli_cmd_get, NULL, cli_cmd_flag_none, "<name> = get information" }, @@ -2328,9 +2636,15 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "pmksa", wpa_cli_cmd_pmksa, NULL, cli_cmd_flag_none, "= show PMKSA cache" }, + { "pmksa_flush", wpa_cli_cmd_pmksa_flush, NULL, + cli_cmd_flag_none, + "= flush PMKSA cache entries" }, { "reassociate", wpa_cli_cmd_reassociate, NULL, cli_cmd_flag_none, "= force reassociation" }, + { "reattach", wpa_cli_cmd_reattach, NULL, + cli_cmd_flag_none, + "= force reassociation back to the same BSS" }, { "preauthenticate", wpa_cli_cmd_preauthenticate, wpa_cli_complete_bss, cli_cmd_flag_none, "<BSSID> = force preauthentication" }, @@ -2354,6 +2668,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { cli_cmd_flag_sensitive, "<network id> <passphrase> = configure private key passphrase\n" " for an SSID" }, + { "sim", wpa_cli_cmd_sim, NULL, + cli_cmd_flag_sensitive, + "<network id> <pin> = report SIM operation result" }, { "bssid", wpa_cli_cmd_bssid, NULL, cli_cmd_flag_none, "<network id> <BSSID> = set preferred BSSID for an SSID" }, @@ -2391,6 +2708,10 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "get_network", wpa_cli_cmd_get_network, NULL, cli_cmd_flag_none, "<network id> <variable> = get network variables" }, + { "dup_network", wpa_cli_cmd_dup_network, NULL, + cli_cmd_flag_none, + "<src network id> <dst network id> <variable> = duplicate network variables" + }, { "list_creds", wpa_cli_cmd_list_creds, NULL, cli_cmd_flag_none, "= list configured credentials" }, @@ -2403,6 +2724,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "set_cred", wpa_cli_cmd_set_cred, NULL, cli_cmd_flag_sensitive, "<cred id> <variable> <value> = set credential variables" }, + { "get_cred", wpa_cli_cmd_get_cred, NULL, + cli_cmd_flag_none, + "<cred id> <variable> = get credential variables" }, { "save_config", wpa_cli_cmd_save_config, NULL, cli_cmd_flag_none, "= save the current configuration" }, @@ -2425,7 +2749,7 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { "<<idx> | <bssid>> = get detailed scan result info" }, { "get_capability", wpa_cli_cmd_get_capability, NULL, cli_cmd_flag_none, - "<eap/pairwise/group/key_mgmt/proto/auth_alg/channels> " + "<eap/pairwise/group/key_mgmt/proto/auth_alg/channels/freq/modes> " "= get capabilies" }, { "reconfigure", wpa_cli_cmd_reconfigure, NULL, cli_cmd_flag_none, @@ -2481,6 +2805,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "wps_nfc", wpa_cli_cmd_wps_nfc, wpa_cli_complete_bss, cli_cmd_flag_none, "[BSSID] = start Wi-Fi Protected Setup: NFC" }, + { "wps_nfc_config_token", wpa_cli_cmd_wps_nfc_config_token, NULL, + cli_cmd_flag_none, + "<WPS|NDEF> = build configuration token" }, { "wps_nfc_token", wpa_cli_cmd_wps_nfc_token, NULL, cli_cmd_flag_none, "<WPS|NDEF> = create password token" }, @@ -2493,12 +2820,10 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "nfc_get_handover_sel", wpa_cli_cmd_nfc_get_handover_sel, NULL, cli_cmd_flag_none, "<NDEF> <WPS> = create NFC handover select" }, - { "nfc_rx_handover_req", wpa_cli_cmd_nfc_rx_handover_req, NULL, + { "nfc_report_handover", wpa_cli_cmd_nfc_report_handover, NULL, cli_cmd_flag_none, - "<hexdump of payload> = report received NFC handover request" }, - { "nfc_rx_handover_sel", wpa_cli_cmd_nfc_rx_handover_sel, NULL, - cli_cmd_flag_none, - "<hexdump of payload> = report received NFC handover select" }, + "<role> <type> <hexdump of req> <hexdump of sel> = report completed " + "NFC handover" }, #endif /* CONFIG_WPS_NFC */ { "wps_reg", wpa_cli_cmd_wps_reg, wpa_cli_complete_bss, cli_cmd_flag_sensitive, @@ -2548,22 +2873,46 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "disassociate", wpa_cli_cmd_disassociate, NULL, cli_cmd_flag_none, "<addr> = disassociate a station" }, + { "chan_switch", wpa_cli_cmd_chanswitch, NULL, + cli_cmd_flag_none, + "<cs_count> <freq> [sec_channel_offset=] [center_freq1=]" + " [center_freq2=] [bandwidth=] [blocktx] [ht|vht]" + " = CSA parameters" }, #endif /* CONFIG_AP */ { "suspend", wpa_cli_cmd_suspend, NULL, cli_cmd_flag_none, "= notification of suspend/hibernate" }, { "resume", wpa_cli_cmd_resume, NULL, cli_cmd_flag_none, "= notification of resume/thaw" }, +#ifdef CONFIG_TESTING_OPTIONS { "drop_sa", wpa_cli_cmd_drop_sa, NULL, cli_cmd_flag_none, "= drop SA without deauth/disassoc (test command)" }, +#endif /* CONFIG_TESTING_OPTIONS */ { "roam", wpa_cli_cmd_roam, wpa_cli_complete_bss, cli_cmd_flag_none, "<addr> = roam to the specified BSS" }, +#ifdef CONFIG_MESH + { "mesh_interface_add", wpa_cli_cmd_mesh_interface_add, NULL, + cli_cmd_flag_none, + "[ifname] = Create a new mesh interface" }, + { "mesh_group_add", wpa_cli_cmd_mesh_group_add, NULL, + cli_cmd_flag_none, + "<network id> = join a mesh network (disable others)" }, + { "mesh_group_remove", wpa_cli_cmd_mesh_group_remove, NULL, + cli_cmd_flag_none, + "<ifname> = Remove mesh group interface" }, +#endif /* CONFIG_MESH */ #ifdef CONFIG_P2P { "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_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, NULL, cli_cmd_flag_none, "= stop P2P Devices search" }, + { "p2p_asp_provision", wpa_cli_cmd_p2p_asp_provision, NULL, + cli_cmd_flag_none, + "<addr> adv_id=<adv_id> conncap=<conncap> [info=<infodata>] = provision with a P2P ASP Device" }, + { "p2p_asp_provision_resp", wpa_cli_cmd_p2p_asp_provision_resp, NULL, + cli_cmd_flag_none, + "<addr> adv_id=<adv_id> [role<conncap>] [info=<infodata>] = provision with a P2P ASP Device" }, { "p2p_connect", wpa_cli_cmd_p2p_connect, wpa_cli_complete_p2p_connect, cli_cmd_flag_none, "<addr> <\"pbc\"|PIN> [ht40] = connect to a P2P Device" }, @@ -2600,8 +2949,12 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { "= remove all stored service entries" }, { "p2p_service_add", wpa_cli_cmd_p2p_service_add, NULL, cli_cmd_flag_none, - "<bonjour|upnp> <query|version> <response|service> = add a local " + "<bonjour|upnp|asp> <query|version> <response|service> = add a local " "service" }, + { "p2p_service_rep", wpa_cli_cmd_p2p_service_rep, NULL, + cli_cmd_flag_none, + "asp <auto> <adv_id> <svc_state> <svc_string> [<svc_info>] = replace " + "local ASP service" }, { "p2p_service_del", wpa_cli_cmd_p2p_service_del, NULL, cli_cmd_flag_none, "<bonjour|upnp> <query|version> [|service] = remove a local " @@ -2618,7 +2971,8 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "p2p_peer", wpa_cli_cmd_p2p_peer, wpa_cli_complete_p2p_peer, cli_cmd_flag_none, "<address> = show information about known P2P peer" }, - { "p2p_set", wpa_cli_cmd_p2p_set, NULL, cli_cmd_flag_none, + { "p2p_set", wpa_cli_cmd_p2p_set, wpa_cli_complete_p2p_set, + cli_cmd_flag_none, "<field> <value> = set a P2P parameter" }, { "p2p_flush", wpa_cli_cmd_p2p_flush, NULL, cli_cmd_flag_none, "= flush P2P state" }, @@ -2634,6 +2988,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, NULL, cli_cmd_flag_none, "[<period> <interval>] = set extended listen timing" }, + { "p2p_remove_client", wpa_cli_cmd_p2p_remove_client, + wpa_cli_complete_p2p_peer, cli_cmd_flag_none, + "<address|iface=address> = remove a peer from all groups" }, #endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY { "wfd_subelem_set", wpa_cli_cmd_wfd_subelem_set, NULL, @@ -2655,6 +3012,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "interworking_connect", wpa_cli_cmd_interworking_connect, wpa_cli_complete_bss, cli_cmd_flag_none, "<BSSID> = connect using Interworking credentials" }, + { "interworking_add_network", wpa_cli_cmd_interworking_add_network, + wpa_cli_complete_bss, cli_cmd_flag_none, + "<BSSID> = connect using Interworking credentials" }, { "anqp_get", wpa_cli_cmd_anqp_get, wpa_cli_complete_bss, cli_cmd_flag_none, "<addr> <info id>[,<info id>]... = request ANQP information" }, @@ -2673,6 +3033,14 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "nai_home_realm_list", wpa_cli_cmd_get_nai_home_realm_list, wpa_cli_complete_bss, cli_cmd_flag_none, "<addr> <home realm> = get HS20 nai home realm list" }, + { "hs20_icon_request", wpa_cli_cmd_hs20_icon_request, + wpa_cli_complete_bss, cli_cmd_flag_none, + "<addr> <icon name> = get Hotspot 2.0 OSU icon" }, + { "fetch_osu", wpa_cli_cmd_fetch_osu, NULL, cli_cmd_flag_none, + "= fetch OSU provider information from all APs" }, + { "cancel_fetch_osu", wpa_cli_cmd_cancel_fetch_osu, NULL, + cli_cmd_flag_none, + "= cancel fetch_osu command" }, #endif /* CONFIG_HS20 */ { "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, NULL, cli_cmd_flag_none, @@ -2686,6 +3054,25 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "tdls_teardown", wpa_cli_cmd_tdls_teardown, NULL, cli_cmd_flag_none, "<addr> = tear down TDLS with <addr>" }, + { "wmm_ac_addts", wpa_cli_cmd_wmm_ac_addts, NULL, + cli_cmd_flag_none, + "<uplink/downlink/bidi> <tsid=0..7> <up=0..7> [nominal_msdu_size=#] " + "[mean_data_rate=#] [min_phy_rate=#] [sba=#] [fixed_nominal_msdu] " + "= add WMM-AC traffic stream" }, + { "wmm_ac_delts", wpa_cli_cmd_wmm_ac_delts, NULL, + cli_cmd_flag_none, + "<tsid> = delete WMM-AC traffic stream" }, + { "wmm_ac_status", wpa_cli_cmd_wmm_ac_status, NULL, + cli_cmd_flag_none, + "= show status for Wireless Multi-Media Admission-Control" }, + { "tdls_chan_switch", wpa_cli_cmd_tdls_chan_switch, NULL, + cli_cmd_flag_none, + "<addr> <oper class> <freq> [sec_channel_offset=] [center_freq1=] " + "[center_freq2=] [bandwidth=] [ht|vht] = enable channel switching " + "with TDLS peer" }, + { "tdls_cancel_chan_switch", wpa_cli_cmd_tdls_cancel_chan_switch, NULL, + cli_cmd_flag_none, + "<addr> = disable channel switching with TDLS peer <addr>" }, { "signal_poll", wpa_cli_cmd_signal_poll, NULL, cli_cmd_flag_none, "= get signal parameters" }, @@ -2702,9 +3089,34 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { #ifdef CONFIG_WNM { "wnm_sleep", wpa_cli_cmd_wnm_sleep, NULL, cli_cmd_flag_none, "<enter/exit> [interval=#] = enter/exit WNM-Sleep mode" }, + { "wnm_bss_query", wpa_cli_cmd_wnm_bss_query, NULL, cli_cmd_flag_none, + "<query reason> = Send BSS Transition Management Query" }, #endif /* CONFIG_WNM */ { "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive, "<params..> = Sent unprocessed command" }, + { "flush", wpa_cli_cmd_flush, NULL, cli_cmd_flag_none, + "= flush wpa_supplicant state" }, +#ifdef ANDROID + { "driver", wpa_cli_cmd_driver, NULL, cli_cmd_flag_none, + "<command> = driver private commands" }, +#endif /* ANDROID */ + { "radio_work", wpa_cli_cmd_radio_work, NULL, cli_cmd_flag_none, + "= radio_work <show/add/done>" }, + { "vendor", wpa_cli_cmd_vendor, NULL, cli_cmd_flag_none, + "<vendor id> <command id> [<hex formatted command argument>] = Send vendor command" + }, + { "neighbor_rep_request", + wpa_cli_cmd_neighbor_rep_request, NULL, cli_cmd_flag_none, + "[ssid=<SSID>] = Trigger request to AP for neighboring AP report " + "(with optional given SSID, default: current SSID)" + }, + { "erp_flush", wpa_cli_cmd_erp_flush, NULL, cli_cmd_flag_none, + "= flush ERP keys" }, + { "mac_rand_scan", + wpa_cli_cmd_mac_rand_scan, NULL, cli_cmd_flag_none, + "<scan|sched|pno|all> enable=<0/1> [addr=mac-address " + "mask=mac-address-mask] = scan MAC randomization" + }, { NULL, NULL, NULL, cli_cmd_flag_none, NULL } }; @@ -2763,9 +3175,12 @@ static char ** wpa_list_cmd_list(void) { char **res; int i, count; + struct cli_txt_entry *e; - count = sizeof(wpa_cli_commands) / sizeof(wpa_cli_commands[0]); - res = os_calloc(count, sizeof(char *)); + count = ARRAY_SIZE(wpa_cli_commands); + count += dl_list_len(&p2p_groups); + count += dl_list_len(&ifnames); + res = os_calloc(count + 1, sizeof(char *)); if (res == NULL) return NULL; @@ -2775,6 +3190,22 @@ static char ** wpa_list_cmd_list(void) break; } + dl_list_for_each(e, &p2p_groups, struct cli_txt_entry, list) { + size_t len = 8 + os_strlen(e->txt); + res[i] = os_malloc(len); + if (res[i] == NULL) + break; + os_snprintf(res[i], len, "ifname=%s", e->txt); + i++; + } + + dl_list_for_each(e, &ifnames, struct cli_txt_entry, list) { + res[i] = os_strdup(e->txt); + if (res[i] == NULL) + break; + i++; + } + return res; } @@ -2806,6 +3237,14 @@ static char ** wpa_cli_edit_completion_cb(void *ctx, const char *str, int pos) const char *end; char *cmd; + if (pos > 7 && os_strncasecmp(str, "IFNAME=", 7) == 0) { + end = os_strchr(str, ' '); + if (end && pos > end - str) { + pos -= end - str + 1; + str = end + 1; + } + } + end = os_strchr(str, ' '); if (end == NULL || str + pos < end) return wpa_list_cmd_list(); @@ -2827,6 +3266,16 @@ static int wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) int count; int ret = 0; + if (argc > 1 && os_strncasecmp(argv[0], "IFNAME=", 7) == 0) { + ifname_prefix = argv[0] + 7; + argv = &argv[1]; + argc--; + } else + ifname_prefix = NULL; + + if (argc == 0) + return -1; + count = 0; cmd = wpa_cli_commands; while (cmd->cmd) { @@ -2875,28 +3324,19 @@ static int str_match(const char *a, const char *b) static int wpa_cli_exec(const char *program, const char *arg1, const char *arg2) { - char *cmd; + char *arg; size_t len; int res; - int ret = 0; - len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3; - cmd = os_malloc(len); - if (cmd == NULL) - return -1; - res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2); - if (res < 0 || (size_t) res >= len) { - os_free(cmd); + len = os_strlen(arg1) + os_strlen(arg2) + 2; + arg = os_malloc(len); + if (arg == NULL) return -1; - } - cmd[len - 1] = '\0'; -#ifndef _WIN32_WCE - if (system(cmd) < 0) - ret = -1; -#endif /* _WIN32_WCE */ - os_free(cmd); + os_snprintf(arg, len, "%s %s", arg1, arg2); + res = os_exec(program, arg, 1); + os_free(arg); - return ret; + return res; } @@ -2904,15 +3344,29 @@ static void wpa_cli_action_process(const char *msg) { const char *pos; char *copy = NULL, *id, *pos2; + const char *ifname = ctrl_ifname; + char ifname_buf[100]; pos = msg; + if (os_strncmp(pos, "IFNAME=", 7) == 0) { + const char *end; + end = os_strchr(pos + 7, ' '); + if (end && (unsigned int) (end - pos) < sizeof(ifname_buf)) { + pos += 7; + os_memcpy(ifname_buf, pos, end - pos); + ifname_buf[end - pos] = '\0'; + ifname = ifname_buf; + pos = end + 1; + } + } if (*pos == '<') { + const char *prev = pos; /* skip priority */ pos = os_strchr(pos, '>'); if (pos) pos++; else - pos = msg; + pos = prev; } if (str_match(pos, WPA_EVENT_CONNECTED)) { @@ -2946,34 +3400,48 @@ static void wpa_cli_action_process(const char *msg) os_setenv("WPA_CTRL_DIR", ctrl_iface_dir, 1); - if (!wpa_cli_connected || new_id != wpa_cli_last_id) { + if (wpa_cli_connected <= 0 || new_id != wpa_cli_last_id) { wpa_cli_connected = 1; wpa_cli_last_id = new_id; - wpa_cli_exec(action_file, ctrl_ifname, "CONNECTED"); + wpa_cli_exec(action_file, ifname, "CONNECTED"); } } else if (str_match(pos, WPA_EVENT_DISCONNECTED)) { if (wpa_cli_connected) { wpa_cli_connected = 0; - wpa_cli_exec(action_file, ctrl_ifname, "DISCONNECTED"); + wpa_cli_exec(action_file, ifname, "DISCONNECTED"); } - } else if (str_match(pos, P2P_EVENT_GROUP_STARTED)) { + } else if (str_match(pos, MESH_GROUP_STARTED)) { wpa_cli_exec(action_file, ctrl_ifname, pos); - } else if (str_match(pos, P2P_EVENT_GROUP_REMOVED)) { + } else if (str_match(pos, MESH_GROUP_REMOVED)) { wpa_cli_exec(action_file, ctrl_ifname, pos); - } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_ENABLE)) { + } else if (str_match(pos, MESH_PEER_CONNECTED)) { wpa_cli_exec(action_file, ctrl_ifname, pos); - } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) { + } else if (str_match(pos, MESH_PEER_DISCONNECTED)) { wpa_cli_exec(action_file, ctrl_ifname, pos); + } else if (str_match(pos, P2P_EVENT_GROUP_STARTED)) { + wpa_cli_exec(action_file, ifname, pos); + } else if (str_match(pos, P2P_EVENT_GROUP_REMOVED)) { + wpa_cli_exec(action_file, ifname, pos); + } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_ENABLE)) { + wpa_cli_exec(action_file, ifname, pos); + } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) { + wpa_cli_exec(action_file, ifname, pos); } else if (str_match(pos, P2P_EVENT_GO_NEG_FAILURE)) { - wpa_cli_exec(action_file, ctrl_ifname, pos); + wpa_cli_exec(action_file, ifname, pos); } else if (str_match(pos, WPS_EVENT_SUCCESS)) { - wpa_cli_exec(action_file, ctrl_ifname, pos); + wpa_cli_exec(action_file, ifname, pos); } else if (str_match(pos, WPS_EVENT_FAIL)) { - wpa_cli_exec(action_file, ctrl_ifname, pos); + wpa_cli_exec(action_file, ifname, pos); } else if (str_match(pos, AP_STA_CONNECTED)) { - wpa_cli_exec(action_file, ctrl_ifname, pos); + wpa_cli_exec(action_file, ifname, pos); } else if (str_match(pos, AP_STA_DISCONNECTED)) { - wpa_cli_exec(action_file, ctrl_ifname, pos); + wpa_cli_exec(action_file, ifname, pos); + } else if (str_match(pos, ESS_DISASSOC_IMMINENT)) { + wpa_cli_exec(action_file, ifname, pos); + } else if (str_match(pos, HS20_SUBSCRIPTION_REMEDIATION)) { + wpa_cli_exec(action_file, ifname, pos); + } else if (str_match(pos, HS20_DEAUTH_IMMINENT_NOTICE)) { + wpa_cli_exec(action_file, ifname, pos); } else if (str_match(pos, WPA_EVENT_TERMINATING)) { printf("wpa_supplicant is terminating - stop monitoring\n"); wpa_cli_quit = 1; @@ -3105,7 +3573,7 @@ static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor) return; } while (wpa_ctrl_pending(ctrl) > 0) { - char buf[256]; + char buf[4096]; size_t len = sizeof(buf) - 1; if (wpa_ctrl_recv(ctrl, buf, &len) == 0) { buf[len] = '\0'; @@ -3169,10 +3637,18 @@ static int tokenize_cmd(char *cmd, char *argv[]) static void wpa_cli_ping(void *eloop_ctx, void *timeout_ctx) { - 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) { + int res; + char *prefix = ifname_prefix; + + ifname_prefix = NULL; + res = _wpa_ctrl_command(ctrl_conn, "PING", 0); + ifname_prefix = prefix; + if (res) { + printf("Connection to wpa_supplicant lost - trying to " + "reconnect\n"); + wpa_cli_close_connection(); + } } if (!ctrl_conn) wpa_cli_reconnect(); @@ -3235,24 +3711,94 @@ static void start_edit(void) } +static void update_bssid_list(struct wpa_ctrl *ctrl) +{ + char buf[4096]; + size_t len = sizeof(buf); + int ret; + char *cmd = "BSS RANGE=ALL MASK=0x2"; + char *pos, *end; + + if (ctrl == NULL) + return; + ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, NULL); + if (ret < 0) + return; + buf[len] = '\0'; + + pos = buf; + while (pos) { + pos = os_strstr(pos, "bssid="); + if (pos == NULL) + break; + pos += 6; + end = os_strchr(pos, '\n'); + if (end == NULL) + break; + *end = '\0'; + cli_txt_list_add(&bsses, pos); + pos = end + 1; + } +} + + +static void update_ifnames(struct wpa_ctrl *ctrl) +{ + char buf[4096]; + size_t len = sizeof(buf); + int ret; + char *cmd = "INTERFACES"; + char *pos, *end; + char txt[200]; + + cli_txt_list_flush(&ifnames); + + if (ctrl == NULL) + return; + ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len, NULL); + if (ret < 0) + return; + buf[len] = '\0'; + + pos = buf; + while (pos) { + end = os_strchr(pos, '\n'); + if (end == NULL) + break; + *end = '\0'; + ret = os_snprintf(txt, sizeof(txt), "ifname=%s", pos); + if (!os_snprintf_error(sizeof(txt), ret)) + cli_txt_list_add(&ifnames, txt); + pos = end + 1; + } +} + + static void try_connection(void *eloop_ctx, void *timeout_ctx) { + if (ctrl_conn) + goto done; + if (ctrl_ifname == NULL) ctrl_ifname = wpa_cli_get_default_ifname(); if (!wpa_cli_open_connection(ctrl_ifname, 1) == 0) { if (!warning_displayed) { printf("Could not connect to wpa_supplicant: " - "%s - re-trying\n", ctrl_ifname); + "%s - re-trying\n", + ctrl_ifname ? ctrl_ifname : "(nil)"); warning_displayed = 1; } eloop_register_timeout(1, 0, try_connection, NULL, NULL); return; } + update_bssid_list(ctrl_conn); + if (warning_displayed) printf("Connection established.\n"); +done: start_edit(); } @@ -3268,6 +3814,7 @@ static void wpa_cli_interactive(void) cli_txt_list_flush(&p2p_peers); cli_txt_list_flush(&p2p_groups); cli_txt_list_flush(&bsses); + cli_txt_list_flush(&ifnames); if (edit_started) edit_deinit(hfile, wpa_cli_edit_filter_history_cb); os_free(hfile); @@ -3374,7 +3921,7 @@ static char * wpa_cli_get_default_ifname(void) #endif /* CONFIG_CTRL_IFACE_UNIX */ #ifdef CONFIG_CTRL_IFACE_NAMED_PIPE - char buf[2048], *pos; + char buf[4096], *pos; size_t len; struct wpa_ctrl *ctrl; int ret; @@ -3468,6 +4015,24 @@ int main(int argc, char *argv[]) global, strerror(errno)); return -1; } + + if (interactive) { + update_ifnames(ctrl_conn); + mon_conn = wpa_ctrl_open(global); + if (mon_conn) { + if (wpa_ctrl_attach(mon_conn) == 0) { + wpa_cli_attached = 1; + eloop_register_read_sock( + wpa_ctrl_get_fd(mon_conn), + wpa_cli_mon_receive, + NULL, NULL); + } else { + printf("Failed to open monitor " + "connection through global " + "control interface\n"); + } + } + } } eloop_register_signal_terminate(wpa_cli_terminate, NULL); @@ -3482,7 +4047,8 @@ int main(int argc, char *argv[]) wpa_cli_open_connection(ctrl_ifname, 0) < 0) { fprintf(stderr, "Failed to connect to non-global " "ctrl_ifname: %s error: %s\n", - ctrl_ifname, strerror(errno)); + ctrl_ifname ? ctrl_ifname : "(nil)", + strerror(errno)); return -1; } diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c index ad6a080b942c4..ac38d69d5d1b2 100644 --- a/wpa_supplicant/wpa_priv.c +++ b/wpa_supplicant/wpa_priv.c @@ -202,7 +202,9 @@ static void wpa_priv_cmd_associate(struct wpa_priv_interface *iface, if (assoc->ssid_len > 32) return; params.ssid_len = assoc->ssid_len; - params.freq = assoc->freq; + params.freq.mode = assoc->hwmode; + params.freq.freq = assoc->freq; + params.freq.channel = assoc->channel; if (assoc->wpa_ie_len) { params.wpa_ie = (u8 *) (assoc + 1); params.wpa_ie_len = assoc->wpa_ie_len; @@ -333,7 +335,7 @@ static void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf, msg.msg_namelen = sizeof(iface->l2_addr); if (sendmsg(iface->fd, &msg, 0) < 0) { - perror("sendmsg(l2 rx)"); + wpa_printf(MSG_ERROR, "sendmsg(l2 rx): %s", strerror(errno)); } } @@ -465,7 +467,7 @@ static void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx) res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from, &fromlen); if (res < 0) { - perror("recvfrom"); + wpa_printf(MSG_ERROR, "recvfrom: %s", strerror(errno)); return; } @@ -552,8 +554,6 @@ static void wpa_priv_interface_deinit(struct wpa_priv_interface *iface) } -extern struct wpa_driver_ops *wpa_drivers[]; - static struct wpa_priv_interface * wpa_priv_interface_init(const char *dir, const char *params) { @@ -573,13 +573,11 @@ wpa_priv_interface_init(const char *dir, const char *params) iface->fd = -1; len = pos - params; - iface->driver_name = os_malloc(len + 1); + iface->driver_name = dup_binstr(params, len); if (iface->driver_name == NULL) { wpa_priv_interface_deinit(iface); return NULL; } - os_memcpy(iface->driver_name, params, len); - iface->driver_name[len] = '\0'; for (i = 0; wpa_drivers[i]; i++) { if (os_strcmp(iface->driver_name, @@ -617,7 +615,7 @@ wpa_priv_interface_init(const char *dir, const char *params) iface->fd = socket(PF_UNIX, SOCK_DGRAM, 0); if (iface->fd < 0) { - perror("socket(PF_UNIX)"); + wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno)); wpa_priv_interface_deinit(iface); return NULL; } @@ -635,15 +633,16 @@ wpa_priv_interface_init(const char *dir, const char *params) "allow connections - assuming it was " "leftover from forced program termination"); if (unlink(iface->sock_name) < 0) { - perror("unlink[ctrl_iface]"); - wpa_printf(MSG_ERROR, "Could not unlink " - "existing ctrl_iface socket '%s'", - iface->sock_name); + wpa_printf(MSG_ERROR, + "Could not unlink existing ctrl_iface socket '%s': %s", + iface->sock_name, strerror(errno)); goto fail; } if (bind(iface->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("wpa-priv-iface-init: bind(PF_UNIX)"); + wpa_printf(MSG_ERROR, + "wpa-priv-iface-init: bind(PF_UNIX): %s", + strerror(errno)); goto fail; } wpa_printf(MSG_DEBUG, "Successfully replaced leftover " @@ -658,7 +657,7 @@ wpa_priv_interface_init(const char *dir, const char *params) } if (chmod(iface->sock_name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) { - perror("chmod"); + wpa_printf(MSG_ERROR, "chmod: %s", strerror(errno)); goto fail; } @@ -690,7 +689,8 @@ static int wpa_priv_send_event(struct wpa_priv_interface *iface, int event, msg.msg_namelen = sizeof(iface->drv_addr); if (sendmsg(iface->fd, &msg, 0) < 0) { - perror("sendmsg(wpas_socket)"); + wpa_printf(MSG_ERROR, "sendmsg(wpas_socket): %s", + strerror(errno)); return -1; } @@ -905,7 +905,8 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, msg.msg_namelen = sizeof(iface->drv_addr); if (sendmsg(iface->fd, &msg, 0) < 0) - perror("sendmsg(wpas_socket)"); + wpa_printf(MSG_ERROR, "sendmsg(wpas_socket): %s", + strerror(errno)); } @@ -948,8 +949,6 @@ static void usage(void) } -extern int wpa_debug_level; - int main(int argc, char *argv[]) { int c, i; diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 0fb4d0fd4705e..19fb8900bd756 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -17,6 +17,7 @@ #include "crypto/sha1.h" #include "eapol_supp/eapol_supp_sm.h" #include "eap_peer/eap.h" +#include "eap_peer/eap_proxy.h" #include "eap_server/eap_methods.h" #include "rsn_supp/wpa.h" #include "eloop.h" @@ -32,6 +33,7 @@ #include "rsn_supp/pmksa_cache.h" #include "common/wpa_ctrl.h" #include "common/ieee802_11_defs.h" +#include "common/hw_features_common.h" #include "p2p/p2p.h" #include "blacklist.h" #include "wpas_glue.h" @@ -49,10 +51,13 @@ #include "scan.h" #include "offchannel.h" #include "hs20_supplicant.h" +#include "wnm_sta.h" +#include "wpas_kay.h" +#include "mesh.h" const char *wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" -"Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> and contributors"; +"Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors"; const char *wpa_supplicant_license = "This software may be distributed under the terms of the BSD license.\n" @@ -102,11 +107,6 @@ const char *wpa_supplicant_full_license5 = "\n"; #endif /* CONFIG_NO_STDOUT_DEBUG */ -extern int wpa_debug_level; -extern int wpa_debug_show_keys; -extern int wpa_debug_timestamp; -extern struct wpa_driver_ops *wpa_drivers[]; - /* Configure default/group WEP keys for static WEP */ int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { @@ -126,13 +126,14 @@ int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) } -static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid) +int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) { u8 key[32]; size_t keylen; enum wpa_alg alg; u8 seq[6] = { 0 }; + int ret; /* IBSS/WPA-None uses only one key (Group) for both receiving and * sending unicast and multicast packets. */ @@ -176,7 +177,9 @@ static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, /* TODO: should actually remember the previously used seq#, both for TX * and RX from each STA.. */ - return wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen); + ret = wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen); + os_memset(key, 0, sizeof(key)); + return ret; } @@ -198,17 +201,6 @@ static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx) * So, wait a second until scanning again. */ wpa_supplicant_req_scan(wpa_s, 1, 0); - -#ifdef CONFIG_P2P - if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled && - wpa_s->global->p2p != NULL) { - wpa_s->global->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 " - "continued after timed out authentication"); - } - } -#endif /* CONFIG_P2P */ } @@ -224,7 +216,7 @@ static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx) void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s, int sec, int usec) { - if (wpa_s->conf && wpa_s->conf->ap_scan == 0 && + if (wpa_s->conf->ap_scan == 0 && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) return; @@ -300,17 +292,37 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s) EAPOL_REQUIRE_KEY_BROADCAST; } - if (wpa_s->conf && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED) eapol_conf.required_keys = 0; } - if (wpa_s->conf) - eapol_conf.fast_reauth = wpa_s->conf->fast_reauth; + eapol_conf.fast_reauth = wpa_s->conf->fast_reauth; eapol_conf.workaround = ssid->eap_workaround; eapol_conf.eap_disabled = !wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) && wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA && wpa_s->key_mgmt != WPA_KEY_MGMT_WPS; + eapol_conf.external_sim = wpa_s->conf->external_sim; + +#ifdef CONFIG_WPS + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) { + eapol_conf.wps |= EAPOL_LOCAL_WPS_IN_USE; + if (wpa_s->current_bss) { + struct wpabuf *ie; + ie = wpa_bss_get_vendor_ie_multi(wpa_s->current_bss, + WPS_IE_VENDOR_TYPE); + if (ie) { + if (wps_is_20(ie)) + eapol_conf.wps |= + EAPOL_PEER_IS_WPS20_AP; + wpabuf_free(ie); + } + } + } +#endif /* CONFIG_WPS */ + eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf); + + ieee802_1x_alloc_kay_sm(wpa_s, ssid); #endif /* IEEE8021X_EAPOL */ } @@ -386,6 +398,8 @@ void free_hw_features(struct wpa_supplicant *wpa_s) static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) { + int i; + bgscan_deinit(wpa_s); autoscan_deinit(wpa_s); scard_deinit(wpa_s->scard); @@ -398,6 +412,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) l2_packet_deinit(wpa_s->l2_br); wpa_s->l2_br = NULL; } +#ifdef CONFIG_TESTING_OPTIONS + l2_packet_deinit(wpa_s->l2_test); + wpa_s->l2_test = NULL; +#endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->conf != NULL) { struct wpa_ssid *ssid; @@ -408,6 +426,9 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) os_free(wpa_s->confname); wpa_s->confname = NULL; + os_free(wpa_s->confanother); + wpa_s->confanother = NULL; + wpa_sm_set_eapol(wpa_s->wpa, NULL); eapol_sm_deinit(wpa_s->eapol); wpa_s->eapol = NULL; @@ -418,6 +439,7 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpa_tdls_deinit(wpa_s->wpa); #endif /* CONFIG_TDLS */ + wmm_ac_clear_saved_tspecs(wpa_s); pmksa_candidate_free(wpa_s->wpa); wpa_sm_deinit(wpa_s->wpa); wpa_s->wpa = NULL; @@ -425,6 +447,7 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpa_bss_deinit(wpa_s); + wpa_supplicant_cancel_delayed_sched_scan(wpa_s); wpa_supplicant_cancel_scan(wpa_s); wpa_supplicant_cancel_auth_timeout(wpa_s); eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL); @@ -449,9 +472,7 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) 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); @@ -462,11 +483,21 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) os_free(wpa_s->next_scan_freqs); wpa_s->next_scan_freqs = NULL; + os_free(wpa_s->manual_scan_freqs); + wpa_s->manual_scan_freqs = NULL; + + os_free(wpa_s->manual_sched_scan_freqs); + wpa_s->manual_sched_scan_freqs = NULL; + + wpas_mac_addr_rand_scan_clear(wpa_s, MAC_ADDR_RAND_ALL); + gas_query_deinit(wpa_s->gas); wpa_s->gas = NULL; free_hw_features(wpa_s); + ieee802_1x_dealloc_kay_sm(wpa_s); + os_free(wpa_s->bssid_filter); wpa_s->bssid_filter = NULL; @@ -476,14 +507,31 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpa_s->disallow_aps_ssid = NULL; wnm_bss_keep_alive_deinit(wpa_s); +#ifdef CONFIG_WNM + wnm_deallocate_memory(wpa_s); +#endif /* CONFIG_WNM */ ext_password_deinit(wpa_s->ext_pw); wpa_s->ext_pw = NULL; wpabuf_free(wpa_s->last_gas_resp); + wpa_s->last_gas_resp = NULL; + wpabuf_free(wpa_s->prev_gas_resp); + wpa_s->prev_gas_resp = NULL; os_free(wpa_s->last_scan_res); wpa_s->last_scan_res = NULL; + +#ifdef CONFIG_HS20 + hs20_deinit(wpa_s); +#endif /* CONFIG_HS20 */ + + for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) { + wpabuf_free(wpa_s->vendor_elem[i]); + wpa_s->vendor_elem[i] = NULL; + } + + wmm_ac_notify_disassoc(wpa_s); } @@ -497,29 +545,23 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) */ void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr) { - 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 - * are set or just after association or something similar. This - * shows up in group key handshake failing often because of the - * 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_dbg(wpa_s, MSG_DEBUG, "No keys have been configured - " - "skip key clearing"); - return; - } + int i, max; - /* MLME-DELETEKEYS.request */ - 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, NULL, 4, 0, NULL, 0, NULL, 0); - wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL, 0); + max = 6; +#else /* CONFIG_IEEE80211W */ + max = 4; #endif /* CONFIG_IEEE80211W */ - if (addr) { + + /* MLME-DELETEKEYS.request */ + for (i = 0; i < max; i++) { + if (wpa_s->keys_cleared & BIT(i)) + continue; + wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, i, 0, NULL, 0, + NULL, 0); + } + if (!(wpa_s->keys_cleared & BIT(0)) && addr && + !is_zero_ether_addr(addr)) { wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL, 0); /* MLME-SETPROTECTION.request(None) */ @@ -528,7 +570,7 @@ void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr) MLME_SETPROTECTION_PROTECT_TYPE_NONE, MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); } - wpa_s->keys_cleared = 1; + wpa_s->keys_cleared = (u32) -1; } @@ -570,14 +612,26 @@ const char * wpa_supplicant_state_txt(enum wpa_states state) static void wpa_supplicant_start_bgscan(struct wpa_supplicant *wpa_s) { + const char *name; + + if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) + name = wpa_s->current_ssid->bgscan; + else + name = wpa_s->conf->bgscan; + if (name == NULL || name[0] == '\0') + return; if (wpas_driver_bss_selection(wpa_s)) return; if (wpa_s->current_ssid == wpa_s->bgscan_ssid) return; +#ifdef CONFIG_P2P + if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) + return; +#endif /* CONFIG_P2P */ bgscan_deinit(wpa_s); - if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) { - if (bgscan_init(wpa_s, wpa_s->current_ssid)) { + if (wpa_s->current_ssid) { + if (bgscan_init(wpa_s, wpa_s->current_ssid, name)) { wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize " "bgscan"); /* @@ -651,12 +705,23 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, wpa_supplicant_state_txt(wpa_s->wpa_state), wpa_supplicant_state_txt(state)); + if (state == WPA_INTERFACE_DISABLED) { + /* Assure normal scan when interface is restored */ + wpa_s->normal_scans = 0; + } + + if (state == WPA_COMPLETED) { + wpas_connect_work_done(wpa_s); + /* Reinitialize normal_scan counter */ + wpa_s->normal_scans = 0; + } + if (state != WPA_SCANNING) wpa_supplicant_notify_scanning(wpa_s, 0); if (state == WPA_COMPLETED && wpa_s->new_connection) { -#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) struct wpa_ssid *ssid = wpa_s->current_ssid; +#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG) wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to " MACSTR " completed [id=%d id_str=%s]", MAC2STR(wpa_s->bssid), @@ -671,9 +736,8 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, wpa_drv_set_supp_port(wpa_s, 1); #endif /* IEEE8021X_EAPOL */ wpa_s->after_wps = 0; -#ifdef CONFIG_P2P + wpa_s->known_wps_freq = 0; wpas_p2p_completed(wpa_s); -#endif /* CONFIG_P2P */ sme_sched_obss_scan(wpa_s, 1); } else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING || @@ -690,7 +754,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, #ifdef CONFIG_BGSCAN if (state == WPA_COMPLETED) wpa_supplicant_start_bgscan(wpa_s); - else + else if (state < WPA_ASSOCIATED) wpa_supplicant_stop_bgscan(wpa_s); #endif /* CONFIG_BGSCAN */ @@ -700,9 +764,18 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, if (state == WPA_DISCONNECTED || state == WPA_INACTIVE) wpa_supplicant_start_autoscan(wpa_s); + if (old_state >= WPA_ASSOCIATED && wpa_s->wpa_state < WPA_ASSOCIATED) + wmm_ac_notify_disassoc(wpa_s); + if (wpa_s->wpa_state != old_state) { wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state); + /* + * Notify the P2P Device interface about a state change in one + * of the interfaces. + */ + wpas_p2p_indicate_state_change(wpa_s); + if (wpa_s->wpa_state == WPA_COMPLETED || old_state == WPA_COMPLETED) wpas_notify_auth_changed(wpa_s); @@ -716,9 +789,15 @@ void wpa_supplicant_terminate_proc(struct wpa_global *global) #ifdef CONFIG_WPS struct wpa_supplicant *wpa_s = global->ifaces; while (wpa_s) { + struct wpa_supplicant *next = wpa_s->next; if (wpas_wps_terminate_pending(wpa_s) == 1) pending = 1; - wpa_s = wpa_s->next; +#ifdef CONFIG_P2P + if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE || + (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group)) + wpas_p2p_disconnect(wpa_s); +#endif /* CONFIG_P2P */ + wpa_s = next; } #endif /* CONFIG_WPS */ if (pending) @@ -769,12 +848,14 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) if (wpa_s->confname == NULL) return -1; - conf = wpa_config_read(wpa_s->confname); + conf = wpa_config_read(wpa_s->confname, NULL); if (conf == NULL) { wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration " "file '%s' - exiting", wpa_s->confname); return -1; } + wpa_config_read(wpa_s->confanother, conf); + conf->changed_parameters = (unsigned int) -1; reconf_ctrl = !!conf->ctrl_interface != !!wpa_s->conf->ctrl_interface @@ -789,13 +870,14 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) eapol_sm_invalidate_cached_session(wpa_s->eapol); if (wpa_s->current_ssid) { + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); } /* * TODO: should notify EAPOL SM about changes in opensc_engine_path, - * pkcs11_engine_path, pkcs11_module_path. + * pkcs11_engine_path, pkcs11_module_path, openssl_ciphers. */ if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { /* @@ -845,54 +927,6 @@ static void wpa_supplicant_reconfig(int sig, void *signal_ctx) } -enum wpa_cipher cipher_suite2driver(int cipher) -{ - switch (cipher) { - case WPA_CIPHER_NONE: - return CIPHER_NONE; - case WPA_CIPHER_WEP40: - return CIPHER_WEP40; - case WPA_CIPHER_WEP104: - return CIPHER_WEP104; - case WPA_CIPHER_CCMP: - return CIPHER_CCMP; - case WPA_CIPHER_GCMP: - return CIPHER_GCMP; - case WPA_CIPHER_TKIP: - default: - return CIPHER_TKIP; - } -} - - -enum wpa_key_mgmt key_mgmt2driver(int key_mgmt) -{ - switch (key_mgmt) { - case WPA_KEY_MGMT_NONE: - return KEY_MGMT_NONE; - case WPA_KEY_MGMT_IEEE8021X_NO_WPA: - return KEY_MGMT_802_1X_NO_WPA; - case WPA_KEY_MGMT_IEEE8021X: - return KEY_MGMT_802_1X; - case WPA_KEY_MGMT_WPA_NONE: - return KEY_MGMT_WPA_NONE; - case WPA_KEY_MGMT_FT_IEEE8021X: - return KEY_MGMT_FT_802_1X; - case WPA_KEY_MGMT_FT_PSK: - return KEY_MGMT_FT_PSK; - case WPA_KEY_MGMT_IEEE8021X_SHA256: - return KEY_MGMT_802_1X_SHA256; - case WPA_KEY_MGMT_PSK_SHA256: - return KEY_MGMT_PSK_SHA256; - case WPA_KEY_MGMT_WPS: - return KEY_MGMT_WPS; - case WPA_KEY_MGMT_PSK: - default: - return KEY_MGMT_PSK; - } -} - - static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_ie_data *ie) @@ -929,9 +963,7 @@ static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IEEE80211W if (!(ie->capabilities & WPA_CAPABILITY_MFPC) && - (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? - wpa_s->conf->pmf : ssid->ieee80211w) == - MGMT_FRAME_PROTECTION_REQUIRED) { + wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) { wpa_msg(wpa_s, MSG_INFO, "WPA: Driver associated with an AP " "that does not support management frame protection - " "reject"); @@ -963,13 +995,14 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, { struct wpa_ie_data ie; int sel, proto; - const u8 *bss_wpa, *bss_rsn; + const u8 *bss_wpa, *bss_rsn, *bss_osen; if (bss) { bss_wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); + bss_osen = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); } else - bss_wpa = bss_rsn = NULL; + bss_wpa = bss_rsn = bss_osen = NULL; if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) && wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 && @@ -979,17 +1012,63 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, 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 && + 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_dbg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0"); proto = WPA_PROTO_WPA; +#ifdef CONFIG_HS20 + } else if (bss_osen && (ssid->proto & WPA_PROTO_OSEN)) { + wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using OSEN"); + /* TODO: parse OSEN element */ + os_memset(&ie, 0, sizeof(ie)); + ie.group_cipher = WPA_CIPHER_CCMP; + ie.pairwise_cipher = WPA_CIPHER_CCMP; + ie.key_mgmt = WPA_KEY_MGMT_OSEN; + proto = WPA_PROTO_OSEN; +#endif /* CONFIG_HS20 */ } else if (bss) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN"); + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: ssid proto=0x%x pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x", + ssid->proto, ssid->pairwise_cipher, ssid->group_cipher, + ssid->key_mgmt); + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: BSS " MACSTR " ssid='%s'%s%s%s", + MAC2STR(bss->bssid), + wpa_ssid_txt(bss->ssid, bss->ssid_len), + bss_wpa ? " WPA" : "", + bss_rsn ? " RSN" : "", + bss_osen ? " OSEN" : ""); + if (bss_rsn) { + wpa_hexdump(MSG_DEBUG, "RSN", bss_rsn, 2 + bss_rsn[1]); + if (wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Could not parse RSN element"); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, + "RSN: pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x", + ie.pairwise_cipher, ie.group_cipher, + ie.key_mgmt); + } + } + if (bss_wpa) { + wpa_hexdump(MSG_DEBUG, "WPA", bss_wpa, 2 + bss_wpa[1]); + if (wpa_parse_wpa_ie(bss_wpa, 2 + bss_wpa[1], &ie)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Could not parse WPA element"); + } else { + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x", + ie.pairwise_cipher, ie.group_cipher, + ie.key_mgmt); + } + } return -1; } else { - if (ssid->proto & WPA_PROTO_RSN) + if (ssid->proto & WPA_PROTO_OSEN) + proto = WPA_PROTO_OSEN; + else if (ssid->proto & WPA_PROTO_RSN) proto = WPA_PROTO_RSN; else proto = WPA_PROTO_WPA; @@ -1022,7 +1101,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, 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)); + !!(ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN))); if (bss || !wpa_s->ap_ies_from_associnfo) { if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa, @@ -1033,45 +1112,24 @@ 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_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP"); - } else if (sel & WPA_CIPHER_GCMP) { - wpa_s->group_cipher = WPA_CIPHER_GCMP; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK GCMP"); - } else if (sel & WPA_CIPHER_TKIP) { - wpa_s->group_cipher = WPA_CIPHER_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_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP104"); - } else if (sel & WPA_CIPHER_WEP40) { - wpa_s->group_cipher = WPA_CIPHER_WEP40; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP40"); - } else { + wpa_s->group_cipher = wpa_pick_group_cipher(sel); + if (wpa_s->group_cipher < 0) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select group " "cipher"); return -1; } + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using GTK %s", + wpa_cipher_txt(wpa_s->group_cipher)); sel = ie.pairwise_cipher & ssid->pairwise_cipher; - if (sel & WPA_CIPHER_CCMP) { - wpa_s->pairwise_cipher = WPA_CIPHER_CCMP; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP"); - } else if (sel & WPA_CIPHER_GCMP) { - wpa_s->pairwise_cipher = WPA_CIPHER_GCMP; - wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK GCMP"); - } else if (sel & WPA_CIPHER_TKIP) { - wpa_s->pairwise_cipher = WPA_CIPHER_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_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK NONE"); - } else { + wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(sel, 1); + if (wpa_s->pairwise_cipher < 0) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select pairwise " "cipher"); return -1; } + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using PTK %s", + wpa_cipher_txt(wpa_s->pairwise_cipher)); sel = ie.key_mgmt & ssid->key_mgmt; #ifdef CONFIG_SAE @@ -1079,6 +1137,18 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE); #endif /* CONFIG_SAE */ if (0) { +#ifdef CONFIG_SUITEB192 + } else if (sel & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { + wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: using KEY_MGMT 802.1X with Suite B (192-bit)"); +#endif /* CONFIG_SUITEB192 */ +#ifdef CONFIG_SUITEB + } else if (sel & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { + wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: using KEY_MGMT 802.1X with Suite B"); +#endif /* CONFIG_SUITEB */ #ifdef CONFIG_IEEE80211R } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; @@ -1114,6 +1184,11 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, } else if (sel & WPA_KEY_MGMT_WPA_NONE) { wpa_s->key_mgmt = WPA_KEY_MGMT_WPA_NONE; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE"); +#ifdef CONFIG_HS20 + } else if (sel & WPA_KEY_MGMT_OSEN) { + wpa_s->key_mgmt = WPA_KEY_MGMT_OSEN; + wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using KEY_MGMT OSEN"); +#endif /* CONFIG_HS20 */ } else { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select " "authenticated key management type"); @@ -1127,14 +1202,25 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IEEE80211W sel = ie.mgmt_group_cipher; - if ((ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? - wpa_s->conf->pmf : ssid->ieee80211w) == NO_MGMT_FRAME_PROTECTION || + if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION || !(ie.capabilities & WPA_CAPABILITY_MFPC)) sel = 0; if (sel & WPA_CIPHER_AES_128_CMAC) { wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " "AES-128-CMAC"); + } else if (sel & WPA_CIPHER_BIP_GMAC_128) { + wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_128; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " + "BIP-GMAC-128"); + } else if (sel & WPA_CIPHER_BIP_GMAC_256) { + wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_256; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " + "BIP-GMAC-256"); + } else if (sel & WPA_CIPHER_BIP_CMAC_256) { + wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_CMAC_256; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher " + "BIP-CMAC-256"); } else { wpa_s->mgmt_group_cipher = 0; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher"); @@ -1142,8 +1228,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP, wpa_s->mgmt_group_cipher); wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP, - (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? - wpa_s->conf->pmf : ssid->ieee80211w)); + wpas_get_ssid_pmf(wpa_s, ssid)); #endif /* CONFIG_IEEE80211W */ if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) { @@ -1152,7 +1237,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, } if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) { - wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN); + wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL); #ifndef CONFIG_NO_PBKDF2 if (bss && ssid->bssid_set && ssid->ssid_len == 0 && ssid->passphrase) { @@ -1161,7 +1246,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, 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); + wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); + os_memset(psk, 0, sizeof(psk)); } #endif /* CONFIG_NO_PBKDF2 */ #ifdef CONFIG_EXT_PASSWORD @@ -1197,7 +1283,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_hexdump_key(MSG_MSGDUMP, "PSK (from " "external passphrase)", psk, PMK_LEN); - wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN); + wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); + os_memset(psk, 0, sizeof(psk)); } else #endif /* CONFIG_NO_PBKDF2 */ if (wpabuf_len(pw) == 2 * PMK_LEN) { @@ -1208,7 +1295,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, ext_password_free(pw); return -1; } - wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN); + wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL); + os_memset(psk, 0, sizeof(psk)); } else { wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable " "PSK available"); @@ -1228,33 +1316,209 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, } -int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf) +static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) { - u32 ext_capab = 0; - u8 *pos = buf; + *pos = 0x00; + switch (idx) { + case 0: /* Bits 0-7 */ + break; + case 1: /* Bits 8-15 */ + break; + case 2: /* Bits 16-23 */ +#ifdef CONFIG_WNM + *pos |= 0x02; /* Bit 17 - WNM-Sleep Mode */ + *pos |= 0x08; /* Bit 19 - BSS Transition */ +#endif /* CONFIG_WNM */ + break; + case 3: /* Bits 24-31 */ +#ifdef CONFIG_WNM + *pos |= 0x02; /* Bit 25 - SSID List */ +#endif /* CONFIG_WNM */ +#ifdef CONFIG_INTERWORKING + if (wpa_s->conf->interworking) + *pos |= 0x80; /* Bit 31 - Interworking */ +#endif /* CONFIG_INTERWORKING */ + break; + case 4: /* Bits 32-39 */ #ifdef CONFIG_INTERWORKING - if (wpa_s->conf->interworking) - ext_capab |= BIT(31); /* Interworking */ + if (wpa_s->drv_flags / WPA_DRIVER_FLAGS_QOS_MAPPING) + *pos |= 0x01; /* Bit 32 - QoS Map */ #endif /* CONFIG_INTERWORKING */ + break; + case 5: /* Bits 40-47 */ +#ifdef CONFIG_HS20 + if (wpa_s->conf->hs20) + *pos |= 0x40; /* Bit 46 - WNM-Notification */ +#endif /* CONFIG_HS20 */ + break; + case 6: /* Bits 48-55 */ + break; + } +} -#ifdef CONFIG_WNM - ext_capab |= BIT(17); /* WNM-Sleep Mode */ - ext_capab |= BIT(19); /* BSS Transition */ -#endif /* CONFIG_WNM */ - if (!ext_capab) - return 0; +int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen) +{ + u8 *pos = buf; + u8 len = 6, i; + + if (len < wpa_s->extended_capa_len) + len = wpa_s->extended_capa_len; + if (buflen < (size_t) len + 2) { + wpa_printf(MSG_INFO, + "Not enough room for building extended capabilities element"); + return -1; + } *pos++ = WLAN_EID_EXT_CAPAB; - *pos++ = 4; - WPA_PUT_LE32(pos, ext_capab); - pos += 4; + *pos++ = len; + for (i = 0; i < len; i++, pos++) { + wpas_ext_capab_byte(wpa_s, pos, i); + + if (i < wpa_s->extended_capa_len) { + *pos &= ~wpa_s->extended_capa_mask[i]; + *pos |= wpa_s->extended_capa[i]; + } + } + + while (len > 0 && buf[1 + len] == 0) { + len--; + buf[1] = len; + } + if (len == 0) + return 0; + + return 2 + len; +} + + +static int wpas_valid_bss(struct wpa_supplicant *wpa_s, + struct wpa_bss *test_bss) +{ + struct wpa_bss *bss; + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (bss == test_bss) + return 1; + } + + return 0; +} + + +static int wpas_valid_ssid(struct wpa_supplicant *wpa_s, + struct wpa_ssid *test_ssid) +{ + struct wpa_ssid *ssid; + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid == test_ssid) + return 1; + } + + return 0; +} + + +int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss, + struct wpa_ssid *test_ssid) +{ + if (test_bss && !wpas_valid_bss(wpa_s, test_bss)) + return 0; + + return test_ssid == NULL || wpas_valid_ssid(wpa_s, test_ssid); +} + + +void wpas_connect_work_free(struct wpa_connect_work *cwork) +{ + if (cwork == NULL) + return; + os_free(cwork); +} + + +void wpas_connect_work_done(struct wpa_supplicant *wpa_s) +{ + struct wpa_connect_work *cwork; + struct wpa_radio_work *work = wpa_s->connect_work; + + if (!work) + return; + + wpa_s->connect_work = NULL; + cwork = work->ctx; + work->ctx = NULL; + wpas_connect_work_free(cwork); + radio_work_done(work); +} + + +int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style) +{ + struct os_reltime now; + u8 addr[ETH_ALEN]; + + os_get_reltime(&now); + if (wpa_s->last_mac_addr_style == style && + wpa_s->last_mac_addr_change.sec != 0 && + !os_reltime_expired(&now, &wpa_s->last_mac_addr_change, + wpa_s->conf->rand_addr_lifetime)) { + wpa_msg(wpa_s, MSG_DEBUG, + "Previously selected random MAC address has not yet expired"); + return 0; + } + + switch (style) { + case 1: + if (random_mac_addr(addr) < 0) + return -1; + break; + case 2: + os_memcpy(addr, wpa_s->perm_addr, ETH_ALEN); + if (random_mac_addr_keep_oui(addr) < 0) + return -1; + break; + default: + return -1; + } + + if (wpa_drv_set_mac_addr(wpa_s, addr) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "Failed to set random MAC address"); + return -1; + } + + os_get_reltime(&wpa_s->last_mac_addr_change); + wpa_s->mac_addr_changed = 1; + wpa_s->last_mac_addr_style = style; - return pos - buf; + if (wpa_supplicant_update_mac_addr(wpa_s) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "Could not update MAC address information"); + return -1; + } + + wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR, + MAC2STR(addr)); + + return 0; } +int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s) +{ + if (wpa_s->wpa_state >= WPA_AUTHENTICATING || + !wpa_s->conf->preassoc_mac_addr) + return 0; + + return wpas_update_random_addr(wpa_s, wpa_s->conf->preassoc_mac_addr); +} + + +static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit); + /** * wpa_supplicant_associate - Request association * @wpa_s: Pointer to wpa_supplicant data @@ -1266,22 +1530,42 @@ int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf) void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid) { - u8 wpa_ie[200]; - size_t wpa_ie_len; - int use_crypt, ret, i, bssid_changed; - int algs = WPA_AUTH_ALG_OPEN; - enum wpa_cipher cipher_pairwise, cipher_group; - struct wpa_driver_associate_params params; - int wep_keys_set = 0; - struct wpa_driver_capa capa; - int assoc_failed = 0; - struct wpa_ssid *old_ssid; - u8 ext_capab[10]; - int ext_capab_len; -#ifdef CONFIG_HT_OVERRIDES - struct ieee80211_ht_capabilities htcaps; - struct ieee80211_ht_capabilities htcaps_mask; -#endif /* CONFIG_HT_OVERRIDES */ + struct wpa_connect_work *cwork; + int rand_style; + + if (ssid->mac_addr == -1) + rand_style = wpa_s->conf->mac_addr; + else + rand_style = ssid->mac_addr; + + wmm_ac_clear_saved_tspecs(wpa_s); + wpa_s->reassoc_same_bss = 0; + + if (wpa_s->last_ssid == ssid) { + wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS"); + if (wpa_s->current_bss && wpa_s->current_bss == bss) { + wmm_ac_save_tspecs(wpa_s); + wpa_s->reassoc_same_bss = 1; + } + } else if (rand_style > 0) { + if (wpas_update_random_addr(wpa_s, rand_style) < 0) + return; + wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); + } else if (wpa_s->mac_addr_changed) { + if (wpa_drv_set_mac_addr(wpa_s, NULL) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "Could not restore permanent MAC address"); + return; + } + wpa_s->mac_addr_changed = 0; + if (wpa_supplicant_update_mac_addr(wpa_s) < 0) { + wpa_msg(wpa_s, MSG_INFO, + "Could not update MAC address information"); + return; + } + wpa_msg(wpa_s, MSG_DEBUG, "Using permanent MAC address"); + } + wpa_s->last_ssid = ssid; #ifdef CONFIG_IBSS_RSN ibss_rsn_deinit(wpa_s->ibss_rsn); @@ -1298,6 +1582,8 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, } if (wpa_supplicant_create_ap(wpa_s, ssid) < 0) { wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) + wpas_p2p_ap_setup_failed(wpa_s); return; } wpa_s->current_bss = bss; @@ -1308,6 +1594,31 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, return; } + if (ssid->mode == WPAS_MODE_MESH) { +#ifdef CONFIG_MESH + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MESH)) { + wpa_msg(wpa_s, MSG_INFO, + "Driver does not support mesh mode"); + return; + } + if (bss) + ssid->frequency = bss->freq; + if (wpa_supplicant_join_mesh(wpa_s, ssid) < 0) { + wpa_msg(wpa_s, MSG_ERROR, "Could not join mesh"); + return; + } + wpa_s->current_bss = bss; + wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_STARTED + "ssid=\"%s\" id=%d", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len), + ssid->id); +#else /* CONFIG_MESH */ + wpa_msg(wpa_s, MSG_ERROR, + "mesh mode support not included in the build"); +#endif /* CONFIG_MESH */ + return; + } + #ifdef CONFIG_TDLS if (bss) wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1), @@ -1320,9 +1631,300 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, return; } + if (wpa_s->connect_work) { + wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since connect_work exist"); + return; + } + + if (radio_work_pending(wpa_s, "connect")) { + wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since pending work exist"); + return; + } + + cwork = os_zalloc(sizeof(*cwork)); + if (cwork == NULL) + return; + + cwork->bss = bss; + cwork->ssid = ssid; + + if (radio_add_work(wpa_s, bss ? bss->freq : 0, "connect", 1, + wpas_start_assoc_cb, cwork) < 0) { + os_free(cwork); + } +} + + +static int bss_is_ibss(struct wpa_bss *bss) +{ + return (bss->caps & (IEEE80211_CAP_ESS | IEEE80211_CAP_IBSS)) == + IEEE80211_CAP_IBSS; +} + + +void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid, + struct hostapd_freq_params *freq) +{ + enum hostapd_hw_mode hw_mode; + struct hostapd_hw_modes *mode = NULL; + int ht40plus[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, + 184, 192 }; + int vht80[] = { 36, 52, 100, 116, 132, 149 }; + struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL; + u8 channel; + int i, chan_idx, ht40 = -1, res, obss_scan = 1; + unsigned int j; + struct hostapd_freq_params vht_freq; + + freq->freq = ssid->frequency; + + for (j = 0; j < wpa_s->last_scan_res_used; j++) { + struct wpa_bss *bss = wpa_s->last_scan_res[j]; + + if (ssid->mode != WPAS_MODE_IBSS) + break; + + /* Don't adjust control freq in case of fixed_freq */ + if (ssid->fixed_freq) + break; + + if (!bss_is_ibss(bss)) + continue; + + if (ssid->ssid_len == bss->ssid_len && + os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) == 0) { + wpa_printf(MSG_DEBUG, + "IBSS already found in scan results, adjust control freq: %d", + bss->freq); + freq->freq = bss->freq; + obss_scan = 0; + break; + } + } + + /* For IBSS check HT_IBSS flag */ + if (ssid->mode == WPAS_MODE_IBSS && + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_HT_IBSS)) + return; + + if (wpa_s->group_cipher == WPA_CIPHER_WEP40 || + wpa_s->group_cipher == WPA_CIPHER_WEP104 || + wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) { + wpa_printf(MSG_DEBUG, + "IBSS: WEP/TKIP detected, do not try to enable HT"); + return; + } + + hw_mode = ieee80211_freq_to_chan(freq->freq, &channel); + for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) { + if (wpa_s->hw.modes[i].mode == hw_mode) { + mode = &wpa_s->hw.modes[i]; + break; + } + } + + if (!mode) + return; + + freq->ht_enabled = ht_supported(mode); + if (!freq->ht_enabled) + return; + + /* Setup higher BW only for 5 GHz */ + if (mode->mode != HOSTAPD_MODE_IEEE80211A) + return; + + for (chan_idx = 0; chan_idx < mode->num_channels; chan_idx++) { + pri_chan = &mode->channels[chan_idx]; + if (pri_chan->chan == channel) + break; + pri_chan = NULL; + } + if (!pri_chan) + return; + + /* Check primary channel flags */ + if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR)) + return; + + /* Check/setup HT40+/HT40- */ + for (j = 0; j < ARRAY_SIZE(ht40plus); j++) { + if (ht40plus[j] == channel) { + ht40 = 1; + break; + } + } + + /* Find secondary channel */ + for (i = 0; i < mode->num_channels; i++) { + sec_chan = &mode->channels[i]; + if (sec_chan->chan == channel + ht40 * 4) + break; + sec_chan = NULL; + } + if (!sec_chan) + return; + + /* Check secondary channel flags */ + if (sec_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR)) + return; + + freq->channel = pri_chan->chan; + + switch (ht40) { + case -1: + if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS)) + return; + freq->sec_channel_offset = -1; + break; + case 1: + if (!(pri_chan->flag & HOSTAPD_CHAN_HT40PLUS)) + return; + freq->sec_channel_offset = 1; + break; + default: + break; + } + + if (freq->sec_channel_offset && obss_scan) { + struct wpa_scan_results *scan_res; + + scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0); + if (scan_res == NULL) { + /* Back to HT20 */ + freq->sec_channel_offset = 0; + return; + } + + res = check_40mhz_5g(mode, scan_res, pri_chan->chan, + sec_chan->chan); + switch (res) { + case 0: + /* Back to HT20 */ + freq->sec_channel_offset = 0; + break; + case 1: + /* Configuration allowed */ + break; + case 2: + /* Switch pri/sec channels */ + freq->freq = hw_get_freq(mode, sec_chan->chan); + freq->sec_channel_offset = -freq->sec_channel_offset; + freq->channel = sec_chan->chan; + break; + default: + freq->sec_channel_offset = 0; + break; + } + + wpa_scan_results_free(scan_res); + } + + wpa_printf(MSG_DEBUG, + "IBSS/mesh: setup freq channel %d, sec_channel_offset %d", + freq->channel, freq->sec_channel_offset); + + /* Not sure if mesh is ready for VHT */ + if (ssid->mode != WPAS_MODE_IBSS) + return; + + /* For IBSS check VHT_IBSS flag */ + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS)) + return; + + vht_freq = *freq; + + vht_freq.vht_enabled = vht_supported(mode); + if (!vht_freq.vht_enabled) + return; + + /* setup center_freq1, bandwidth */ + for (j = 0; j < ARRAY_SIZE(vht80); j++) { + if (freq->channel >= vht80[j] && + freq->channel < vht80[j] + 16) + break; + } + + if (j == ARRAY_SIZE(vht80)) + return; + + for (i = vht80[j]; i < vht80[j] + 16; i += 4) { + struct hostapd_channel_data *chan; + + chan = hw_get_channel_chan(mode, i, NULL); + if (!chan) + return; + + /* Back to HT configuration if channel not usable */ + if (chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR)) + return; + } + + if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq, + freq->channel, freq->ht_enabled, + vht_freq.vht_enabled, + freq->sec_channel_offset, + VHT_CHANWIDTH_80MHZ, + vht80[j] + 6, 0, 0) != 0) + return; + + *freq = vht_freq; + + wpa_printf(MSG_DEBUG, "IBSS: VHT setup freq cf1 %d, cf2 %d, bw %d", + freq->center_freq1, freq->center_freq2, freq->bandwidth); +} + + +static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_connect_work *cwork = work->ctx; + struct wpa_bss *bss = cwork->bss; + struct wpa_ssid *ssid = cwork->ssid; + struct wpa_supplicant *wpa_s = work->wpa_s; + u8 wpa_ie[200]; + size_t wpa_ie_len; + int use_crypt, ret, i, bssid_changed; + int algs = WPA_AUTH_ALG_OPEN; + unsigned int cipher_pairwise, cipher_group; + struct wpa_driver_associate_params params; + int wep_keys_set = 0; + int assoc_failed = 0; + struct wpa_ssid *old_ssid; +#ifdef CONFIG_HT_OVERRIDES + struct ieee80211_ht_capabilities htcaps; + struct ieee80211_ht_capabilities htcaps_mask; +#endif /* CONFIG_HT_OVERRIDES */ +#ifdef CONFIG_VHT_OVERRIDES + struct ieee80211_vht_capabilities vhtcaps; + struct ieee80211_vht_capabilities vhtcaps_mask; +#endif /* CONFIG_VHT_OVERRIDES */ + + if (deinit) { + if (work->started) { + wpa_s->connect_work = NULL; + + /* cancel possible auth. timeout */ + eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, + NULL); + } + wpas_connect_work_free(cwork); + return; + } + + wpa_s->connect_work = work; + + if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt"); + wpas_connect_work_done(wpa_s); + return; + } + os_memset(¶ms, 0, sizeof(params)); wpa_s->reassociate = 0; - if (bss && !wpas_driver_bss_selection(wpa_s)) { + wpa_s->eap_expected_failure = 0; + if (bss && + (!wpas_driver_bss_selection(wpa_s) || wpas_wps_searching(wpa_s))) { #ifdef CONFIG_IEEE80211R const u8 *ie, *md = NULL; #endif /* CONFIG_IEEE80211R */ @@ -1350,6 +1952,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { /* Use ap_scan==1 style network selection to find the network */ + wpas_connect_work_done(wpa_s); wpa_s->scan_req = MANUAL_SCAN_REQ; wpa_s->reassociate = 1; wpa_supplicant_req_scan(wpa_s, 0, 0); @@ -1393,14 +1996,14 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, ssid->proactive_key_caching) && (ssid->proto & WPA_PROTO_RSN); if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, - wpa_s->current_ssid, - try_opportunistic) == 0) - eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1); + ssid, try_opportunistic) == 0) + eapol_sm_notify_pmkid_attempt(wpa_s->eapol); wpa_ie_len = sizeof(wpa_ie); if (wpa_supplicant_set_suites(wpa_s, bss, ssid, wpa_ie, &wpa_ie_len)) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA " "key management and encryption suites"); + wpas_connect_work_done(wpa_s); return; } } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss && @@ -1420,6 +2023,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA " "key management and encryption suites (no " "scan results)"); + wpas_connect_work_done(wpa_s); return; } #ifdef CONFIG_WPS @@ -1472,37 +2076,70 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, "disallows" : "allows"); } } + + os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info)); #endif /* CONFIG_P2P */ #ifdef CONFIG_HS20 - if (wpa_s->conf->hs20) { + if (is_hs20_network(wpa_s, ssid, bss)) { struct wpabuf *hs20; hs20 = wpabuf_alloc(20); if (hs20) { - wpas_hs20_add_indication(hs20); - os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(hs20), - wpabuf_len(hs20)); - wpa_ie_len += wpabuf_len(hs20); + int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid); + size_t len; + + wpas_hs20_add_indication(hs20, pps_mo_id); + len = sizeof(wpa_ie) - wpa_ie_len; + if (wpabuf_len(hs20) <= len) { + os_memcpy(wpa_ie + wpa_ie_len, + wpabuf_head(hs20), wpabuf_len(hs20)); + wpa_ie_len += wpabuf_len(hs20); + } wpabuf_free(hs20); } } #endif /* CONFIG_HS20 */ - ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab); - if (ext_capab_len > 0) { - u8 *pos = wpa_ie; - if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN) - pos += 2 + pos[1]; - os_memmove(pos + ext_capab_len, pos, - wpa_ie_len - (pos - wpa_ie)); - wpa_ie_len += ext_capab_len; - os_memcpy(pos, ext_capab, ext_capab_len); + /* + * Workaround: Add Extended Capabilities element only if the AP + * included this element in Beacon/Probe Response frames. Some older + * APs seem to have interoperability issues if this element is + * included, so while the standard may require us to include the + * element in all cases, it is justifiable to skip it to avoid + * interoperability issues. + */ + if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) { + u8 ext_capab[18]; + int ext_capab_len; + ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, + sizeof(ext_capab)); + if (ext_capab_len > 0) { + u8 *pos = wpa_ie; + if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN) + pos += 2 + pos[1]; + os_memmove(pos + ext_capab_len, pos, + wpa_ie_len - (pos - wpa_ie)); + wpa_ie_len += ext_capab_len; + os_memcpy(pos, ext_capab, ext_capab_len); + } + } + + if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) { + struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]; + size_t len; + + len = sizeof(wpa_ie) - wpa_ie_len; + if (wpabuf_len(buf) <= len) { + os_memcpy(wpa_ie + wpa_ie_len, + wpabuf_head(buf), wpabuf_len(buf)); + wpa_ie_len += wpabuf_len(buf); + } } wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL); use_crypt = 1; - cipher_pairwise = cipher_suite2driver(wpa_s->pairwise_cipher); - cipher_group = cipher_suite2driver(wpa_s->group_cipher); + cipher_pairwise = wpa_s->pairwise_cipher; + cipher_group = wpa_s->group_cipher; if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE || wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) @@ -1526,7 +2163,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, /* Assume that dynamic WEP-104 keys will be used and * set cipher suites in order for drivers to expect * encryption. */ - cipher_pairwise = cipher_group = CIPHER_WEP104; + cipher_pairwise = cipher_group = WPA_CIPHER_WEP104; } } #endif /* IEEE8021X_EAPOL */ @@ -1547,8 +2184,10 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, MAC2STR(bss->bssid), bss->freq, ssid->bssid_set); params.bssid = bss->bssid; - params.freq = bss->freq; + params.freq.freq = bss->freq; } + params.bssid_hint = bss->bssid; + params.freq_hint = bss->freq; } else { params.ssid = ssid->ssid; params.ssid_len = ssid->ssid_len; @@ -1560,14 +2199,24 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, params.fixed_bssid = 1; } - if (ssid->mode == WPAS_MODE_IBSS && ssid->frequency > 0 && - params.freq == 0) - params.freq = ssid->frequency; /* Initial channel for IBSS */ + /* Initial frequency for IBSS/mesh */ + if ((ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_MESH) && + ssid->frequency > 0 && params.freq.freq == 0) + ibss_mesh_setup_freq(wpa_s, ssid, ¶ms.freq); + + if (ssid->mode == WPAS_MODE_IBSS) { + params.fixed_freq = ssid->fixed_freq; + if (ssid->beacon_int) + params.beacon_int = ssid->beacon_int; + else + params.beacon_int = wpa_s->conf->beacon_int; + } + params.wpa_ie = wpa_ie; params.wpa_ie_len = wpa_ie_len; params.pairwise_suite = cipher_pairwise; params.group_suite = cipher_group; - params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt); + params.key_mgmt_suite = wpa_s->key_mgmt; params.wpa_proto = wpa_s->wpa_proto; params.auth_alg = algs; params.mode = ssid->mode; @@ -1580,19 +2229,35 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, params.wep_tx_keyidx = ssid->wep_tx_keyidx; if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) && - (params.key_mgmt_suite == KEY_MGMT_PSK || - params.key_mgmt_suite == KEY_MGMT_FT_PSK)) { + (params.key_mgmt_suite == WPA_KEY_MGMT_PSK || + params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) { params.passphrase = ssid->passphrase; if (ssid->psk_set) params.psk = ssid->psk; } + if (wpa_s->conf->key_mgmt_offload) { + if (params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X || + params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || + params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B || + params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + params.req_key_mgmt_offload = + ssid->proactive_key_caching < 0 ? + wpa_s->conf->okc : ssid->proactive_key_caching; + else + params.req_key_mgmt_offload = 1; + + if ((params.key_mgmt_suite == WPA_KEY_MGMT_PSK || + params.key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || + params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK) && + ssid->psk_set) + params.psk = ssid->psk; + } + params.drop_unencrypted = use_crypt; #ifdef CONFIG_IEEE80211W - params.mgmt_frame_protection = - ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ? - wpa_s->conf->pmf : ssid->ieee80211w; + params.mgmt_frame_protection = wpas_get_ssid_pmf(wpa_s, ssid); if (params.mgmt_frame_protection != NO_MGMT_FRAME_PROTECTION && bss) { const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN); struct wpa_ie_data ie; @@ -1621,6 +2286,35 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, params.htcaps_mask = (u8 *) &htcaps_mask; wpa_supplicant_apply_ht_overrides(wpa_s, ssid, ¶ms); #endif /* CONFIG_HT_OVERRIDES */ +#ifdef CONFIG_VHT_OVERRIDES + os_memset(&vhtcaps, 0, sizeof(vhtcaps)); + os_memset(&vhtcaps_mask, 0, sizeof(vhtcaps_mask)); + params.vhtcaps = &vhtcaps; + params.vhtcaps_mask = &vhtcaps_mask; + wpa_supplicant_apply_vht_overrides(wpa_s, ssid, ¶ms); +#endif /* CONFIG_VHT_OVERRIDES */ + +#ifdef CONFIG_P2P + /* + * If multi-channel concurrency is not supported, check for any + * frequency conflict. In case of any frequency conflict, remove the + * least prioritized connection. + */ + if (wpa_s->num_multichan_concurrent < 2) { + int freq, num; + num = get_shared_radio_freqs(wpa_s, &freq, 1); + if (num > 0 && freq > 0 && freq != params.freq.freq) { + wpa_printf(MSG_DEBUG, + "Assoc conflicting freq found (%d != %d)", + freq, params.freq.freq); + if (wpas_p2p_handle_frequency_conflicts( + wpa_s, params.freq.freq, ssid) < 0) { + wpas_connect_work_done(wpa_s); + return; + } + } + } +#endif /* CONFIG_P2P */ ret = wpa_drv_associate(wpa_s, ¶ms); if (ret < 0) { @@ -1674,8 +2368,8 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0); } - if (wep_keys_set && wpa_drv_get_capa(wpa_s, &capa) == 0 && - capa.flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC) { + if (wep_keys_set && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC)) { /* Set static WEP keys again */ wpa_set_wep_keys(wpa_s, ssid); } @@ -1702,6 +2396,7 @@ static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s, { struct wpa_ssid *old_ssid; + wpas_connect_work_done(wpa_s); wpa_clear_keys(wpa_s, addr); old_ssid = wpa_s->current_ssid; wpa_supplicant_mark_disassoc(wpa_s); @@ -1750,6 +2445,18 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, zero_addr = 1; } +#ifdef CONFIG_TDLS + wpa_tdls_teardown_peers(wpa_s->wpa); +#endif /* CONFIG_TDLS */ + +#ifdef CONFIG_MESH + if (wpa_s->ifmsh) { + wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s", + wpa_s->ifname); + wpa_supplicant_leave_mesh(wpa_s); + } +#endif /* CONFIG_MESH */ + if (addr) { wpa_drv_deauthenticate(wpa_s, addr, reason_code); os_memset(&event, 0, sizeof(event)); @@ -1763,6 +2470,24 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, wpa_supplicant_clear_connection(wpa_s, addr); } +static void wpa_supplicant_enable_one_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + if (!ssid || !ssid->disabled || ssid->disabled == 2) + return; + + ssid->disabled = 0; + wpas_clear_temp_disabled(wpa_s, ssid, 1); + wpas_notify_network_enabled_changed(wpa_s, ssid); + + /* + * Try to reassociate since there is no current configuration and a new + * network was made available. + */ + if (!wpa_s->current_ssid && !wpa_s->disconnected) + wpa_s->reassociate = 1; +} + /** * wpa_supplicant_enable_network - Mark a configured network as enabled @@ -1774,48 +2499,21 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { - struct wpa_ssid *other_ssid; - int was_disabled; - if (ssid == NULL) { - 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; - - was_disabled = other_ssid->disabled; - - other_ssid->disabled = 0; - if (was_disabled) - wpas_clear_temp_disabled(wpa_s, other_ssid, 0); + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) + wpa_supplicant_enable_one_network(wpa_s, ssid); + } else + wpa_supplicant_enable_one_network(wpa_s, ssid); - if (was_disabled != other_ssid->disabled) - wpas_notify_network_enabled_changed( - wpa_s, other_ssid); - } - if (wpa_s->reassociate) - wpa_supplicant_req_scan(wpa_s, 0, 0); - } else if (ssid->disabled && ssid->disabled != 2) { - if (wpa_s->current_ssid == NULL) { - /* - * Try to reassociate since there is no current - * configuration and a new network was made available. - */ - wpa_s->reassociate = 1; - wpa_supplicant_req_scan(wpa_s, 0, 0); + if (wpa_s->reassociate && !wpa_s->disconnected) { + if (wpa_s->sched_scanning) { + wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to add " + "new network to scan filters"); + wpa_supplicant_cancel_sched_scan(wpa_s); } - was_disabled = ssid->disabled; - - ssid->disabled = 0; - wpas_clear_temp_disabled(wpa_s, ssid, 1); - - if (was_disabled != ssid->disabled) - wpas_notify_network_enabled_changed(wpa_s, ssid); + if (wpa_supplicant_fast_associate(wpa_s) != 1) + wpa_supplicant_req_scan(wpa_s, 0, 0); } } @@ -1834,6 +2532,9 @@ void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s, int was_disabled; if (ssid == NULL) { + if (wpa_s->sched_scanning) + wpa_supplicant_cancel_sched_scan(wpa_s); + for (other_ssid = wpa_s->conf->ssid; other_ssid; other_ssid = other_ssid->next) { was_disabled = other_ssid->disabled; @@ -1859,8 +2560,15 @@ void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s, ssid->disabled = 1; - if (was_disabled != ssid->disabled) + if (was_disabled != ssid->disabled) { wpas_notify_network_enabled_changed(wpa_s, ssid); + if (wpa_s->sched_scanning) { + wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan " + "to remove network from filters"); + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } + } } } @@ -1878,6 +2586,7 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, int disconnected = 0; if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) { + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); disconnected = 1; @@ -1911,12 +2620,21 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, return; } - if (ssid) + if (ssid) { wpa_s->current_ssid = ssid; - wpa_s->connect_without_scan = NULL; + eapol_sm_notify_config(wpa_s->eapol, NULL, NULL); + wpa_s->connect_without_scan = + (ssid->mode == WPAS_MODE_MESH) ? ssid : NULL; + } else { + wpa_s->connect_without_scan = NULL; + } + wpa_s->disconnected = 0; wpa_s->reassociate = 1; - wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0); + + if (wpa_s->connect_without_scan || + wpa_supplicant_fast_associate(wpa_s) != 1) + wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0); if (ssid) wpas_notify_network_selected(wpa_s, ssid); @@ -1924,6 +2642,59 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, /** + * wpas_set_pkcs11_engine_and_module_path - Set PKCS #11 engine and module path + * @wpa_s: wpa_supplicant structure for a network interface + * @pkcs11_engine_path: PKCS #11 engine path or NULL + * @pkcs11_module_path: PKCS #11 module path or NULL + * Returns: 0 on success; -1 on failure + * + * Sets the PKCS #11 engine and module path. Both have to be NULL or a valid + * path. If resetting the EAPOL state machine with the new PKCS #11 engine and + * module path fails the paths will be reset to the default value (NULL). + */ +int wpas_set_pkcs11_engine_and_module_path(struct wpa_supplicant *wpa_s, + const char *pkcs11_engine_path, + const char *pkcs11_module_path) +{ + char *pkcs11_engine_path_copy = NULL; + char *pkcs11_module_path_copy = NULL; + + if (pkcs11_engine_path != NULL) { + pkcs11_engine_path_copy = os_strdup(pkcs11_engine_path); + if (pkcs11_engine_path_copy == NULL) + return -1; + } + if (pkcs11_module_path != NULL) { + pkcs11_module_path_copy = os_strdup(pkcs11_module_path); + if (pkcs11_module_path_copy == NULL) { + os_free(pkcs11_engine_path_copy); + return -1; + } + } + + os_free(wpa_s->conf->pkcs11_engine_path); + os_free(wpa_s->conf->pkcs11_module_path); + wpa_s->conf->pkcs11_engine_path = pkcs11_engine_path_copy; + wpa_s->conf->pkcs11_module_path = pkcs11_module_path_copy; + + wpa_sm_set_eapol(wpa_s->wpa, NULL); + eapol_sm_deinit(wpa_s->eapol); + wpa_s->eapol = NULL; + if (wpa_supplicant_init_eapol(wpa_s)) { + /* Error -> Reset paths to the default value (NULL) once. */ + if (pkcs11_engine_path != NULL && pkcs11_module_path != NULL) + wpas_set_pkcs11_engine_and_module_path(wpa_s, NULL, + NULL); + + return -1; + } + wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol); + + return 0; +} + + +/** * wpa_supplicant_set_ap_scan - Set AP scan mode for interface * @wpa_s: wpa_supplicant structure for a network interface * @ap_scan: AP scan mode @@ -2021,7 +2792,7 @@ int wpa_supplicant_set_scan_interval(struct wpa_supplicant *wpa_s, } wpa_msg(wpa_s, MSG_DEBUG, "Setting scan interval: %d sec", scan_interval); - wpa_s->scan_interval = scan_interval; + wpa_supplicant_update_scan_int(wpa_s, scan_interval); return 0; } @@ -2217,6 +2988,16 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr)); wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len); +#ifdef CONFIG_PEERKEY + if (wpa_s->wpa_state > WPA_ASSOCIATED && wpa_s->current_ssid && + wpa_s->current_ssid->peerkey && + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) && + wpa_sm_rx_eapol_peerkey(wpa_s->wpa, src_addr, buf, len) == 1) { + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: Processed PeerKey EAPOL-Key"); + return; + } +#endif /* CONFIG_PEERKEY */ + if (wpa_s->wpa_state < WPA_ASSOCIATED || (wpa_s->last_eapol_matches_bssid && #ifdef CONFIG_AP @@ -2242,7 +3023,7 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, wpabuf_free(wpa_s->pending_eapol_rx); wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len); if (wpa_s->pending_eapol_rx) { - os_get_time(&wpa_s->pending_eapol_rx_time); + os_get_reltime(&wpa_s->pending_eapol_rx_time); os_memcpy(wpa_s->pending_eapol_rx_src, src_addr, ETH_ALEN); } @@ -2322,12 +3103,9 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s) { - 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 if (!(wpa_s->drv_flags & - WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) { + if ((!wpa_s->p2p_mgmt || + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) && + !(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), @@ -2346,8 +3124,6 @@ int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s) return -1; } - 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; @@ -2395,14 +3171,17 @@ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s) if (wpa_supplicant_update_mac_addr(wpa_s) < 0) return -1; + wpa_dbg(wpa_s, MSG_DEBUG, "Own MAC address: " MACSTR, + MAC2STR(wpa_s->own_addr)); + os_memcpy(wpa_s->perm_addr, wpa_s->own_addr, ETH_ALEN); + wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr); + if (wpa_s->bridge_ifname[0]) { 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_bridge, - wpa_s, 1); + wpa_s->l2_br = l2_packet_init_bridge( + wpa_s->bridge_ifname, wpa_s->ifname, wpa_s->own_addr, + ETH_P_EAPOL, wpa_supplicant_rx_eapol_bridge, wpa_s, 1); if (wpa_s->l2_br == NULL) { wpa_msg(wpa_s, MSG_ERROR, "Failed to open l2_packet " "connection for the bridge interface '%s'", @@ -2424,10 +3203,18 @@ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s) wpa_s->prev_scan_wildcard = 0; if (wpa_supplicant_enabled_networks(wpa_s)) { - if (wpa_supplicant_delayed_sched_scan(wpa_s, interface_count, + if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); + interface_count = 0; + } +#ifndef ANDROID + if (!wpa_s->p2p_mgmt && + wpa_supplicant_delayed_sched_scan(wpa_s, + interface_count % 3, 100000)) - wpa_supplicant_req_scan(wpa_s, interface_count, + wpa_supplicant_req_scan(wpa_s, interface_count % 3, 100000); +#endif /* ANDROID */ interface_count++; } else wpa_supplicant_set_state(wpa_s, WPA_INACTIVE); @@ -2443,7 +3230,8 @@ static int wpa_supplicant_daemon(const char *pid_file) } -static struct wpa_supplicant * wpa_supplicant_alloc(void) +static struct wpa_supplicant * +wpa_supplicant_alloc(struct wpa_supplicant *parent) { struct wpa_supplicant *wpa_s; @@ -2453,7 +3241,7 @@ static struct wpa_supplicant * wpa_supplicant_alloc(void) wpa_s->scan_req = INITIAL_SCAN_REQ; wpa_s->scan_interval = 5; wpa_s->new_connection = 1; - wpa_s->parent = wpa_s; + wpa_s->parent = parent ? parent : wpa_s; wpa_s->sched_scanning = 0; return wpa_s; @@ -2521,7 +3309,7 @@ static int wpa_disable_max_amsdu(struct wpa_supplicant *wpa_s, struct ieee80211_ht_capabilities *htcaps_mask, int disabled) { - u16 msk; + le16 msk; wpa_msg(wpa_s, MSG_DEBUG, "set_disable_max_amsdu: %d", disabled); @@ -2594,8 +3382,8 @@ static int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s, int disabled) { /* Masking these out disables HT40 */ - u16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET | - HT_CAP_INFO_SHORT_GI40MHZ); + le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET | + HT_CAP_INFO_SHORT_GI40MHZ); wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled); @@ -2616,8 +3404,8 @@ static int wpa_set_disable_sgi(struct wpa_supplicant *wpa_s, int disabled) { /* Masking these out disables SGI */ - u16 msk = host_to_le16(HT_CAP_INFO_SHORT_GI20MHZ | - HT_CAP_INFO_SHORT_GI40MHZ); + le16 msk = host_to_le16(HT_CAP_INFO_SHORT_GI20MHZ | + HT_CAP_INFO_SHORT_GI40MHZ); wpa_msg(wpa_s, MSG_DEBUG, "set_disable_sgi: %d", disabled); @@ -2632,6 +3420,27 @@ static int wpa_set_disable_sgi(struct wpa_supplicant *wpa_s, } +static int wpa_set_disable_ldpc(struct wpa_supplicant *wpa_s, + struct ieee80211_ht_capabilities *htcaps, + struct ieee80211_ht_capabilities *htcaps_mask, + int disabled) +{ + /* Masking these out disables LDPC */ + le16 msk = host_to_le16(HT_CAP_INFO_LDPC_CODING_CAP); + + wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ldpc: %d", disabled); + + if (disabled) + htcaps->ht_capabilities_info &= ~msk; + else + htcaps->ht_capabilities_info |= msk; + + htcaps_mask->ht_capabilities_info |= msk; + + return 0; +} + + void wpa_supplicant_apply_ht_overrides( struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_driver_associate_params *params) @@ -2655,11 +3464,83 @@ void wpa_supplicant_apply_ht_overrides( wpa_set_ampdu_density(wpa_s, htcaps, htcaps_mask, ssid->ampdu_density); wpa_set_disable_ht40(wpa_s, htcaps, htcaps_mask, ssid->disable_ht40); wpa_set_disable_sgi(wpa_s, htcaps, htcaps_mask, ssid->disable_sgi); + wpa_set_disable_ldpc(wpa_s, htcaps, htcaps_mask, ssid->disable_ldpc); + + if (ssid->ht40_intolerant) { + le16 bit = host_to_le16(HT_CAP_INFO_40MHZ_INTOLERANT); + htcaps->ht_capabilities_info |= bit; + htcaps_mask->ht_capabilities_info |= bit; + } } #endif /* CONFIG_HT_OVERRIDES */ +#ifdef CONFIG_VHT_OVERRIDES +void wpa_supplicant_apply_vht_overrides( + struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + struct wpa_driver_associate_params *params) +{ + struct ieee80211_vht_capabilities *vhtcaps; + struct ieee80211_vht_capabilities *vhtcaps_mask; + + if (!ssid) + return; + + params->disable_vht = ssid->disable_vht; + + vhtcaps = (void *) params->vhtcaps; + vhtcaps_mask = (void *) params->vhtcaps_mask; + + if (!vhtcaps || !vhtcaps_mask) + return; + + vhtcaps->vht_capabilities_info = ssid->vht_capa; + vhtcaps_mask->vht_capabilities_info = ssid->vht_capa_mask; + +#ifdef CONFIG_HT_OVERRIDES + /* if max ampdu is <= 3, we have to make the HT cap the same */ + if (ssid->vht_capa_mask & VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) { + int max_ampdu; + + max_ampdu = (ssid->vht_capa & + VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) >> + VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT; + + max_ampdu = max_ampdu < 3 ? max_ampdu : 3; + wpa_set_ampdu_factor(wpa_s, + (void *) params->htcaps, + (void *) params->htcaps_mask, + max_ampdu); + } +#endif /* CONFIG_HT_OVERRIDES */ + +#define OVERRIDE_MCS(i) \ + if (ssid->vht_tx_mcs_nss_ ##i >= 0) { \ + vhtcaps_mask->vht_supported_mcs_set.tx_map |= \ + 3 << 2 * (i - 1); \ + vhtcaps->vht_supported_mcs_set.tx_map |= \ + ssid->vht_tx_mcs_nss_ ##i << 2 * (i - 1); \ + } \ + if (ssid->vht_rx_mcs_nss_ ##i >= 0) { \ + vhtcaps_mask->vht_supported_mcs_set.rx_map |= \ + 3 << 2 * (i - 1); \ + vhtcaps->vht_supported_mcs_set.rx_map |= \ + ssid->vht_rx_mcs_nss_ ##i << 2 * (i - 1); \ + } + + OVERRIDE_MCS(1); + OVERRIDE_MCS(2); + OVERRIDE_MCS(3); + OVERRIDE_MCS(4); + OVERRIDE_MCS(5); + OVERRIDE_MCS(6); + OVERRIDE_MCS(7); + OVERRIDE_MCS(8); +} +#endif /* CONFIG_VHT_OVERRIDES */ + + static int pcsc_reader_init(struct wpa_supplicant *wpa_s) { #ifdef PCSC_FUNCS @@ -2668,7 +3549,7 @@ static int pcsc_reader_init(struct wpa_supplicant *wpa_s) if (!wpa_s->conf->pcsc_reader) return 0; - wpa_s->scard = scard_init(SCARD_TRY_BOTH, wpa_s->conf->pcsc_reader); + wpa_s->scard = scard_init(wpa_s->conf->pcsc_reader); if (!wpa_s->scard) return 1; @@ -2734,11 +3615,337 @@ int wpas_init_ext_pw(struct wpa_supplicant *wpa_s) } +static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s, + const struct wpa_driver_capa *capa) +{ + struct wowlan_triggers *triggers; + int ret = 0; + + if (!wpa_s->conf->wowlan_triggers) + return 0; + + triggers = wpa_get_wowlan_triggers(wpa_s->conf->wowlan_triggers, capa); + if (triggers) { + ret = wpa_drv_wowlan(wpa_s, triggers); + os_free(triggers); + } + return ret; +} + + +static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s, + const char *rn) +{ + struct wpa_supplicant *iface = wpa_s->global->ifaces; + struct wpa_radio *radio; + + while (rn && iface) { + radio = iface->radio; + if (radio && os_strcmp(rn, radio->name) == 0) { + wpa_printf(MSG_DEBUG, "Add interface %s to existing radio %s", + wpa_s->ifname, rn); + dl_list_add(&radio->ifaces, &wpa_s->radio_list); + return radio; + } + + iface = iface->next; + } + + wpa_printf(MSG_DEBUG, "Add interface %s to a new radio %s", + wpa_s->ifname, rn ? rn : "N/A"); + radio = os_zalloc(sizeof(*radio)); + if (radio == NULL) + return NULL; + + if (rn) + os_strlcpy(radio->name, rn, sizeof(radio->name)); + dl_list_init(&radio->ifaces); + dl_list_init(&radio->work); + dl_list_add(&radio->ifaces, &wpa_s->radio_list); + + return radio; +} + + +static void radio_work_free(struct wpa_radio_work *work) +{ + if (work->wpa_s->scan_work == work) { + /* This should not really happen. */ + wpa_dbg(work->wpa_s, MSG_INFO, "Freeing radio work '%s'@%p (started=%d) that is marked as scan_work", + work->type, work, work->started); + work->wpa_s->scan_work = NULL; + } + +#ifdef CONFIG_P2P + if (work->wpa_s->p2p_scan_work == work) { + /* This should not really happen. */ + wpa_dbg(work->wpa_s, MSG_INFO, "Freeing radio work '%s'@%p (started=%d) that is marked as p2p_scan_work", + work->type, work, work->started); + work->wpa_s->p2p_scan_work = NULL; + } +#endif /* CONFIG_P2P */ + + dl_list_del(&work->list); + os_free(work); +} + + +static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_radio *radio = eloop_ctx; + struct wpa_radio_work *work; + struct os_reltime now, diff; + struct wpa_supplicant *wpa_s; + + work = dl_list_first(&radio->work, struct wpa_radio_work, list); + if (work == NULL) + return; + + if (work->started) + return; /* already started and still in progress */ + + wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant, + radio_list); + if (wpa_s && wpa_s->radio->external_scan_running) { + wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes"); + return; + } + + os_get_reltime(&now); + os_reltime_sub(&now, &work->time, &diff); + wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting radio work '%s'@%p after %ld.%06ld second wait", + work->type, work, diff.sec, diff.usec); + work->started = 1; + work->time = now; + work->cb(work, 0); +} + + +/* + * This function removes both started and pending radio works running on + * the provided interface's radio. + * Prior to the removal of the radio work, its callback (cb) is called with + * deinit set to be 1. Each work's callback is responsible for clearing its + * internal data and restoring to a correct state. + * @wpa_s: wpa_supplicant data + * @type: type of works to be removed + * @remove_all: 1 to remove all the works on this radio, 0 to remove only + * this interface's works. + */ +void radio_remove_works(struct wpa_supplicant *wpa_s, + const char *type, int remove_all) +{ + struct wpa_radio_work *work, *tmp; + struct wpa_radio *radio = wpa_s->radio; + + dl_list_for_each_safe(work, tmp, &radio->work, struct wpa_radio_work, + list) { + if (type && os_strcmp(type, work->type) != 0) + continue; + + /* skip other ifaces' works */ + if (!remove_all && work->wpa_s != wpa_s) + continue; + + wpa_dbg(wpa_s, MSG_DEBUG, "Remove radio work '%s'@%p%s", + work->type, work, work->started ? " (started)" : ""); + work->cb(work, 1); + radio_work_free(work); + } + + /* in case we removed the started work */ + radio_work_check_next(wpa_s); +} + + +static void radio_remove_interface(struct wpa_supplicant *wpa_s) +{ + struct wpa_radio *radio = wpa_s->radio; + + if (!radio) + return; + + wpa_printf(MSG_DEBUG, "Remove interface %s from radio %s", + wpa_s->ifname, radio->name); + dl_list_del(&wpa_s->radio_list); + radio_remove_works(wpa_s, NULL, 0); + wpa_s->radio = NULL; + if (!dl_list_empty(&radio->ifaces)) + return; /* Interfaces remain for this radio */ + + wpa_printf(MSG_DEBUG, "Remove radio %s", radio->name); + eloop_cancel_timeout(radio_start_next_work, radio, NULL); + os_free(radio); +} + + +void radio_work_check_next(struct wpa_supplicant *wpa_s) +{ + struct wpa_radio *radio = wpa_s->radio; + + if (dl_list_empty(&radio->work)) + return; + if (wpa_s->ext_work_in_progress) { + wpa_printf(MSG_DEBUG, + "External radio work in progress - delay start of pending item"); + return; + } + eloop_cancel_timeout(radio_start_next_work, radio, NULL); + eloop_register_timeout(0, 0, radio_start_next_work, radio, NULL); +} + + +/** + * radio_add_work - Add a radio work item + * @wpa_s: Pointer to wpa_supplicant data + * @freq: Frequency of the offchannel operation in MHz or 0 + * @type: Unique identifier for each type of work + * @next: Force as the next work to be executed + * @cb: Callback function for indicating when radio is available + * @ctx: Context pointer for the work (work->ctx in cb()) + * Returns: 0 on success, -1 on failure + * + * This function is used to request time for an operation that requires + * exclusive radio control. Once the radio is available, the registered callback + * function will be called. radio_work_done() must be called once the exclusive + * radio operation has been completed, so that the radio is freed for other + * operations. The special case of deinit=1 is used to free the context data + * during interface removal. That does not allow the callback function to start + * the radio operation, i.e., it must free any resources allocated for the radio + * work and return. + * + * The @freq parameter can be used to indicate a single channel on which the + * offchannel operation will occur. This may allow multiple radio work + * operations to be performed in parallel if they apply for the same channel. + * Setting this to 0 indicates that the work item may use multiple channels or + * requires exclusive control of the radio. + */ +int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, + const char *type, int next, + void (*cb)(struct wpa_radio_work *work, int deinit), + void *ctx) +{ + struct wpa_radio_work *work; + int was_empty; + + work = os_zalloc(sizeof(*work)); + if (work == NULL) + return -1; + wpa_dbg(wpa_s, MSG_DEBUG, "Add radio work '%s'@%p", type, work); + os_get_reltime(&work->time); + work->freq = freq; + work->type = type; + work->wpa_s = wpa_s; + work->cb = cb; + work->ctx = ctx; + + was_empty = dl_list_empty(&wpa_s->radio->work); + if (next) + dl_list_add(&wpa_s->radio->work, &work->list); + else + dl_list_add_tail(&wpa_s->radio->work, &work->list); + if (was_empty) { + wpa_dbg(wpa_s, MSG_DEBUG, "First radio work item in the queue - schedule start immediately"); + radio_work_check_next(wpa_s); + } + + return 0; +} + + +/** + * radio_work_done - Indicate that a radio work item has been completed + * @work: Completed work + * + * This function is called once the callback function registered with + * radio_add_work() has completed its work. + */ +void radio_work_done(struct wpa_radio_work *work) +{ + struct wpa_supplicant *wpa_s = work->wpa_s; + struct os_reltime now, diff; + unsigned int started = work->started; + + os_get_reltime(&now); + os_reltime_sub(&now, &work->time, &diff); + wpa_dbg(wpa_s, MSG_DEBUG, "Radio work '%s'@%p %s in %ld.%06ld seconds", + work->type, work, started ? "done" : "canceled", + diff.sec, diff.usec); + radio_work_free(work); + if (started) + radio_work_check_next(wpa_s); +} + + +struct wpa_radio_work * +radio_work_pending(struct wpa_supplicant *wpa_s, const char *type) +{ + struct wpa_radio_work *work; + struct wpa_radio *radio = wpa_s->radio; + + dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) { + if (work->wpa_s == wpa_s && os_strcmp(work->type, type) == 0) + return work; + } + + return NULL; +} + + +static int wpas_init_driver(struct wpa_supplicant *wpa_s, + struct wpa_interface *iface) +{ + const char *ifname, *driver, *rn; + + driver = iface->driver; +next_driver: + if (wpa_supplicant_set_driver(wpa_s, driver) < 0) + return -1; + + wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname); + if (wpa_s->drv_priv == NULL) { + const char *pos; + pos = driver ? os_strchr(driver, ',') : NULL; + if (pos) { + wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize " + "driver interface - try next driver wrapper"); + driver = pos + 1; + goto next_driver; + } + 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_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_dbg(wpa_s, MSG_DEBUG, "Driver interface replaced " + "interface name with '%s'", ifname); + os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname)); + } + + rn = wpa_driver_get_radio_name(wpa_s); + if (rn && rn[0] == '\0') + rn = NULL; + + wpa_s->radio = radio_add_interface(wpa_s, rn); + if (wpa_s->radio == NULL) + return -1; + + return 0; +} + + static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, struct wpa_interface *iface) { - const char *ifname, *driver; struct wpa_driver_capa capa; + int capa_res; wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver " "'%s' ctrl_interface '%s' bridge '%s'", iface->ifname, @@ -2761,12 +3968,14 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, #else /* CONFIG_BACKEND_FILE */ wpa_s->confname = os_strdup(iface->confname); #endif /* CONFIG_BACKEND_FILE */ - wpa_s->conf = wpa_config_read(wpa_s->confname); + wpa_s->conf = wpa_config_read(wpa_s->confname, NULL); if (wpa_s->conf == NULL) { wpa_printf(MSG_ERROR, "Failed to read or parse " "configuration '%s'.", wpa_s->confname); return -1; } + wpa_s->confanother = os_rel2abs_path(iface->confanother); + wpa_config_read(wpa_s->confanother, wpa_s->conf); /* * Override ctrl_interface and driver_param if set on command @@ -2783,6 +3992,11 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, wpa_s->conf->driver_param = os_strdup(iface->driver_param); } + + if (iface->p2p_mgmt && !iface->ctrl_interface) { + os_free(wpa_s->conf->ctrl_interface); + wpa_s->conf->ctrl_interface = NULL; + } } else wpa_s->conf = wpa_config_alloc_empty(iface->ctrl_interface, iface->driver_param); @@ -2822,37 +4036,8 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, * L2 receive handler so that association events are processed before * EAPOL-Key packets if both become available for the same select() * call. */ - driver = iface->driver; -next_driver: - if (wpa_supplicant_set_driver(wpa_s, driver) < 0) - return -1; - - wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname); - if (wpa_s->drv_priv == NULL) { - const char *pos; - pos = driver ? os_strchr(driver, ',') : NULL; - if (pos) { - wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize " - "driver interface - try next driver wrapper"); - driver = pos + 1; - goto next_driver; - } - wpa_msg(wpa_s, MSG_ERROR, "Failed to initialize driver " - "interface"); + if (wpas_init_driver(wpa_s, iface) < 0) return -1; - } - if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) { - 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_dbg(wpa_s, MSG_DEBUG, "Driver interface replaced " - "interface name with '%s'", ifname); - os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname)); - } if (wpa_supplicant_init_wpa(wpa_s) < 0) return -1; @@ -2889,11 +4074,31 @@ next_driver: wpa_s->hw.modes = wpa_drv_get_hw_feature_data(wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags); + if (wpa_s->hw.modes) { + u16 i; + + for (i = 0; i < wpa_s->hw.num_modes; i++) { + if (wpa_s->hw.modes[i].vht_capab) { + wpa_s->hw_capab = CAPAB_VHT; + break; + } - if (wpa_drv_get_capa(wpa_s, &capa) == 0) { + if (wpa_s->hw.modes[i].ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) + wpa_s->hw_capab = CAPAB_HT40; + else if (wpa_s->hw.modes[i].ht_capab && + wpa_s->hw_capab == CAPAB_NO_HT_VHT) + wpa_s->hw_capab = CAPAB_HT; + } + } + + capa_res = wpa_drv_get_capa(wpa_s, &capa); + if (capa_res == 0) { wpa_s->drv_capa_known = 1; wpa_s->drv_flags = capa.flags; wpa_s->drv_enc = capa.enc; + wpa_s->drv_smps_modes = capa.smps_modes; + wpa_s->drv_rrm_flags = capa.rrm_flags; wpa_s->probe_resp_offloads = capa.probe_resp_offloads; wpa_s->max_scan_ssids = capa.max_scan_ssids; wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids; @@ -2901,15 +4106,44 @@ next_driver: 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; + wpa_s->extended_capa = capa.extended_capa; + wpa_s->extended_capa_mask = capa.extended_capa_mask; + wpa_s->extended_capa_len = capa.extended_capa_len; + wpa_s->num_multichan_concurrent = + capa.num_multichan_concurrent; + wpa_s->wmm_ac_supported = capa.wmm_ac_supported; + + if (capa.mac_addr_rand_scan_supported) + wpa_s->mac_addr_rand_supported |= MAC_ADDR_RAND_SCAN; + if (wpa_s->sched_scan_supported && + capa.mac_addr_rand_sched_scan_supported) + wpa_s->mac_addr_rand_supported |= + (MAC_ADDR_RAND_SCHED_SCAN | MAC_ADDR_RAND_PNO); } if (wpa_s->max_remain_on_chan == 0) wpa_s->max_remain_on_chan = 1000; + /* + * Only take p2p_mgmt parameters when P2P Device is supported. + * Doing it here as it determines whether l2_packet_init() will be done + * during wpa_supplicant_driver_init(). + */ + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) + wpa_s->p2p_mgmt = iface->p2p_mgmt; + else + iface->p2p_mgmt = 1; + + if (wpa_s->num_multichan_concurrent == 0) + wpa_s->num_multichan_concurrent = 1; + if (wpa_supplicant_driver_init(wpa_s) < 0) return -1; #ifdef CONFIG_TDLS - if (wpa_tdls_init(wpa_s->wpa)) + if ((!iface->p2p_mgmt || + !(wpa_s->drv_flags & + WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) && + wpa_tdls_init(wpa_s->wpa)) return -1; #endif /* CONFIG_TDLS */ @@ -2946,22 +4180,45 @@ next_driver: return -1; } -#ifdef CONFIG_P2P - if (wpas_p2p_init(wpa_s->global, wpa_s) < 0) { + if (iface->p2p_mgmt && 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; + /* + * Set Wake-on-WLAN triggers, if configured. + * Note: We don't restore/remove the triggers on shutdown (it doesn't + * have effect anyway when the interface is down). + */ + if (capa_res == 0 && wpas_set_wowlan_triggers(wpa_s, &capa) < 0) + return -1; + +#ifdef CONFIG_EAP_PROXY +{ + size_t len; + wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, wpa_s->imsi, + &len); + if (wpa_s->mnc_len > 0) { + wpa_s->imsi[len] = '\0'; + wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)", + wpa_s->imsi, wpa_s->mnc_len); + } else { + wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available"); + } +} +#endif /* CONFIG_EAP_PROXY */ + if (pcsc_reader_init(wpa_s) < 0) return -1; if (wpas_init_ext_pw(wpa_s) < 0) return -1; + wpas_rrm_reset(wpa_s); + return 0; } @@ -2969,6 +4226,27 @@ next_driver: static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, int notify, int terminate) { + struct wpa_global *global = wpa_s->global; + struct wpa_supplicant *iface, *prev; + + if (wpa_s == wpa_s->parent) + wpas_p2p_group_remove(wpa_s, "*"); + + iface = global->ifaces; + while (iface) { + if (iface == wpa_s || iface->parent != wpa_s) { + iface = iface->next; + continue; + } + wpa_printf(MSG_DEBUG, + "Remove remaining child interface %s from parent %s", + iface->ifname, wpa_s->ifname); + prev = iface; + iface = iface->next; + wpa_supplicant_remove_iface(global, prev, terminate); + } + + wpa_s->disconnected = 1; if (wpa_s->drv_priv) { wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); @@ -2978,14 +4256,10 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, } wpa_supplicant_cleanup(wpa_s); + wpas_p2p_deinit_iface(wpa_s); -#ifdef CONFIG_P2P - if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) { - wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing " - "the management interface is being removed"); - wpas_p2p_deinit_global(wpa_s->global); - } -#endif /* CONFIG_P2P */ + wpas_ctrl_radio_work_flush(wpa_s); + radio_remove_interface(wpa_s); if (wpa_s->drv_priv) wpa_drv_deinit(wpa_s); @@ -3001,10 +4275,19 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, wpa_s->ctrl_iface = NULL; } +#ifdef CONFIG_MESH + if (wpa_s->ifmsh) { + wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh); + wpa_s->ifmsh = NULL; + } +#endif /* CONFIG_MESH */ + if (wpa_s->conf != NULL) { wpa_config_free(wpa_s->conf); wpa_s->conf = NULL; } + + os_free(wpa_s); } @@ -3012,6 +4295,7 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, * wpa_supplicant_add_iface - Add a new network interface * @global: Pointer to global data from wpa_supplicant_init() * @iface: Interface configuration options + * @parent: Parent interface or %NULL to assign new interface as parent * Returns: Pointer to the created interface or %NULL on failure * * This function is used to add new network interfaces for %wpa_supplicant. @@ -3021,7 +4305,8 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s, * e.g., when a hotplug network adapter is inserted. */ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, - struct wpa_interface *iface) + struct wpa_interface *iface, + struct wpa_supplicant *parent) { struct wpa_supplicant *wpa_s; struct wpa_interface t_iface; @@ -3030,7 +4315,7 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, if (global == NULL || iface == NULL) return NULL; - wpa_s = wpa_supplicant_alloc(); + wpa_s = wpa_supplicant_alloc(parent); if (wpa_s == NULL) return NULL; @@ -3055,19 +4340,19 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, wpa_printf(MSG_DEBUG, "Failed to add interface %s", iface->ifname); wpa_supplicant_deinit_iface(wpa_s, 0, 0); - os_free(wpa_s); return NULL; } - /* Notify the control interfaces about new iface */ - if (wpas_notify_iface_added(wpa_s)) { - wpa_supplicant_deinit_iface(wpa_s, 1, 0); - os_free(wpa_s); - return NULL; - } + if (iface->p2p_mgmt == 0) { + /* Notify the control interfaces about new iface */ + if (wpas_notify_iface_added(wpa_s)) { + wpa_supplicant_deinit_iface(wpa_s, 1, 0); + return NULL; + } - for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) - wpas_notify_network_added(wpa_s, ssid); + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) + wpas_notify_network_added(wpa_s, ssid); + } wpa_s->next = global->ifaces; global->ifaces = wpa_s; @@ -3075,6 +4360,16 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, wpa_dbg(wpa_s, MSG_DEBUG, "Added interface %s", wpa_s->ifname); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); +#ifdef CONFIG_P2P + if (wpa_s->global->p2p == NULL && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) && + wpas_p2p_add_p2pdev_interface(wpa_s, iface->conf_p2p_dev) < 0) { + wpa_printf(MSG_INFO, + "P2P: Failed to enable P2P Device interface"); + /* Try to continue without. P2P will be disabled. */ + } +#endif /* CONFIG_P2P */ + return wpa_s; } @@ -3095,6 +4390,10 @@ int wpa_supplicant_remove_iface(struct wpa_global *global, int terminate) { struct wpa_supplicant *prev; +#ifdef CONFIG_MESH + unsigned int mesh_if_created = wpa_s->mesh_if_created; + char *ifname = NULL; +#endif /* CONFIG_MESH */ /* Remove interface from the global list of interfaces */ prev = global->ifaces; @@ -3110,10 +4409,29 @@ int wpa_supplicant_remove_iface(struct wpa_global *global, wpa_dbg(wpa_s, MSG_DEBUG, "Removing interface %s", wpa_s->ifname); +#ifdef CONFIG_MESH + if (mesh_if_created) { + ifname = os_strdup(wpa_s->ifname); + if (ifname == NULL) { + wpa_dbg(wpa_s, MSG_ERROR, + "mesh: Failed to malloc ifname"); + return -1; + } + } +#endif /* CONFIG_MESH */ + if (global->p2p_group_formation == wpa_s) global->p2p_group_formation = NULL; + if (global->p2p_invite_group == wpa_s) + global->p2p_invite_group = NULL; wpa_supplicant_deinit_iface(wpa_s, 1, terminate); - os_free(wpa_s); + +#ifdef CONFIG_MESH + if (mesh_if_created) { + wpa_drv_if_remove(global->ifaces, WPA_IF_MESH, ifname); + os_free(ifname); + } +#endif /* CONFIG_MESH */ return 0; } @@ -3199,7 +4517,10 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) 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_file_path) + wpa_debug_open_file(params->wpa_debug_file_path); + else + wpa_debug_setup_stdout(); if (params->wpa_debug_syslog) wpa_debug_open_syslog(); if (params->wpa_debug_tracing) { @@ -3233,6 +4554,9 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) if (params->ctrl_interface) global->params.ctrl_interface = os_strdup(params->ctrl_interface); + if (params->ctrl_interface_group) + global->params.ctrl_interface_group = + os_strdup(params->ctrl_interface_group); if (params->override_driver) global->params.override_driver = os_strdup(params->override_driver); @@ -3274,7 +4598,7 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params) wpa_supplicant_deinit(global); return NULL; } - global->drv_priv = os_zalloc(global->drv_count * sizeof(void *)); + global->drv_priv = os_calloc(global->drv_count, sizeof(void *)); if (global->drv_priv == NULL) { wpa_supplicant_deinit(global); return NULL; @@ -3342,9 +4666,6 @@ void wpa_supplicant_deinit(struct wpa_global *global) #ifdef CONFIG_WIFI_DISPLAY wifi_display_deinit(global); #endif /* CONFIG_WIFI_DISPLAY */ -#ifdef CONFIG_P2P - wpas_p2p_deinit_global(global); -#endif /* CONFIG_P2P */ while (global->ifaces) wpa_supplicant_remove_iface(global, global->ifaces, 1); @@ -3375,10 +4696,13 @@ void wpa_supplicant_deinit(struct wpa_global *global) os_free(global->params.pid_file); } os_free(global->params.ctrl_interface); + os_free(global->params.ctrl_interface_group); os_free(global->params.override_driver); os_free(global->params.override_ctrl_interface); - os_free(global->p2p_disallow_freq); + os_free(global->p2p_disallow_freq.range); + os_free(global->p2p_go_avoid_freq.range); + os_free(global->add_psk); os_free(global); wpa_debug_close_syslog(); @@ -3407,16 +4731,12 @@ void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s) #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) +void add_freq(int *freqs, int *num_freqs, int freq) { int i; @@ -3437,7 +4757,7 @@ static int * get_bss_freqs_in_ess(struct wpa_supplicant *wpa_s) int *freqs; int num_freqs = 0; - freqs = os_zalloc(sizeof(int) * (max_freqs + 1)); + freqs = os_calloc(max_freqs + 1, sizeof(int)); if (freqs == NULL) return NULL; @@ -3470,12 +4790,31 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid) int count; int *freqs = NULL; + wpas_connect_work_done(wpa_s); + /* * Remove possible authentication timeout since the connection failed. */ eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); /* + * There is no point in blacklisting the AP if this event is + * generated based on local request to disconnect. + */ + if (wpa_s->own_disconnect_req) { + wpa_s->own_disconnect_req = 0; + wpa_dbg(wpa_s, MSG_DEBUG, + "Ignore connection failure due to local request to disconnect"); + return; + } + if (wpa_s->disconnected) { + wpa_dbg(wpa_s, MSG_DEBUG, "Ignore connection failure " + "indication since interface has been put into " + "disconnected state"); + return; + } + + /* * 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 @@ -3512,6 +4851,12 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid) */ count += wpa_s->extra_blacklist_count; + if (count > 3 && wpa_s->current_ssid) { + wpa_printf(MSG_DEBUG, "Continuous association failures - " + "consider temporary network disabling"); + wpas_auth_failed(wpa_s, "CONN_FAILED"); + } + switch (count) { case 1: timeout = 100; @@ -3539,17 +4884,6 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid) */ wpa_supplicant_req_scan(wpa_s, timeout / 1000, 1000 * (timeout % 1000)); - -#ifdef CONFIG_P2P - if (wpa_s->global->p2p_cb_on_scan_complete && !wpa_s->global->p2p_disabled && - wpa_s->global->p2p != NULL) { - wpa_s->global->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 " - "continued after failed association"); - } - } -#endif /* CONFIG_P2P */ } @@ -3583,7 +4917,7 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, wpa_s->reassociate = 1; break; case WPA_CTRL_REQ_EAP_PASSWORD: - os_free(eap->password); + bin_clear_free(eap->password, eap->password_len); eap->password = (u8 *) os_strdup(value); eap->password_len = os_strlen(value); eap->pending_req_password = 0; @@ -3591,7 +4925,7 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, wpa_s->reassociate = 1; break; case WPA_CTRL_REQ_EAP_NEW_PASSWORD: - os_free(eap->new_password); + bin_clear_free(eap->new_password, eap->new_password_len); eap->new_password = (u8 *) os_strdup(value); eap->new_password_len = os_strlen(value); eap->pending_req_new_password = 0; @@ -3599,14 +4933,14 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, wpa_s->reassociate = 1; break; case WPA_CTRL_REQ_EAP_PIN: - os_free(eap->pin); + str_clear_free(eap->pin); eap->pin = os_strdup(value); eap->pending_req_pin = 0; if (ssid == wpa_s->current_ssid) wpa_s->reassociate = 1; break; case WPA_CTRL_REQ_EAP_OTP: - os_free(eap->otp); + bin_clear_free(eap->otp, eap->otp_len); eap->otp = (u8 *) os_strdup(value); eap->otp_len = os_strlen(value); os_free(eap->pending_req_otp); @@ -3614,12 +4948,16 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, eap->pending_req_otp_len = 0; break; case WPA_CTRL_REQ_EAP_PASSPHRASE: - os_free(eap->private_key_passwd); - eap->private_key_passwd = (u8 *) os_strdup(value); + str_clear_free(eap->private_key_passwd); + eap->private_key_passwd = os_strdup(value); eap->pending_req_passphrase = 0; if (ssid == wpa_s->current_ssid) wpa_s->reassociate = 1; break; + case WPA_CTRL_REQ_SIM: + str_clear_free(eap->external_sim_resp); + eap->external_sim_resp = os_strdup(value); + break; default: wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field); return -1; @@ -3639,13 +4977,16 @@ int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) int i; unsigned int drv_enc; + if (wpa_s->p2p_mgmt) + return 1; /* no normal network profiles on p2p_mgmt interface */ + if (ssid == NULL) return 1; if (ssid->disabled) return 1; - if (wpa_s && wpa_s->drv_capa_known) + if (wpa_s->drv_capa_known) drv_enc = wpa_s->drv_enc; else drv_enc = (unsigned int) -1; @@ -3664,13 +5005,37 @@ int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) } if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set && - !ssid->ext_psk) + (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk) return 1; return 0; } +int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) +{ +#ifdef CONFIG_IEEE80211W + if (ssid == NULL || ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) { + if (wpa_s->conf->pmf == MGMT_FRAME_PROTECTION_OPTIONAL && + !(wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)) { + /* + * Driver does not support BIP -- ignore pmf=1 default + * since the connection with PMF would fail and the + * configuration does not require PMF to be enabled. + */ + return NO_MGMT_FRAME_PROTECTION; + } + + return wpa_s->conf->pmf; + } + + return ssid->ieee80211w; +#else /* CONFIG_IEEE80211W */ + return NO_MGMT_FRAME_PROTECTION; +#endif /* CONFIG_IEEE80211W */ +} + + int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s) { if (wpa_s->global->conc_pref == WPA_CONC_PREF_P2P) @@ -3681,11 +5046,11 @@ int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s) } -void wpas_auth_failed(struct wpa_supplicant *wpa_s) +void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason) { struct wpa_ssid *ssid = wpa_s->current_ssid; int dur; - struct os_time now; + struct os_reltime now; if (ssid == NULL) { wpa_printf(MSG_DEBUG, "Authentication failure but no known " @@ -3697,29 +5062,47 @@ void wpas_auth_failed(struct wpa_supplicant *wpa_s) return; ssid->auth_failures++; + +#ifdef CONFIG_P2P + if (ssid->p2p_group && + (wpa_s->p2p_in_provisioning || wpa_s->show_group_started)) { + /* + * Skip the wait time since there is a short timeout on the + * connection to a P2P group. + */ + return; + } +#endif /* CONFIG_P2P */ + if (ssid->auth_failures > 50) dur = 300; - else if (ssid->auth_failures > 20) - dur = 120; else if (ssid->auth_failures > 10) - dur = 60; + dur = 120; else if (ssid->auth_failures > 5) + dur = 90; + else if (ssid->auth_failures > 3) + dur = 60; + else if (ssid->auth_failures > 2) dur = 30; else if (ssid->auth_failures > 1) dur = 20; else dur = 10; - os_get_time(&now); + if (ssid->auth_failures > 1 && + wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) + dur += os_random() % (ssid->auth_failures * 10); + + os_get_reltime(&now); if (now.sec + dur <= ssid->disabled_until.sec) return; ssid->disabled_until.sec = now.sec + dur; wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TEMP_DISABLED - "id=%d ssid=\"%s\" auth_failures=%u duration=%d", + "id=%d ssid=\"%s\" auth_failures=%u duration=%d reason=%s", ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len), - ssid->auth_failures, dur); + ssid->auth_failures, dur, reason); } @@ -3788,9 +5171,375 @@ int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid, void wpas_request_connection(struct wpa_supplicant *wpa_s) { wpa_s->normal_scans = 0; + wpa_s->scan_req = NORMAL_SCAN_REQ; wpa_supplicant_reinit_autoscan(wpa_s); wpa_s->extra_blacklist_count = 0; wpa_s->disconnected = 0; wpa_s->reassociate = 1; - wpa_supplicant_req_scan(wpa_s, 0, 0); + + if (wpa_supplicant_fast_associate(wpa_s) != 1) + wpa_supplicant_req_scan(wpa_s, 0, 0); + else + wpa_s->reattach = 0; +} + + +void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title, + struct wpa_used_freq_data *freqs_data, + unsigned int len) +{ + unsigned int i; + + wpa_dbg(wpa_s, MSG_DEBUG, "Shared frequencies (len=%u): %s", + len, title); + for (i = 0; i < len; i++) { + struct wpa_used_freq_data *cur = &freqs_data[i]; + wpa_dbg(wpa_s, MSG_DEBUG, "freq[%u]: %d, flags=0x%X", + i, cur->freq, cur->flags); + } +} + + +/* + * Find the operating frequencies of any of the virtual interfaces that + * are using the same radio as the current interface, and in addition, get + * information about the interface types that are using the frequency. + */ +int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s, + struct wpa_used_freq_data *freqs_data, + unsigned int len) +{ + struct wpa_supplicant *ifs; + u8 bssid[ETH_ALEN]; + int freq; + unsigned int idx = 0, i; + + wpa_dbg(wpa_s, MSG_DEBUG, + "Determining shared radio frequencies (max len %u)", len); + os_memset(freqs_data, 0, sizeof(struct wpa_used_freq_data) * len); + + dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant, + radio_list) { + if (idx == len) + break; + + if (ifs->current_ssid == NULL || ifs->assoc_freq == 0) + continue; + + if (ifs->current_ssid->mode == WPAS_MODE_AP || + ifs->current_ssid->mode == WPAS_MODE_P2P_GO) + freq = ifs->current_ssid->frequency; + else if (wpa_drv_get_bssid(ifs, bssid) == 0) + freq = ifs->assoc_freq; + else + continue; + + /* Hold only distinct freqs */ + for (i = 0; i < idx; i++) + if (freqs_data[i].freq == freq) + break; + + if (i == idx) + freqs_data[idx++].freq = freq; + + if (ifs->current_ssid->mode == WPAS_MODE_INFRA) { + freqs_data[i].flags = ifs->current_ssid->p2p_group ? + WPA_FREQ_USED_BY_P2P_CLIENT : + WPA_FREQ_USED_BY_INFRA_STATION; + } + } + + dump_freq_data(wpa_s, "completed iteration", freqs_data, idx); + return idx; +} + + +/* + * Find the operating frequencies of any of the virtual interfaces that + * are using the same radio as the current interface. + */ +int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, + int *freq_array, unsigned int len) +{ + struct wpa_used_freq_data *freqs_data; + int num, i; + + os_memset(freq_array, 0, sizeof(int) * len); + + freqs_data = os_calloc(len, sizeof(struct wpa_used_freq_data)); + if (!freqs_data) + return -1; + + num = get_shared_radio_freqs_data(wpa_s, freqs_data, len); + for (i = 0; i < num; i++) + freq_array[i] = freqs_data[i].freq; + + os_free(freqs_data); + + return num; +} + + +static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx) +{ + struct rrm_data *rrm = data; + + if (!rrm->notify_neighbor_rep) { + wpa_printf(MSG_ERROR, + "RRM: Unexpected neighbor report timeout"); + return; + } + + wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE"); + rrm->notify_neighbor_rep(rrm->neighbor_rep_cb_ctx, NULL); + + rrm->notify_neighbor_rep = NULL; + rrm->neighbor_rep_cb_ctx = NULL; +} + + +/* + * wpas_rrm_reset - Clear and reset all RRM data in wpa_supplicant + * @wpa_s: Pointer to wpa_supplicant + */ +void wpas_rrm_reset(struct wpa_supplicant *wpa_s) +{ + wpa_s->rrm.rrm_used = 0; + + eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm, + NULL); + if (wpa_s->rrm.notify_neighbor_rep) + wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL); + wpa_s->rrm.next_neighbor_rep_token = 1; +} + + +/* + * wpas_rrm_process_neighbor_rep - Handle incoming neighbor report + * @wpa_s: Pointer to wpa_supplicant + * @report: Neighbor report buffer, prefixed by a 1-byte dialog token + * @report_len: Length of neighbor report buffer + */ +void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s, + const u8 *report, size_t report_len) +{ + struct wpabuf *neighbor_rep; + + wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len); + if (report_len < 1) + return; + + if (report[0] != wpa_s->rrm.next_neighbor_rep_token - 1) { + wpa_printf(MSG_DEBUG, + "RRM: Discarding neighbor report with token %d (expected %d)", + report[0], wpa_s->rrm.next_neighbor_rep_token - 1); + return; + } + + eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm, + NULL); + + if (!wpa_s->rrm.notify_neighbor_rep) { + wpa_printf(MSG_ERROR, "RRM: Unexpected neighbor report"); + return; + } + + /* skipping the first byte, which is only an id (dialog token) */ + neighbor_rep = wpabuf_alloc(report_len - 1); + if (neighbor_rep == NULL) + return; + wpabuf_put_data(neighbor_rep, report + 1, report_len - 1); + wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)", + report[0]); + wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx, + neighbor_rep); + wpa_s->rrm.notify_neighbor_rep = NULL; + wpa_s->rrm.neighbor_rep_cb_ctx = NULL; +} + + +#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS) +/* Workaround different, undefined for Windows, error codes used here */ +#define ENOTCONN -1 +#define EOPNOTSUPP -1 +#define ECANCELED -1 +#endif + +/** + * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP + * @wpa_s: Pointer to wpa_supplicant + * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE + * is sent in the request. + * @cb: Callback function to be called once the requested report arrives, or + * timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds. + * In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's + * the requester's responsibility to free it. + * In the latter case NULL will be sent in 'neighbor_rep'. + * @cb_ctx: Context value to send the callback function + * Returns: 0 in case of success, negative error code otherwise + * + * In case there is a previous request which has not been answered yet, the + * new request fails. The caller may retry after RRM_NEIGHBOR_REPORT_TIMEOUT. + * Request must contain a callback function. + */ +int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid, + void (*cb)(void *ctx, + struct wpabuf *neighbor_rep), + void *cb_ctx) +{ + struct wpabuf *buf; + const u8 *rrm_ie; + + if (wpa_s->wpa_state != WPA_COMPLETED || wpa_s->current_ssid == NULL) { + wpa_printf(MSG_DEBUG, "RRM: No connection, no RRM."); + return -ENOTCONN; + } + + if (!wpa_s->rrm.rrm_used) { + wpa_printf(MSG_DEBUG, "RRM: No RRM in current connection."); + return -EOPNOTSUPP; + } + + rrm_ie = wpa_bss_get_ie(wpa_s->current_bss, + WLAN_EID_RRM_ENABLED_CAPABILITIES); + if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) || + !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) { + wpa_printf(MSG_DEBUG, + "RRM: No network support for Neighbor Report."); + return -EOPNOTSUPP; + } + + if (!cb) { + wpa_printf(MSG_DEBUG, + "RRM: Neighbor Report request must provide a callback."); + return -EINVAL; + } + + /* Refuse if there's a live request */ + if (wpa_s->rrm.notify_neighbor_rep) { + wpa_printf(MSG_DEBUG, + "RRM: Currently handling previous Neighbor Report."); + return -EBUSY; + } + + /* 3 = action category + action code + dialog token */ + buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0)); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, + "RRM: Failed to allocate Neighbor Report Request"); + return -ENOMEM; + } + + wpa_printf(MSG_DEBUG, "RRM: Neighbor report request (for %s), token=%d", + (ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""), + wpa_s->rrm.next_neighbor_rep_token); + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST); + wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token); + if (ssid) { + wpabuf_put_u8(buf, WLAN_EID_SSID); + wpabuf_put_u8(buf, ssid->ssid_len); + wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len); + } + + wpa_s->rrm.next_neighbor_rep_token++; + + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0) < 0) { + wpa_printf(MSG_DEBUG, + "RRM: Failed to send Neighbor Report Request"); + wpabuf_free(buf); + return -ECANCELED; + } + + wpa_s->rrm.neighbor_rep_cb_ctx = cb_ctx; + wpa_s->rrm.notify_neighbor_rep = cb; + eloop_register_timeout(RRM_NEIGHBOR_REPORT_TIMEOUT, 0, + wpas_rrm_neighbor_rep_timeout_handler, + &wpa_s->rrm, NULL); + + wpabuf_free(buf); + return 0; +} + + +void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, + const u8 *src, + const u8 *frame, size_t len, + int rssi) +{ + struct wpabuf *buf; + const struct rrm_link_measurement_request *req; + struct rrm_link_measurement_report report; + + if (wpa_s->wpa_state != WPA_COMPLETED) { + wpa_printf(MSG_INFO, + "RRM: Ignoring link measurement request. Not associated"); + return; + } + + if (!wpa_s->rrm.rrm_used) { + wpa_printf(MSG_INFO, + "RRM: Ignoring link measurement request. Not RRM network"); + return; + } + + if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) { + wpa_printf(MSG_INFO, + "RRM: Measurement report failed. TX power insertion not supported"); + return; + } + + req = (const struct rrm_link_measurement_request *) frame; + if (len < sizeof(*req)) { + wpa_printf(MSG_INFO, + "RRM: Link measurement report failed. Request too short"); + return; + } + + os_memset(&report, 0, sizeof(report)); + report.tpc.eid = WLAN_EID_TPC_REPORT; + report.tpc.len = 2; + report.rsni = 255; /* 255 indicates that RSNI is not available */ + report.dialog_token = req->dialog_token; + + /* + * It's possible to estimate RCPI based on RSSI in dBm. This + * calculation will not reflect the correct value for high rates, + * but it's good enough for Action frames which are transmitted + * with up to 24 Mbps rates. + */ + if (!rssi) + report.rcpi = 255; /* not available */ + else if (rssi < -110) + report.rcpi = 0; + else if (rssi > 0) + report.rcpi = 220; + else + report.rcpi = (rssi + 110) * 2; + + /* action_category + action_code */ + buf = wpabuf_alloc(2 + sizeof(report)); + if (buf == NULL) { + wpa_printf(MSG_ERROR, + "RRM: Link measurement report failed. Buffer allocation failed"); + return; + } + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REPORT); + wpabuf_put_data(buf, &report, sizeof(report)); + wpa_hexdump(MSG_DEBUG, "RRM: Link measurement report:", + wpabuf_head(buf), wpabuf_len(buf)); + + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0)) { + wpa_printf(MSG_ERROR, + "RRM: Link measurement report failed. Send action failed"); + } + wpabuf_free(buf); } diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index 381ab47ccc5c6..8964b3f720a24 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -81,6 +81,8 @@ ctrl_interface=/var/run/wpa_supplicant # to make wpa_supplicant interoperate with these APs, the version number is set # to 1 by default. This configuration value can be used to set it to the new # version (2). +# Note: When using MACsec, eapol_version shall be set to 3, which is +# defined in IEEE Std 802.1X-2010. eapol_version=1 # AP scanning/selection @@ -97,6 +99,8 @@ eapol_version=1 # non-WPA drivers when using IEEE 802.1X mode; do not try to associate with # APs (i.e., external program needs to control association). This mode must # also be used when using wired Ethernet drivers. +# Note: macsec_qca driver is one type of Ethernet driver which implements +# macsec feature. # 2: like 0, but associate with APs using security policy and SSID (but not # BSSID); this can be used, e.g., with ndiswrapper and NDIS drivers to # enable operation with hidden SSIDs and optimized roaming; in this mode, @@ -110,6 +114,30 @@ eapol_version=1 # networks are found, a new IBSS or AP mode network is created. ap_scan=1 +# MPM residency +# By default, wpa_supplicant implements the mesh peering manager (MPM) for an +# open mesh. However, if the driver can implement the MPM, you may set this to +# 0 to use the driver version. When AMPE is enabled, the wpa_supplicant MPM is +# always used. +# 0: MPM lives in the driver +# 1: wpa_supplicant provides an MPM which handles peering (default) +#user_mpm=1 + +# Maximum number of peer links (0-255; default: 99) +# Maximum number of mesh peering currently maintained by the STA. +#max_peer_links=99 + +# Timeout in seconds to detect STA inactivity (default: 300 seconds) +# +# This timeout value is used in mesh STA to clean up inactive stations. +#mesh_max_inactivity=300 + +# cert_in_cb - Whether to include a peer certificate dump in events +# This controls whether peer certificates for authentication server and +# its certificate chain are included in EAP peer certificate events. This is +# enabled by default. +#cert_in_cb=1 + # EAP fast re-authentication # By default, fast re-authentication is enabled for all EAP methods that # support it. This variable can be used to disable fast re-authentication. @@ -128,6 +156,16 @@ fast_reauth=1 # configure the path to the pkcs11 module required by the pkcs11 engine #pkcs11_module_path=/usr/lib/pkcs11/opensc-pkcs11.so +# OpenSSL cipher string +# +# This is an OpenSSL specific configuration option for configuring the default +# ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the default. +# See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation +# on cipher suite configuration. This is applicable only if wpa_supplicant is +# built to use OpenSSL. +#openssl_ciphers=DEFAULT:!EXP:!LOW + + # Dynamic EAP methods # If EAP methods were built dynamically as shared object files, they need to be # loaded here before being used in the network blocks. By default, EAP methods @@ -241,14 +279,14 @@ fast_reauth=1 # This is an optional set of parameters for automatic scanning # within an interface in following format: #autoscan=<autoscan module name>:<module parameters> -# autoscan is like bgscan but on disconnected or inactive state. -# For instance, on exponential module parameters would be <base>:<limit> +# autoscan is like bgscan but on disconnected or inactive state. +# For instance, on exponential module parameters would be <base>:<limit> #autoscan=exponential:3:300 # Which means a delay between scans on a base exponential of 3, -# up to the limit of 300 seconds (3, 9, 27 ... 300) -# For periodic module, parameters would be <fixed interval> +# up to the limit of 300 seconds (3, 9, 27 ... 300) +# For periodic module, parameters would be <fixed interval> #autoscan=periodic:30 -# So a delay of 30 seconds will be applied between each scan +# So a delay of 30 seconds will be applied between each scan # filter_ssids - SSID-based scan result filtering # 0 = do not filter scan results (default) @@ -265,6 +303,19 @@ fast_reauth=1 # inactive stations. #p2p_go_max_inactivity=300 +# Passphrase length (8..63) for P2P GO +# +# This parameter controls the length of the random passphrase that is +# generated at the GO. Default: 8. +#p2p_passphrase_len=8 + +# Extra delay between concurrent P2P search iterations +# +# This value adds extra delay in milliseconds between concurrent search +# iterations to make p2p_find friendlier to concurrent operations by avoiding +# it from taking 100% of radio resources. The default value is 500 ms. +#p2p_search_delay=500 + # Opportunistic Key Caching (also known as Proactive Key Caching) default # This parameter can be used to set the default behavior for the # proactive_key_caching parameter. By default, OKC is disabled unless enabled @@ -281,6 +332,59 @@ fast_reauth=1 # ieee80211w parameter. #pmf=0 +# Enabled SAE finite cyclic groups in preference order +# By default (if this parameter is not set), the mandatory group 19 (ECC group +# defined over a 256-bit prime order field) is preferred, but other groups are +# also enabled. If this parameter is set, the groups will be tried in the +# indicated order. The group values are listed in the IANA registry: +# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9 +#sae_groups=21 20 19 26 25 + +# Default value for DTIM period (if not overridden in network block) +#dtim_period=2 + +# Default value for Beacon interval (if not overridden in network block) +#beacon_int=100 + +# Additional vendor specific elements for Beacon and Probe Response frames +# This parameter can be used to add additional vendor specific element(s) into +# the end of the Beacon and Probe Response frames. The format for these +# element(s) is a hexdump of the raw information elements (id+len+payload for +# one or more elements). This is used in AP and P2P GO modes. +#ap_vendor_elements=dd0411223301 + +# Ignore scan results older than request +# +# The driver may have a cache of scan results that makes it return +# information that is older than our scan trigger. This parameter can +# be used to configure such old information to be ignored instead of +# allowing it to update the internal BSS table. +#ignore_old_scan_res=0 + +# scan_cur_freq: Whether to scan only the current frequency +# 0: Scan all available frequencies. (Default) +# 1: Scan current operating frequency if another VIF on the same radio +# is already associated. + +# MAC address policy default +# 0 = use permanent MAC address +# 1 = use random MAC address for each ESS connection +# 2 = like 1, but maintain OUI (with local admin bit set) +# +# By default, permanent MAC address is used unless policy is changed by +# the per-network mac_addr parameter. Global mac_addr=1 can be used to +# change this default behavior. +#mac_addr=0 + +# Lifetime of random MAC address in seconds (default: 60) +#rand_addr_lifetime=60 + +# MAC address policy for pre-association operations (scanning, ANQP) +# 0 = use permanent MAC address +# 1 = use random MAC address +# 2 = like 1, but maintain OUI (with local admin bit set) +#preassoc_mac_addr=0 + # Interworking (IEEE 802.11u) # Enable Interworking @@ -308,6 +412,8 @@ fast_reauth=1 # # credential fields: # +# temporary: Whether this credential is temporary and not to be saved +# # priority: Priority group # By default, all networks and credentials get the same priority group # (0). This field can be used to give higher priority for credentials @@ -365,9 +471,11 @@ fast_reauth=1 # milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN> # format # -# domain: Home service provider FQDN +# domain: Home service provider FQDN(s) # This is used to compare against the Domain Name List to figure out -# whether the AP is operated by the Home SP. +# whether the AP is operated by the Home SP. Multiple domain entries can +# be used to configure alternative FQDNs that will be considered home +# networks. # # roaming_consortium: Roaming Consortium OI # If roaming_consortium_len is non-zero, this field contains the @@ -394,6 +502,59 @@ fast_reauth=1 # matching with the network. Multiple entries can be used to specify more # than one SSID. # +# roaming_partner: Roaming partner information +# This optional field can be used to configure preferences between roaming +# partners. The field is a string in following format: +# <FQDN>,<0/1 exact match>,<priority>,<* or country code> +# (non-exact match means any subdomain matches the entry; priority is in +# 0..255 range with 0 being the highest priority) +# +# update_identifier: PPS MO ID +# (Hotspot 2.0 PerProviderSubscription/UpdateIdentifier) +# +# provisioning_sp: FQDN of the SP that provisioned the credential +# This optional field can be used to keep track of the SP that provisioned +# the credential to find the PPS MO (./Wi-Fi/<provisioning_sp>). +# +# Minimum backhaul threshold (PPS/<X+>/Policy/MinBackhauldThreshold/*) +# These fields can be used to specify minimum download/upload backhaul +# bandwidth that is preferred for the credential. This constraint is +# ignored if the AP does not advertise WAN Metrics information or if the +# limit would prevent any connection. Values are in kilobits per second. +# min_dl_bandwidth_home +# min_ul_bandwidth_home +# min_dl_bandwidth_roaming +# min_ul_bandwidth_roaming +# +# max_bss_load: Maximum BSS Load Channel Utilization (1..255) +# (PPS/<X+>/Policy/MaximumBSSLoadValue) +# This value is used as the maximum channel utilization for network +# selection purposes for home networks. If the AP does not advertise +# BSS Load or if the limit would prevent any connection, this constraint +# will be ignored. +# +# req_conn_capab: Required connection capability +# (PPS/<X+>/Policy/RequiredProtoPortTuple) +# This value is used to configure set of required protocol/port pairs that +# a roaming network shall support (include explicitly in Connection +# Capability ANQP element). This constraint is ignored if the AP does not +# advertise Connection Capability or if this constraint would prevent any +# network connection. This policy is not used in home networks. +# Format: <protocol>[:<comma-separated list of ports] +# Multiple entries can be used to list multiple requirements. +# For example, number of common TCP protocols: +# req_conn_capab=6,22,80,443 +# For example, IPSec/IKE: +# req_conn_capab=17:500 +# req_conn_capab=50 +# +# ocsp: Whether to use/require OCSP to check server certificate +# 0 = do not use OCSP stapling (TLS certificate status extension) +# 1 = try to use OCSP stapling, but not require response +# 2 = require valid OCSP stapling response +# +# sim_num: Identifier for which SIM to use in multi-SIM devices +# # for example: # #cred={ @@ -470,9 +631,10 @@ fast_reauth=1 # 0 = infrastructure (Managed) mode, i.e., associate with an AP (default) # 1 = IBSS (ad-hoc, peer-to-peer) # 2 = AP (access point) -# Note: IBSS can only be used with key_mgmt NONE (plaintext and static WEP) -# and key_mgmt=WPA-NONE (fixed group key TKIP/CCMP). WPA-None requires -# following network block options: +# Note: IBSS can only be used with key_mgmt NONE (plaintext and static WEP) and +# WPA-PSK (with proto=RSN). In addition, key_mgmt=WPA-NONE (fixed group key +# TKIP/CCMP) is available for backwards compatibility, but its use is +# deprecated. WPA-None requires following network block options: # proto=WPA, key_mgmt=WPA-NONE, pairwise=NONE, group=TKIP (or CCMP, but not # both), and psk must also be set. # @@ -494,6 +656,9 @@ fast_reauth=1 # set, scan results that do not match any of the specified frequencies are not # considered when selecting a BSS. # +# This can also be set on the outside of the network block. In this case, +# it limits the frequencies that will be scanned. +# # bgscan: Background scanning # wpa_supplicant behavior for background scanning can be specified by # configuring a bgscan module. These modules are responsible for requesting @@ -510,6 +675,12 @@ fast_reauth=1 # bgscan="learn:<short bgscan interval in seconds>:<signal strength threshold>: # <long interval>[:<database file name>]" # bgscan="learn:30:-45:300:/etc/wpa_supplicant/network1.bgscan" +# Explicitly disable bgscan by setting +# bgscan="" +# +# This option can also be set outside of all network blocks for the bgscan +# parameter to apply for all the networks that have no specific bgscan +# parameter. # # proto: list of accepted protocols # WPA = WPA/IEEE 802.11i/D3.0 @@ -574,8 +745,16 @@ fast_reauth=1 # bit0 (1): require dynamically generated unicast WEP key # bit1 (2): require dynamically generated broadcast WEP key # (3 = require both keys; default) -# Note: When using wired authentication, eapol_flags must be set to 0 for the -# authentication to be completed successfully. +# Note: When using wired authentication (including macsec_qca driver), +# eapol_flags must be set to 0 for the authentication to be completed +# successfully. +# +# macsec_policy: IEEE 802.1X/MACsec options +# This determines how sessions are secured with MACsec. It is currently +# applicable only when using the macsec_qca driver interface. +# 0: MACsec not in use (default) +# 1: MACsec enabled - Should secure, accept key server's advice to +# determine whether to use a secure session or not. # # 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 @@ -697,6 +876,10 @@ fast_reauth=1 # sertificate is only accepted if it contains this string in the subject. # The subject string is in following format: # /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@example.com +# Note: Since this is a substring match, this cannot be used securily to +# do a suffix match against a possible domain name in the CN entry. For +# such a use case, domain_suffix_match or domain_match should be used +# instead. # altsubject_match: Semicolon separated string of entries to be matched against # the alternative subject name of the authentication server certificate. # If this string is set, the server sertificate is only accepted if it @@ -705,6 +888,30 @@ fast_reauth=1 # Example: EMAIL:server@example.com # Example: DNS:server.example.com;DNS:server2.example.com # Following types are supported: EMAIL, DNS, URI +# domain_suffix_match: Constraint for server domain name. If set, this FQDN is +# used as a suffix match requirement for the AAAserver certificate in +# SubjectAltName dNSName element(s). If a matching dNSName is found, this +# constraint is met. If no dNSName values are present, this constraint is +# matched against SubjectName CN using same suffix match comparison. +# +# Suffix match here means that the host/domain name is compared one label +# at a time starting from the top-level domain and all the labels in +# domain_suffix_match shall be included in the certificate. The +# certificate may include additional sub-level labels in addition to the +# required labels. +# +# For example, domain_suffix_match=example.com would match +# test.example.com but would not match test-example.com. +# domain_match: Constraint for server domain name +# If set, this FQDN is used as a full match requirement for the +# server certificate in SubjectAltName dNSName element(s). If a +# matching dNSName is found, this constraint is met. If no dNSName +# values are present, this constraint is matched against SubjectName CN +# using same full match comparison. This behavior is similar to +# domain_suffix_match, but has the requirement of a full match, i.e., +# no subdomains or wildcard matches are allowed. Case-insensitive +# comparison is used, so "Example.com" matches "example.com", but would +# not match "test.Example.com". # phase1: Phase1 (outer authentication, i.e., TLS tunnel) parameters # (string with field-value pairs, e.g., "peapver=0" or # "peapver=1 peaplabel=1") @@ -733,9 +940,20 @@ fast_reauth=1 # * 2 = require cryptobinding # EAP-WSC (WPS) uses following options: pin=<Device Password> or # pbc=1. +# +# For wired IEEE 802.1X authentication, "allow_canned_success=1" can be +# used to configure a mode that allows EAP-Success (and EAP-Failure) +# without going through authentication step. Some switches use such +# sequence when forcing the port to be authorized/unauthorized or as a +# fallback option if the authentication server is unreachable. By default, +# wpa_supplicant discards such frames to protect against potential attacks +# by rogue devices, but this option can be used to disable that protection +# for cases where the server/authenticator does not need to be +# authenticated. # phase2: Phase2 (inner authentication with TLS tunnel) parameters # (string with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or -# "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS) +# "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS). "mschapv2_retry=0" can be +# used to disable MSCHAPv2 password retry in authentication failure cases. # # TLS-based methods can use the following parameters to control TLS behavior # (these are normally in the phase1 parameter, but can be used also in the @@ -754,6 +972,10 @@ fast_reauth=1 # EAP workarounds are disabled with eap_workarounds=0. # For EAP-FAST, this must be set to 0 (or left unconfigured for the # default value to be used automatically). +# tls_disable_tlsv1_1=1 - disable use of TLSv1.1 (a workaround for AAA servers +# that have issues interoperating with updated TLS version) +# tls_disable_tlsv1_2=1 - disable use of TLSv1.2 (a workaround for AAA servers +# that have issues interoperating with updated TLS version) # # Following certificate/private key fields are used in inner Phase2 # authentication when using EAP-TTLS or EAP-PEAP. @@ -767,9 +989,12 @@ fast_reauth=1 # private_key2_passwd: Password for private key file # dh_file2: File path to DH/DSA parameters file (in PEM format) # subject_match2: Substring to be matched against the subject of the -# authentication server certificate. -# altsubject_match2: Substring to be matched against the alternative subject -# name of the authentication server certificate. +# authentication server certificate. See subject_match for more details. +# altsubject_match2: Semicolon separated string of entries to be matched +# against the alternative subject name of the authentication server +# certificate. See altsubject_match documentation for more details. +# domain_suffix_match2: Constraint for server domain name. See +# domain_suffix_match for more details. # # fragment_size: Maximum EAP fragment size in bytes (default 1398). # This value limits the fragment size for EAP methods that support @@ -778,6 +1003,17 @@ fast_reauth=1 # interface used for EAPOL. The default value is suitable for most # cases. # +# ocsp: Whether to use/require OCSP to check server certificate +# 0 = do not use OCSP stapling (TLS certificate status extension) +# 1 = try to use OCSP stapling, but not require response +# 2 = require valid OCSP stapling response +# +# openssl_ciphers: OpenSSL specific cipher configuration +# This can be used to override the global openssl_ciphers configuration +# parameter (see above). +# +# erp: Whether EAP Re-authentication Protocol (ERP) is enabled +# # EAP-FAST variables: # pac_file: File path for the PAC entries. wpa_supplicant will need to be able # to create this file and write updates to it when PAC is being @@ -824,6 +1060,15 @@ fast_reauth=1 # DTIM period in Beacon intervals for AP mode (default: 2) #dtim_period=2 +# Beacon interval (default: 100 TU) +#beacon_int=100 + +# MAC address policy +# 0 = use permanent MAC address +# 1 = use random MAC address for each ESS connection +# 2 = like 1, but maintain OUI (with local admin bit set) +#mac_addr=0 + # disable_ht: Whether HT (802.11n) should be disabled. # 0 = HT enabled (if AP supports it) # 1 = HT disabled @@ -836,6 +1081,14 @@ fast_reauth=1 # 0 = SGI enabled (if AP supports it) # 1 = SGI disabled # +# disable_ldpc: Whether LDPC should be disabled. +# 0 = LDPC enabled (if AP supports it) +# 1 = LDPC disabled +# +# ht40_intolerant: Whether 40 MHz intolerant should be indicated. +# 0 = 40 MHz tolerant (default) +# 1 = 40 MHz intolerant +# # ht_mcs: Configure allowed MCS rates. # Parsed as an array of bytes, in base-16 (ascii-hex) # ht_mcs="" // Use all available (default) @@ -847,11 +1100,28 @@ fast_reauth=1 # 0 = Enable MAX-AMSDU if hardware supports it. # 1 = Disable AMSDU # +# ampdu_factor: Maximum A-MPDU Length Exponent +# Value: 0-3, see 7.3.2.56.3 in IEEE Std 802.11n-2009. +# # ampdu_density: Allow overriding AMPDU density configuration. # Treated as hint by the kernel. # -1 = Do not make any changes. # 0-3 = Set AMPDU density (aka factor) to specified value. +# disable_vht: Whether VHT should be disabled. +# 0 = VHT enabled (if AP supports it) +# 1 = VHT disabled +# +# vht_capa: VHT capabilities to set in the override +# vht_capa_mask: mask of VHT capabilities +# +# vht_rx_mcs_nss_1/2/3/4/5/6/7/8: override the MCS set for RX NSS 1-8 +# vht_tx_mcs_nss_1/2/3/4/5/6/7/8: override the MCS set for TX NSS 1-8 +# 0: MCS 0-7 +# 1: MCS 0-8 +# 2: MCS 0-9 +# 3: not supported + # Example blocks: # Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers @@ -1098,7 +1368,19 @@ network={ } -# IBSS/ad-hoc network with WPA-None/TKIP. +# IBSS/ad-hoc network with RSN +network={ + ssid="ibss-rsn" + key_mgmt=WPA-PSK + proto=RSN + psk="12345678" + mode=1 + frequency=2412 + pairwise=CCMP + group=CCMP +} + +# IBSS/ad-hoc network with WPA-None/TKIP (deprecated) network={ ssid="test adhoc" mode=1 @@ -1110,6 +1392,23 @@ network={ psk="secret passphrase" } +# open mesh network +network={ + ssid="test mesh" + mode=5 + frequency=2437 + key_mgmt=NONE +} + +# secure (SAE + AMPE) network +network={ + ssid="secure mesh" + mode=5 + frequency=2437 + key_mgmt=SAE + psk="very secret passphrase" +} + # Catch all example that allows more or less all configuration modes network={ @@ -1184,3 +1483,39 @@ SGVsbG8gV29ybGQhCg== network={ key_mgmt=NONE } + +# Example configuration blacklisting two APs - these will be ignored +# for this network. +network={ + ssid="example" + psk="very secret passphrase" + bssid_blacklist=02:11:22:33:44:55 02:22:aa:44:55:66 +} + +# Example configuration limiting AP selection to a specific set of APs; +# any other AP not matching the masked address will be ignored. +network={ + ssid="example" + psk="very secret passphrase" + bssid_whitelist=02:55:ae:bc:00:00/ff:ff:ff:ff:00:00 00:00:77:66:55:44/00:00:ff:ff:ff:ff +} + +# Example config file that will only scan on channel 36. +freq_list=5180 +network={ + key_mgmt=NONE +} + + +# Example MACsec configuration +#network={ +# key_mgmt=IEEE8021X +# eap=TTLS +# phase2="auth=PAP" +# anonymous_identity="anonymous@example.com" +# identity="user@example.com" +# password="secretr" +# ca_cert="/etc/cert/ca.pem" +# eapol_flags=0 +# macsec_policy=1 +#} diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 544977b470e95..26ff216b02da4 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1,6 +1,6 @@ /* * wpa_supplicant - Internal definitions - * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -11,7 +11,11 @@ #include "utils/list.h" #include "common/defs.h" +#include "common/sae.h" +#include "common/wpa_ctrl.h" +#include "wps/wps_defs.h" #include "config_ssid.h" +#include "wmm_ac.h" extern const char *wpa_supplicant_version; extern const char *wpa_supplicant_license; @@ -55,6 +59,25 @@ struct wpa_interface { const char *confname; /** + * confanother - Additional configuration name (file or profile) name + * + * This can also be %NULL when the additional configuration file is not + * used. + */ + const char *confanother; + +#ifdef CONFIG_P2P + /** + * conf_p2p_dev - Configuration file used to hold the + * P2P Device configuration parameters. + * + * This can also be %NULL. In such a case, if a P2P Device dedicated + * interfaces is created, the main configuration file will be used. + */ + const char *conf_p2p_dev; +#endif /* CONFIG_P2P */ + + /** * ctrl_interface - Control interface parameter * * If a configuration file is not used, this variable can be used to @@ -95,6 +118,15 @@ struct wpa_interface { * receiving of EAPOL frames from an additional interface. */ const char *bridge_ifname; + + /** + * p2p_mgmt - Interface used for P2P management (P2P Device operations) + * + * Indicates whether wpas_p2p_init() must be called for this interface. + * This is used only when the driver supports a dedicated P2P Device + * interface that is not a network interface. + */ + int p2p_mgmt; }; /** @@ -146,6 +178,11 @@ struct wpa_params { char *ctrl_interface; /** + * ctrl_interface_group - Global ctrl_iface group + */ + char *ctrl_interface_group; + + /** * dbus_ctrl_interface - Enable the DBus control interface */ int dbus_ctrl_interface; @@ -204,12 +241,6 @@ struct p2p_srv_upnp { char *service; }; -struct wpa_freq_range { - unsigned int min; - unsigned int max; -}; - - /** * struct wpa_global - Internal, global data for all %wpa_supplicant interfaces * @@ -227,27 +258,93 @@ struct wpa_global { struct p2p_data *p2p; struct wpa_supplicant *p2p_init_wpa_s; struct wpa_supplicant *p2p_group_formation; + struct wpa_supplicant *p2p_invite_group; u8 p2p_dev_addr[ETH_ALEN]; + struct os_reltime p2p_go_wait_client; 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_freq_range *p2p_disallow_freq; - unsigned int num_p2p_disallow_freq; + struct wpa_freq_range_list p2p_disallow_freq; + struct wpa_freq_range_list p2p_go_avoid_freq; enum wpa_conc_pref { WPA_CONC_PREF_NOT_SET, WPA_CONC_PREF_STA, WPA_CONC_PREF_P2P } conc_pref; - unsigned int p2p_cb_on_scan_complete:1; + unsigned int p2p_per_sta_psk:1; + unsigned int p2p_fail_on_wps_complete:1; + unsigned int p2p_24ghz_social_channels:1; + unsigned int pending_p2ps_group:1; + unsigned int pending_group_iface_for_p2ps:1; #ifdef CONFIG_WIFI_DISPLAY int wifi_display; #define MAX_WFD_SUBELEMS 10 struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS]; #endif /* CONFIG_WIFI_DISPLAY */ + + struct psk_list_entry *add_psk; /* From group formation */ +}; + + +/** + * struct wpa_radio - Internal data for per-radio information + * + * This structure is used to share data about configured interfaces + * (struct wpa_supplicant) that share the same physical radio, e.g., to allow + * better coordination of offchannel operations. + */ +struct wpa_radio { + char name[16]; /* from driver_ops get_radio_name() or empty if not + * available */ + unsigned int external_scan_running:1; + struct dl_list ifaces; /* struct wpa_supplicant::radio_list entries */ + struct dl_list work; /* struct wpa_radio_work::list entries */ +}; + +/** + * struct wpa_radio_work - Radio work item + */ +struct wpa_radio_work { + struct dl_list list; + unsigned int freq; /* known frequency (MHz) or 0 for multiple/unknown */ + const char *type; + struct wpa_supplicant *wpa_s; + void (*cb)(struct wpa_radio_work *work, int deinit); + void *ctx; + unsigned int started:1; + struct os_reltime time; +}; + +int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, + const char *type, int next, + void (*cb)(struct wpa_radio_work *work, int deinit), + void *ctx); +void radio_work_done(struct wpa_radio_work *work); +void radio_remove_works(struct wpa_supplicant *wpa_s, + const char *type, int remove_all); +void radio_work_check_next(struct wpa_supplicant *wpa_s); +struct wpa_radio_work * +radio_work_pending(struct wpa_supplicant *wpa_s, const char *type); + +struct wpa_connect_work { + unsigned int sme:1; + unsigned int bss_removed:1; + struct wpa_bss *bss; + struct wpa_ssid *ssid; }; +int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss, + struct wpa_ssid *test_ssid); +void wpas_connect_work_free(struct wpa_connect_work *cwork); +void wpas_connect_work_done(struct wpa_supplicant *wpa_s); + +struct wpa_external_work { + unsigned int id; + char type[100]; + unsigned int timeout; +}; /** * offchannel_send_action_result - Result of offchannel send Action frame @@ -268,7 +365,7 @@ struct wps_ap_info { WPS_AP_SEL_REG_OUR } type; unsigned int tries; - struct os_time last_attempt; + struct os_reltime last_attempt; }; struct wpa_ssid_value { @@ -276,6 +373,44 @@ struct wpa_ssid_value { size_t ssid_len; }; +#define WPA_FREQ_USED_BY_INFRA_STATION BIT(0) +#define WPA_FREQ_USED_BY_P2P_CLIENT BIT(1) + +struct wpa_used_freq_data { + int freq; + unsigned int flags; +}; + +#define RRM_NEIGHBOR_REPORT_TIMEOUT 1 /* 1 second for AP to send a report */ + +/* + * struct rrm_data - Data used for managing RRM features + */ +struct rrm_data { + /* rrm_used - indication regarding the current connection */ + unsigned int rrm_used:1; + + /* + * notify_neighbor_rep - Callback for notifying report requester + */ + void (*notify_neighbor_rep)(void *ctx, struct wpabuf *neighbor_rep); + + /* + * neighbor_rep_cb_ctx - Callback context + * Received in the callback registration, and sent to the callback + * function as a parameter. + */ + void *neighbor_rep_cb_ctx; + + /* next_neighbor_rep_token - Next request's dialog token */ + u8 next_neighbor_rep_token; +}; + +enum wpa_supplicant_test_failure { + WPAS_TEST_FAILURE_NONE, + WPAS_TEST_FAILURE_SCAN_TRIGGER, +}; + /** * struct wpa_supplicant - Internal data for wpa_supplicant interface * @@ -286,11 +421,14 @@ struct wpa_ssid_value { */ struct wpa_supplicant { struct wpa_global *global; + struct wpa_radio *radio; /* shared radio context */ + struct dl_list radio_list; /* list head: struct wpa_radio::ifaces */ struct wpa_supplicant *parent; struct wpa_supplicant *next; struct l2_packet_data *l2; struct l2_packet_data *l2_br; unsigned char own_addr[ETH_ALEN]; + unsigned char perm_addr[ETH_ALEN]; char ifname[100]; #ifdef CONFIG_CTRL_IFACE_DBUS char *dbus_path; @@ -305,16 +443,20 @@ struct wpa_supplicant { char bridge_ifname[16]; char *confname; + char *confanother; + struct wpa_config *conf; int countermeasures; - os_time_t last_michael_mic_error; + struct os_reltime last_michael_mic_error; u8 bssid[ETH_ALEN]; u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this * field contains the target BSSID. */ int reassociate; /* reassociation requested */ + int reassoc_same_bss; /* reassociating to the same bss */ int disconnected; /* all connections disabled; i.e., do no reassociate * before this has been cleared */ struct wpa_ssid *current_ssid; + struct wpa_ssid *last_ssid; struct wpa_bss *current_bss; int ap_ies_from_associnfo; unsigned int assoc_freq; @@ -337,6 +479,11 @@ struct wpa_supplicant { struct wpa_ssid_value *disallow_aps_ssid; size_t disallow_aps_ssid_count; + enum { WPA_SETBAND_AUTO, WPA_SETBAND_5G, WPA_SETBAND_2G } setband; + + /* Preferred network for the next connection attempt */ + struct wpa_ssid *next_ssid; + /* previous scan was wildcard when interleaving between * wildcard scans and specific SSID scan when max_ssids=1 */ int prev_scan_wildcard; @@ -369,8 +516,7 @@ struct wpa_supplicant { struct wpa_bss **last_scan_res; unsigned int last_scan_res_used; unsigned int last_scan_res_size; - int last_scan_full; - struct os_time last_scan; + struct os_reltime last_scan; struct wpa_driver_ops *driver; int interface_removed; /* whether the network interface has been @@ -381,6 +527,7 @@ struct wpa_supplicant { struct ctrl_iface_priv *ctrl_iface; enum wpa_states wpa_state; + struct wpa_radio_work *scan_work; int scanning; int sched_scanning; int new_connection; @@ -389,14 +536,13 @@ struct wpa_supplicant { * previous association event */ struct scard_data *scard; -#ifdef PCSC_FUNCS char imsi[20]; int mnc_len; -#endif /* PCSC_FUNCS */ unsigned char last_eapol_src[ETH_ALEN]; - int keys_cleared; + unsigned int keys_cleared; /* bitfield of key indexes that the driver is + * known not to be configured with a key */ struct wpa_blacklist *blacklist; @@ -441,16 +587,32 @@ struct wpa_supplicant { * to be run. */ MANUAL_SCAN_REQ - } scan_req; + } scan_req, last_scan_req; + enum wpa_states scan_prev_wpa_state; + struct os_reltime scan_trigger_time, scan_start_time; int scan_runs; /* number of scan runs since WPS was started */ int *next_scan_freqs; + int *manual_scan_freqs; + int *manual_sched_scan_freqs; + unsigned int manual_scan_passive:1; + unsigned int manual_scan_use_id:1; + unsigned int manual_scan_only_new:1; + unsigned int own_scan_requested:1; + unsigned int own_scan_running:1; + unsigned int clear_driver_scan_cache:1; + unsigned int manual_scan_id; int scan_interval; /* time in sec between scans to find suitable AP */ int normal_scans; /* normal scans run before sched_scan */ int scan_for_connection; /* whether the scan request was triggered for * finding a connection */ +#define MAX_SCAN_ID 16 + int scan_id[MAX_SCAN_ID]; + unsigned int scan_id_count; - unsigned int drv_flags; + u64 drv_flags; unsigned int drv_enc; + unsigned int drv_smps_modes; + unsigned int drv_rrm_flags; /* * A bitmap of supported protocols for probe response offload. See @@ -458,6 +620,10 @@ struct wpa_supplicant { */ unsigned int probe_resp_offloads; + /* extended capabilities supported by the driver */ + const u8 *extended_capa, *extended_capa_mask; + unsigned int extended_capa_len; + int max_scan_ssids; int max_sched_scan_ssids; int sched_scan_supported; @@ -472,12 +638,19 @@ struct wpa_supplicant { struct wps_context *wps; int wps_success; /* WPS success event received */ struct wps_er *wps_er; + unsigned int wps_run; int blacklist_cleared; struct wpabuf *pending_eapol_rx; - struct os_time pending_eapol_rx_time; + struct os_reltime pending_eapol_rx_time; u8 pending_eapol_rx_src[ETH_ALEN]; unsigned int last_eapol_matches_bssid:1; + unsigned int eap_expected_failure:1; + unsigned int reattach:1; /* reassociation to the same BSS requested */ + unsigned int mac_addr_changed:1; + + struct os_reltime last_mac_addr_change; + int last_mac_addr_style; struct ibss_rsn *ibss_rsn; @@ -509,16 +682,20 @@ struct wpa_supplicant { 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; + struct os_reltime sa_query_start; + struct os_reltime last_unprot_disconnect; + enum { HT_SEC_CHAN_UNKNOWN, + HT_SEC_CHAN_ABOVE, + HT_SEC_CHAN_BELOW } ht_sec_chan; u8 sched_obss_scan; u16 obss_scan_int; u16 bss_max_idle_period; - enum { - SME_SAE_INIT, - SME_SAE_COMMIT, - SME_SAE_CONFIRM - } sae_state; - u16 sae_send_confirm; +#ifdef CONFIG_SAE + struct sae_data sae; + struct wpabuf *sae_token; + int sae_group_index; + unsigned int sae_pmksa_caching:1; +#endif /* CONFIG_SAE */ } sme; #endif /* CONFIG_SME */ @@ -529,6 +706,15 @@ struct wpa_supplicant { void *ap_configured_cb_data; #endif /* CONFIG_AP */ + struct hostapd_iface *ifmsh; +#ifdef CONFIG_MESH + struct mesh_rsn *mesh_rsn; + int mesh_if_idx; + unsigned int mesh_if_created:1; + unsigned int mesh_ht_enabled:1; + int mesh_auth_block_duration; /* sec */ +#endif /* CONFIG_MESH */ + unsigned int off_channel_freq; struct wpabuf *pending_action_tx; u8 pending_action_src[ETH_ALEN]; @@ -537,6 +723,7 @@ struct wpa_supplicant { unsigned int pending_action_freq; int pending_action_no_cck; int pending_action_without_roc; + unsigned int pending_action_tx_done:1; void (*pending_action_tx_status_cb)(struct wpa_supplicant *wpa_s, unsigned int freq, const u8 *dst, const u8 *src, const u8 *bssid, @@ -546,7 +733,10 @@ struct wpa_supplicant { unsigned int roc_waiting_drv_freq; int action_tx_wait_time; + int p2p_mgmt; + #ifdef CONFIG_P2P + struct wpa_supplicant *p2p_dev; struct p2p_go_neg_results *go_params; int create_p2p_iface; u8 pending_interface_addr[ETH_ALEN]; @@ -568,6 +758,8 @@ struct wpa_supplicant { u8 p2p_auth_invite[ETH_ALEN]; int p2p_sd_over_ctrl_iface; int p2p_in_provisioning; + int p2p_in_invitation; + int p2p_invite_go_freq; int pending_invite_ssid_id; int show_group_started; u8 go_dev_addr[ETH_ALEN]; @@ -575,12 +767,14 @@ struct wpa_supplicant { u8 pending_join_iface_addr[ETH_ALEN]; u8 pending_join_dev_addr[ETH_ALEN]; int pending_join_wps_method; + u8 p2p_join_ssid[32]; + size_t p2p_join_ssid_len; int p2p_join_scan_count; int auto_pd_scan_retry; int force_long_sd; u16 pending_pd_config_methods; enum { - NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN + NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN, AUTO_PD_ASP } pending_pd_use; /* @@ -604,19 +798,42 @@ struct wpa_supplicant { */ char cross_connect_uplink[100]; - unsigned int sta_scan_pending:1; unsigned int p2p_auto_join:1; unsigned int p2p_auto_pd:1; unsigned int p2p_persistent_group:1; unsigned int p2p_fallback_to_go_neg:1; unsigned int p2p_pd_before_go_neg:1; unsigned int p2p_go_ht40:1; + unsigned int p2p_go_vht:1; unsigned int user_initiated_pd:1; + unsigned int p2p_go_group_formation_completed:1; + unsigned int group_formation_reported:1; + unsigned int waiting_presence_resp; + int p2p_first_connection_timeout; + unsigned int p2p_nfc_tag_enabled:1; + unsigned int p2p_peer_oob_pk_hash_known:1; + unsigned int p2p_disable_ip_addr_req:1; + unsigned int p2ps_join_addr_valid:1; int p2p_persistent_go_freq; int p2p_persistent_id; int p2p_go_intent; int p2p_connect_freq; - struct os_time p2p_auto_started; + struct os_reltime p2p_auto_started; + struct wpa_ssid *p2p_last_4way_hs_fail; + struct wpa_radio_work *p2p_scan_work; + struct wpa_radio_work *p2p_listen_work; + struct wpa_radio_work *p2p_send_action_work; + + u16 p2p_oob_dev_pw_id; /* OOB Device Password Id for group formation */ + struct wpabuf *p2p_oob_dev_pw; /* OOB Device Password for group + * formation */ + u8 p2p_peer_oob_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN]; + u8 p2p_ip_addr_info[3 * 4]; + + /* group common frequencies */ + int *p2p_group_common_freqs; + unsigned int p2p_group_common_freqs_num; + u8 p2ps_join_addr[ETH_ALEN]; #endif /* CONFIG_P2P */ struct wpa_ssid *bgscan_ssid; @@ -636,7 +853,6 @@ struct wpa_supplicant { int after_wps; int known_wps_freq; unsigned int wps_freq; - u16 wps_ap_channel; int wps_fragment_size; int auto_reconnect_disabled; @@ -652,7 +868,18 @@ struct wpa_supplicant { unsigned int network_select:1; unsigned int auto_select:1; unsigned int auto_network_select:1; + unsigned int interworking_fast_assoc_tried:1; unsigned int fetch_all_anqp:1; + unsigned int fetch_osu_info:1; + unsigned int fetch_osu_waiting_scan:1; + unsigned int fetch_osu_icon_in_progress:1; + struct wpa_bss *interworking_gas_bss; + unsigned int osu_icon_id; + struct osu_provider *osu_prov; + size_t osu_prov_count; + struct os_reltime osu_icon_fetch_start; + unsigned int num_osu_scans; + unsigned int num_prov_found; #endif /* CONFIG_INTERWORKING */ unsigned int drv_capa_known; @@ -661,19 +888,87 @@ struct wpa_supplicant { u16 num_modes; u16 flags; } hw; + enum local_hw_capab { + CAPAB_NO_HT_VHT, + CAPAB_HT, + CAPAB_HT40, + CAPAB_VHT, + } hw_capab; +#ifdef CONFIG_MACSEC + struct ieee802_1x_kay *kay; +#endif /* CONFIG_MACSEC */ int pno; + int pno_sched_pending; /* WLAN_REASON_* reason codes. Negative if locally generated. */ int disconnect_reason; struct ext_password_data *ext_pw; - struct wpabuf *last_gas_resp; - u8 last_gas_addr[ETH_ALEN]; - u8 last_gas_dialog_token; + struct wpabuf *last_gas_resp, *prev_gas_resp; + u8 last_gas_addr[ETH_ALEN], prev_gas_addr[ETH_ALEN]; + u8 last_gas_dialog_token, prev_gas_dialog_token; unsigned int no_keep_alive:1; + unsigned int ext_mgmt_frame_handling:1; + unsigned int ext_eapol_frame_io:1; + unsigned int wmm_ac_supported:1; + unsigned int ext_work_in_progress:1; + unsigned int own_disconnect_req:1; + +#define MAC_ADDR_RAND_SCAN BIT(0) +#define MAC_ADDR_RAND_SCHED_SCAN BIT(1) +#define MAC_ADDR_RAND_PNO BIT(2) +#define MAC_ADDR_RAND_ALL (MAC_ADDR_RAND_SCAN | \ + MAC_ADDR_RAND_SCHED_SCAN | \ + MAC_ADDR_RAND_PNO) + unsigned int mac_addr_rand_supported; + unsigned int mac_addr_rand_enable; + + /* MAC Address followed by mask (2 * ETH_ALEN) */ + u8 *mac_addr_scan; + u8 *mac_addr_sched_scan; + u8 *mac_addr_pno; + +#ifdef CONFIG_WNM + u8 wnm_dialog_token; + u8 wnm_reply; + u8 wnm_num_neighbor_report; + u8 wnm_mode; + u16 wnm_dissoc_timer; + u8 wnm_bss_termination_duration[12]; + struct neighbor_report *wnm_neighbor_report_elements; + struct os_reltime wnm_cand_valid_until; + u8 wnm_cand_from_bss[ETH_ALEN]; +#endif /* CONFIG_WNM */ + +#ifdef CONFIG_TESTING_GET_GTK + u8 last_gtk[32]; + size_t last_gtk_len; +#endif /* CONFIG_TESTING_GET_GTK */ + + unsigned int num_multichan_concurrent; + struct wpa_radio_work *connect_work; + + unsigned int ext_work_id; + + struct wpabuf *vendor_elem[NUM_VENDOR_ELEM_FRAMES]; + +#ifdef CONFIG_TESTING_OPTIONS + struct l2_packet_data *l2_test; + unsigned int extra_roc_dur; + enum wpa_supplicant_test_failure test_failure; +#endif /* CONFIG_TESTING_OPTIONS */ + + struct wmm_ac_assoc_data *wmm_ac_assoc_info; + struct wmm_tspec_element *tspecs[WMM_AC_NUM][TS_DIR_IDX_COUNT]; + struct wmm_ac_addts_request *addts_request; + u8 wmm_ac_last_dialog_token; + struct wmm_tspec_element *last_tspecs; + u8 last_tspecs_count; + + struct rrm_data rrm; }; @@ -681,8 +976,13 @@ struct wpa_supplicant { void wpa_supplicant_apply_ht_overrides( struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_driver_associate_params *params); +void wpa_supplicant_apply_vht_overrides( + struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + struct wpa_driver_associate_params *params); int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s); @@ -716,6 +1016,9 @@ void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +int wpas_set_pkcs11_engine_and_module_path(struct wpa_supplicant *wpa_s, + const char *pkcs11_engine_path, + const char *pkcs11_module_path); 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, @@ -732,7 +1035,8 @@ void free_hw_features(struct wpa_supplicant *wpa_s); void wpa_show_license(void); struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global, - struct wpa_interface *iface); + struct wpa_interface *iface, + struct wpa_supplicant *parent); int wpa_supplicant_remove_iface(struct wpa_global *global, struct wpa_supplicant *wpa_s, int terminate); @@ -747,21 +1051,35 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s, void wpa_supplicant_terminate_proc(struct wpa_global *global); 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); int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s); -void wpas_auth_failed(struct wpa_supplicant *wpa_s); +void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason); void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int clear_failures); int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid); int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid, size_t ssid_len); void wpas_request_connection(struct wpa_supplicant *wpa_s); -int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf); +int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen); +int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style); +int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s); +void add_freq(int *freqs, int *num_freqs, int freq); + +void wpas_rrm_reset(struct wpa_supplicant *wpa_s); +void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s, + const u8 *report, size_t report_len); +int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid, + void (*cb)(void *ctx, + struct wpabuf *neighbor_rep), + void *cb_ctx); +void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, + const u8 *src, + const u8 *frame, size_t len, + int rssi); /** * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response @@ -778,6 +1096,10 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, const char *field, const char *value); +void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, + const struct wpa_ssid *ssid, + struct hostapd_freq_params *freq); + /* events.c */ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s); int wpa_supplicant_connect(struct wpa_supplicant *wpa_s, @@ -786,7 +1108,9 @@ int wpa_supplicant_connect(struct wpa_supplicant *wpa_s, void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx); void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx, void *sock_ctx); void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s); -int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s); +int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s); +struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid **selected_ssid); /* eap_register.c */ int eap_register_methods(void); @@ -802,7 +1126,18 @@ static inline int network_is_persistent_group(struct wpa_ssid *ssid) } int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); +int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); int wpas_init_ext_pw(struct wpa_supplicant *wpa_s); +void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title, + struct wpa_used_freq_data *freqs_data, + unsigned int len); + +int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s, + struct wpa_used_freq_data *freqs_data, + unsigned int len); +int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, + int *freq_array, unsigned int len); + #endif /* WPA_SUPPLICANT_I_H */ diff --git a/wpa_supplicant/wpa_supplicant_template.conf b/wpa_supplicant/wpa_supplicant_template.conf index a08eb33d71e75..f3f2a6417d2e7 100644 --- a/wpa_supplicant/wpa_supplicant_template.conf +++ b/wpa_supplicant/wpa_supplicant_template.conf @@ -1,6 +1,6 @@ ##### wpa_supplicant configuration file template ##### update_config=1 -ctrl_interface=wlan0 eapol_version=1 ap_scan=1 fast_reauth=1 +pmf=1 diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index 6f69ddb42291b..1bb82ba716960 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - Glue code to setup EAPOL and RSN modules - * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -26,6 +26,7 @@ #include "bss.h" #include "scan.h" #include "notify.h" +#include "wpas_kay.h" #ifndef CONFIG_NO_CONFIG_BLOBS @@ -95,11 +96,26 @@ static u8 * wpa_alloc_eapol(const struct wpa_supplicant *wpa_s, u8 type, static int wpa_ether_send(struct wpa_supplicant *wpa_s, const u8 *dest, u16 proto, const u8 *buf, size_t len) { +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->ext_eapol_frame_io && proto == ETH_P_EAPOL) { + size_t hex_len = 2 * len + 1; + char *hex = os_malloc(hex_len); + + if (hex == NULL) + return -1; + wpa_snprintf_hex(hex, hex_len, buf, len); + wpa_msg(wpa_s, MSG_INFO, "EAPOL-TX " MACSTR " %s", + MAC2STR(dest), hex); + os_free(hex); + return 0; + } +#endif /* CONFIG_TESTING_OPTIONS */ + if (wpa_s->l2) { return l2_packet_send(wpa_s->l2, dest, proto, buf, len); } - return wpa_drv_send_eapol(wpa_s, dest, proto, buf, len); + return -1; } #endif /* IEEE8021X_EAPOL || !CONFIG_NO_WPA */ @@ -141,11 +157,29 @@ static int wpa_supplicant_eapol_send(void *ctx, int type, const u8 *buf, if (pmksa_cache_get_current(wpa_s->wpa) && type == IEEE802_1X_TYPE_EAPOL_START) { - /* Trying to use PMKSA caching - do not send EAPOL-Start frames - * since they will trigger full EAPOL authentication. */ - wpa_printf(MSG_DEBUG, "RSN: PMKSA caching - do not send " - "EAPOL-Start"); - return -1; + /* + * We were trying to use PMKSA caching and sending EAPOL-Start + * would abort that and trigger full EAPOL authentication. + * However, we've already waited for the AP/Authenticator to + * start 4-way handshake or EAP authentication, and apparently + * it has not done so since the startWhen timer has reached zero + * to get the state machine sending EAPOL-Start. This is not + * really supposed to happen, but an interoperability issue with + * a deployed AP has been identified where the connection fails + * due to that AP failing to operate correctly if PMKID is + * included in the Association Request frame. To work around + * this, assume PMKSA caching failed and try to initiate full + * EAP authentication. + */ + if (!wpa_s->current_ssid || + wpa_s->current_ssid->eap_workaround) { + wpa_printf(MSG_DEBUG, + "RSN: Timeout on waiting for the AP to initiate 4-way handshake for PMKSA caching or EAP authentication - try to force it to start EAP authentication"); + } else { + wpa_printf(MSG_DEBUG, + "RSN: PMKSA caching - do not send EAPOL-Start"); + return -1; + } } if (is_zero_ether_addr(wpa_s->bssid)) { @@ -216,29 +250,50 @@ static void wpa_supplicant_aborted_cached(void *ctx) } -static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol, int success, +static const char * result_str(enum eapol_supp_result result) +{ + switch (result) { + case EAPOL_SUPP_RESULT_FAILURE: + return "FAILURE"; + case EAPOL_SUPP_RESULT_SUCCESS: + return "SUCCESS"; + case EAPOL_SUPP_RESULT_EXPECTED_FAILURE: + return "EXPECTED_FAILURE"; + } + return "?"; +} + + +static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol, + enum eapol_supp_result result, void *ctx) { struct wpa_supplicant *wpa_s = ctx; int res, pmk_len; u8 pmk[PMK_LEN]; - wpa_printf(MSG_DEBUG, "EAPOL authentication completed %ssuccessfully", - success ? "" : "un"); + wpa_printf(MSG_DEBUG, "EAPOL authentication completed - result=%s", + result_str(result)); if (wpas_wps_eapol_cb(wpa_s) > 0) return; - if (!success) { + wpa_s->eap_expected_failure = result == + EAPOL_SUPP_RESULT_EXPECTED_FAILURE; + + if (result != EAPOL_SUPP_RESULT_SUCCESS) { /* * Make sure we do not get stuck here waiting for long EAPOL * timeout if the AP does not disconnect in case of * authentication failure. */ wpa_supplicant_req_auth_timeout(wpa_s, 2, 0); + } else { + ieee802_1x_notify_create_actor(wpa_s, wpa_s->last_eapol_src); } - if (!success || !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) + if (result != EAPOL_SUPP_RESULT_SUCCESS || + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) return; if (!wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) @@ -437,6 +492,13 @@ static int wpa_supplicant_set_key(void *_wpa_s, enum wpa_alg alg, /* Clear the MIC error counter when setting a new PTK. */ wpa_s->mic_errors_seen = 0; } +#ifdef CONFIG_TESTING_GET_GTK + if (key_idx > 0 && addr && is_broadcast_ether_addr(addr) && + alg != WPA_ALG_NONE && key_len <= sizeof(wpa_s->last_gtk)) { + os_memcpy(wpa_s->last_gtk, key, key_len); + wpa_s->last_gtk_len = key_len; + } +#endif /* CONFIG_TESTING_GET_GTK */ return wpa_drv_set_key(wpa_s, alg, addr, key_idx, set_tx, seq, seq_len, key, key_len); } @@ -481,7 +543,44 @@ static int wpa_supplicant_send_ft_action(void *ctx, u8 action, const u8 *ies, size_t ies_len) { struct wpa_supplicant *wpa_s = ctx; - return wpa_drv_send_ft_action(wpa_s, action, target_ap, ies, ies_len); + int ret; + u8 *data, *pos; + size_t data_len; + + if (action != 1) { + wpa_printf(MSG_ERROR, "Unsupported send_ft_action action %d", + action); + return -1; + } + + /* + * Action frame payload: + * Category[1] = 6 (Fast BSS Transition) + * Action[1] = 1 (Fast BSS Transition Request) + * STA Address + * Target AP Address + * FT IEs + */ + + data_len = 2 + 2 * ETH_ALEN + ies_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + *pos++ = 0x06; /* FT Action category */ + *pos++ = action; + os_memcpy(pos, wpa_s->own_addr, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, target_ap, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, ies, ies_len); + + ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, + wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid, + data, data_len, 0); + os_free(data); + + return ret; } @@ -506,18 +605,18 @@ static int wpa_supplicant_mark_authenticated(void *ctx, const u8 *target_ap) } #endif /* CONFIG_IEEE80211R */ -#endif /* CONFIG_NO_WPA */ - #ifdef CONFIG_TDLS static int wpa_supplicant_tdls_get_capa(void *ctx, int *tdls_supported, - int *tdls_ext_setup) + int *tdls_ext_setup, + int *tdls_chan_switch) { struct wpa_supplicant *wpa_s = ctx; *tdls_supported = 0; *tdls_ext_setup = 0; + *tdls_chan_switch = 0; if (!wpa_s->drv_capa_known) return -1; @@ -528,18 +627,23 @@ static int wpa_supplicant_tdls_get_capa(void *ctx, int *tdls_supported, if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP) *tdls_ext_setup = 1; + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH) + *tdls_chan_switch = 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, + u16 status_code, u32 peer_capab, + int initiator, 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); + status_code, peer_capab, initiator, buf, + len); } @@ -551,27 +655,71 @@ static int wpa_supplicant_tdls_oper(void *ctx, int oper, const u8 *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) + void *ctx, const u8 *peer, int add, u16 aid, u16 capability, + const u8 *supp_rates, size_t supp_rates_len, + const struct ieee80211_ht_capabilities *ht_capab, + const struct ieee80211_vht_capabilities *vht_capab, + u8 qosinfo, int wmm, const u8 *ext_capab, size_t ext_capab_len, + const u8 *supp_channels, size_t supp_channels_len, + const u8 *supp_oper_classes, size_t supp_oper_classes_len) { struct wpa_supplicant *wpa_s = ctx; struct hostapd_sta_add_params params; + os_memset(¶ms, 0, sizeof(params)); + params.addr = peer; - params.aid = 1; + params.aid = aid; params.capability = capability; params.flags = WPA_STA_TDLS_PEER | WPA_STA_AUTHORIZED; - params.ht_capabilities = NULL; + + /* + * Don't rely only on qosinfo for WMM capability. It may be 0 even when + * present. Allow the WMM IE to also indicate QoS support. + */ + if (wmm || qosinfo) + params.flags |= WPA_STA_WMM; + + params.ht_capabilities = ht_capab; + params.vht_capabilities = vht_capab; + params.qosinfo = qosinfo; params.listen_interval = 0; params.supp_rates = supp_rates; params.supp_rates_len = supp_rates_len; params.set = !add; + params.ext_capab = ext_capab; + params.ext_capab_len = ext_capab_len; + params.supp_channels = supp_channels; + params.supp_channels_len = supp_channels_len; + params.supp_oper_classes = supp_oper_classes; + params.supp_oper_classes_len = supp_oper_classes_len; return wpa_drv_sta_add(wpa_s, ¶ms); } + +static int wpa_supplicant_tdls_enable_channel_switch( + void *ctx, const u8 *addr, u8 oper_class, + const struct hostapd_freq_params *params) +{ + struct wpa_supplicant *wpa_s = ctx; + + return wpa_drv_tdls_enable_channel_switch(wpa_s, addr, oper_class, + params); +} + + +static int wpa_supplicant_tdls_disable_channel_switch(void *ctx, const u8 *addr) +{ + struct wpa_supplicant *wpa_s = ctx; + + return wpa_drv_tdls_disable_channel_switch(wpa_s, addr); +} + #endif /* CONFIG_TDLS */ +#endif /* CONFIG_NO_WPA */ + enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field) { @@ -587,6 +735,8 @@ enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field) return WPA_CTRL_REQ_EAP_OTP; else if (os_strcmp(field, "PASSPHRASE") == 0) return WPA_CTRL_REQ_EAP_PASSPHRASE; + else if (os_strcmp(field, "SIM") == 0) + return WPA_CTRL_REQ_SIM; return WPA_CTRL_REQ_UNKNOWN; } @@ -623,6 +773,9 @@ const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field, *txt = "Private key passphrase"; ret = "PASSPHRASE"; break; + case WPA_CTRL_REQ_SIM: + ret = "SIM"; + break; default: break; } @@ -662,6 +815,8 @@ static void wpa_supplicant_eap_param_needed(void *ctx, return; } + wpas_notify_eap_status(wpa_s, "eap parameter needed", field_name); + buflen = 100 + os_strlen(txt) + ssid->ssid_len; buf = os_malloc(buflen); if (buf == NULL) @@ -669,7 +824,7 @@ static void wpa_supplicant_eap_param_needed(void *ctx, len = os_snprintf(buf, buflen, WPA_CTRL_REQ "%s-%d:%s needed for SSID ", field_name, ssid->id, txt); - if (len < 0 || (size_t) len >= buflen) { + if (os_snprintf_error(buflen, len)) { os_free(buf); return; } @@ -687,6 +842,25 @@ static void wpa_supplicant_eap_param_needed(void *ctx, #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ +#ifdef CONFIG_EAP_PROXY +static void wpa_supplicant_eap_proxy_cb(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + size_t len; + + wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, + wpa_s->imsi, &len); + if (wpa_s->mnc_len > 0) { + wpa_s->imsi[len] = '\0'; + wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)", + wpa_s->imsi, wpa_s->mnc_len); + } else { + wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available"); + } +} +#endif /* CONFIG_EAP_PROXY */ + + static void wpa_supplicant_port_cb(void *ctx, int authorized) { struct wpa_supplicant *wpa_s = ctx; @@ -705,12 +879,14 @@ static void wpa_supplicant_port_cb(void *ctx, int authorized) static void wpa_supplicant_cert_cb(void *ctx, int depth, const char *subject, + const char *altsubject[], int num_altsubject, const char *cert_hash, const struct wpabuf *cert) { struct wpa_supplicant *wpa_s = ctx; - wpas_notify_certification(wpa_s, depth, subject, cert_hash, cert); + wpas_notify_certification(wpa_s, depth, subject, altsubject, + num_altsubject, cert_hash, cert); } @@ -779,17 +955,24 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s) ctx->eapol_done_cb = wpa_supplicant_notify_eapol_done; ctx->eapol_send = wpa_supplicant_eapol_send; ctx->set_wep_key = wpa_eapol_set_wep_key; +#ifndef CONFIG_NO_CONFIG_BLOBS ctx->set_config_blob = wpa_supplicant_set_config_blob; ctx->get_config_blob = wpa_supplicant_get_config_blob; +#endif /* CONFIG_NO_CONFIG_BLOBS */ ctx->aborted_cached = wpa_supplicant_aborted_cached; 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->openssl_ciphers = wpa_s->conf->openssl_ciphers; ctx->wps = wpa_s->wps; ctx->eap_param_needed = wpa_supplicant_eap_param_needed; +#ifdef CONFIG_EAP_PROXY + ctx->eap_proxy_cb = wpa_supplicant_eap_proxy_cb; +#endif /* CONFIG_EAP_PROXY */ ctx->port_cb = wpa_supplicant_port_cb; ctx->cb = wpa_supplicant_eapol_cb; ctx->cert_cb = wpa_supplicant_cert_cb; + ctx->cert_in_cb = wpa_s->conf->cert_in_cb; ctx->status_cb = wpa_supplicant_status_cb; ctx->set_anon_id = wpa_supplicant_set_anon_id; ctx->cb_ctx = wpa_s; @@ -807,17 +990,31 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s) #ifndef CONFIG_NO_WPA -static void wpa_supplicant_set_rekey_offload(void *ctx, const u8 *kek, - const u8 *kck, +static void wpa_supplicant_set_rekey_offload(void *ctx, + const u8 *kek, size_t kek_len, + const u8 *kck, size_t kck_len, const u8 *replay_ctr) { struct wpa_supplicant *wpa_s = ctx; - wpa_drv_set_rekey_info(wpa_s, kek, kck, replay_ctr); + wpa_drv_set_rekey_info(wpa_s, kek, kek_len, kck, kck_len, replay_ctr); } #endif /* CONFIG_NO_WPA */ +static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk, + size_t pmk_len) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (wpa_s->conf->key_mgmt_offload) + return wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0, + NULL, 0, pmk, pmk_len); + else + return 0; +} + + int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s) { #ifndef CONFIG_NO_WPA @@ -857,13 +1054,19 @@ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s) 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; + ctx->tdls_enable_channel_switch = + wpa_supplicant_tdls_enable_channel_switch; + ctx->tdls_disable_channel_switch = + wpa_supplicant_tdls_disable_channel_switch; #endif /* CONFIG_TDLS */ ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload; + ctx->key_mgmt_set_pmk = wpa_supplicant_key_mgmt_set_pmk; wpa_s->wpa = wpa_sm_init(ctx); if (wpa_s->wpa == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize WPA state " "machine"); + os_free(ctx); return -1; } #endif /* CONFIG_NO_WPA */ @@ -890,6 +1093,22 @@ void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s, conf.ssid = ssid->ssid; conf.ssid_len = ssid->ssid_len; conf.wpa_ptk_rekey = ssid->wpa_ptk_rekey; +#ifdef CONFIG_P2P + if (ssid->p2p_group && wpa_s->current_bss && + !wpa_s->p2p_disable_ip_addr_req) { + struct wpabuf *p2p; + p2p = wpa_bss_get_vendor_ie_multi(wpa_s->current_bss, + P2P_IE_VENDOR_TYPE); + if (p2p) { + u8 group_capab; + group_capab = p2p_get_group_capab(p2p); + if (group_capab & + P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION) + conf.p2p = 1; + wpabuf_free(p2p); + } + } +#endif /* CONFIG_P2P */ } wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL); } diff --git a/wpa_supplicant/wpas_kay.c b/wpa_supplicant/wpas_kay.c new file mode 100644 index 0000000000000..354decf98c8b7 --- /dev/null +++ b/wpa_supplicant/wpas_kay.c @@ -0,0 +1,378 @@ +/* + * IEEE 802.1X-2010 KaY Interface + * Copyright (c) 2013-2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ +#include <openssl/ssl.h> +#include "utils/includes.h" + +#include "utils/common.h" +#include "eap_peer/eap.h" +#include "eap_peer/eap_i.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "pae/ieee802_1x_key.h" +#include "pae/ieee802_1x_kay.h" +#include "wpa_supplicant_i.h" +#include "config.h" +#include "config_ssid.h" +#include "driver_i.h" +#include "wpas_kay.h" + + +#define DEFAULT_KEY_LEN 16 +/* secure Connectivity Association Key Name (CKN) */ +#define DEFAULT_CKN_LEN 16 + + +static int wpas_macsec_init(void *priv, struct macsec_init_params *params) +{ + return wpa_drv_macsec_init(priv, params); +} + + +static int wpas_macsec_deinit(void *priv) +{ + return wpa_drv_macsec_deinit(priv); +} + + +static int wpas_enable_protect_frames(void *wpa_s, Boolean enabled) +{ + return wpa_drv_enable_protect_frames(wpa_s, enabled); +} + + +static int wpas_set_replay_protect(void *wpa_s, Boolean enabled, u32 window) +{ + return wpa_drv_set_replay_protect(wpa_s, enabled, window); +} + + +static int wpas_set_current_cipher_suite(void *wpa_s, const u8 *cs, + size_t cs_len) +{ + return wpa_drv_set_current_cipher_suite(wpa_s, cs, cs_len); +} + + +static int wpas_enable_controlled_port(void *wpa_s, Boolean enabled) +{ + return wpa_drv_enable_controlled_port(wpa_s, enabled); +} + + +static int wpas_get_receive_lowest_pn(void *wpa_s, u32 channel, + u8 an, u32 *lowest_pn) +{ + return wpa_drv_get_receive_lowest_pn(wpa_s, channel, an, lowest_pn); +} + + +static int wpas_get_transmit_next_pn(void *wpa_s, u32 channel, + u8 an, u32 *next_pn) +{ + return wpa_drv_get_transmit_next_pn(wpa_s, channel, an, next_pn); +} + + +static int wpas_set_transmit_next_pn(void *wpa_s, u32 channel, + u8 an, u32 next_pn) +{ + return wpa_drv_set_transmit_next_pn(wpa_s, channel, an, next_pn); +} + + +static int wpas_get_available_receive_sc(void *wpa_s, u32 *channel) +{ + return wpa_drv_get_available_receive_sc(wpa_s, channel); +} + + +static unsigned int conf_offset_val(enum confidentiality_offset co) +{ + switch (co) { + case CONFIDENTIALITY_OFFSET_30: + return 30; + break; + case CONFIDENTIALITY_OFFSET_50: + return 50; + default: + return 0; + } +} + + +static int wpas_create_receive_sc(void *wpa_s, u32 channel, + struct ieee802_1x_mka_sci *sci, + enum validate_frames vf, + enum confidentiality_offset co) +{ + return wpa_drv_create_receive_sc(wpa_s, channel, sci->addr, sci->port, + conf_offset_val(co), vf); +} + + +static int wpas_delete_receive_sc(void *wpa_s, u32 channel) +{ + return wpa_drv_delete_receive_sc(wpa_s, channel); +} + + +static int wpas_create_receive_sa(void *wpa_s, u32 channel, u8 an, + u32 lowest_pn, const u8 *sak) +{ + return wpa_drv_create_receive_sa(wpa_s, channel, an, lowest_pn, sak); +} + + +static int wpas_enable_receive_sa(void *wpa_s, u32 channel, u8 an) +{ + return wpa_drv_enable_receive_sa(wpa_s, channel, an); +} + + +static int wpas_disable_receive_sa(void *wpa_s, u32 channel, u8 an) +{ + return wpa_drv_disable_receive_sa(wpa_s, channel, an); +} + + +static int wpas_get_available_transmit_sc(void *wpa_s, u32 *channel) +{ + return wpa_drv_get_available_transmit_sc(wpa_s, channel); +} + + +static int +wpas_create_transmit_sc(void *wpa_s, u32 channel, + const struct ieee802_1x_mka_sci *sci, + enum confidentiality_offset co) +{ + return wpa_drv_create_transmit_sc(wpa_s, channel, sci->addr, sci->port, + conf_offset_val(co)); +} + + +static int wpas_delete_transmit_sc(void *wpa_s, u32 channel) +{ + return wpa_drv_delete_transmit_sc(wpa_s, channel); +} + + +static int wpas_create_transmit_sa(void *wpa_s, u32 channel, u8 an, + u32 next_pn, Boolean confidentiality, + const u8 *sak) +{ + return wpa_drv_create_transmit_sa(wpa_s, channel, an, next_pn, + confidentiality, sak); +} + + +static int wpas_enable_transmit_sa(void *wpa_s, u32 channel, u8 an) +{ + return wpa_drv_enable_transmit_sa(wpa_s, channel, an); +} + + +static int wpas_disable_transmit_sa(void *wpa_s, u32 channel, u8 an) +{ + return wpa_drv_disable_transmit_sa(wpa_s, channel, an); +} + + +int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) +{ + struct ieee802_1x_kay_ctx *kay_ctx; + struct ieee802_1x_kay *res = NULL; + enum macsec_policy policy; + + ieee802_1x_dealloc_kay_sm(wpa_s); + + if (!ssid || ssid->macsec_policy == 0) + return 0; + + policy = ssid->macsec_policy == 1 ? SHOULD_SECURE : DO_NOT_SECURE; + + kay_ctx = os_zalloc(sizeof(*kay_ctx)); + if (!kay_ctx) + return -1; + + kay_ctx->ctx = wpa_s; + + kay_ctx->macsec_init = wpas_macsec_init; + kay_ctx->macsec_deinit = wpas_macsec_deinit; + kay_ctx->enable_protect_frames = wpas_enable_protect_frames; + kay_ctx->set_replay_protect = wpas_set_replay_protect; + kay_ctx->set_current_cipher_suite = wpas_set_current_cipher_suite; + kay_ctx->enable_controlled_port = wpas_enable_controlled_port; + kay_ctx->get_receive_lowest_pn = wpas_get_receive_lowest_pn; + kay_ctx->get_transmit_next_pn = wpas_get_transmit_next_pn; + kay_ctx->set_transmit_next_pn = wpas_set_transmit_next_pn; + kay_ctx->get_available_receive_sc = wpas_get_available_receive_sc; + kay_ctx->create_receive_sc = wpas_create_receive_sc; + kay_ctx->delete_receive_sc = wpas_delete_receive_sc; + kay_ctx->create_receive_sa = wpas_create_receive_sa; + kay_ctx->enable_receive_sa = wpas_enable_receive_sa; + kay_ctx->disable_receive_sa = wpas_disable_receive_sa; + kay_ctx->get_available_transmit_sc = wpas_get_available_transmit_sc; + kay_ctx->create_transmit_sc = wpas_create_transmit_sc; + kay_ctx->delete_transmit_sc = wpas_delete_transmit_sc; + kay_ctx->create_transmit_sa = wpas_create_transmit_sa; + kay_ctx->enable_transmit_sa = wpas_enable_transmit_sa; + kay_ctx->disable_transmit_sa = wpas_disable_transmit_sa; + + res = ieee802_1x_kay_init(kay_ctx, policy, wpa_s->ifname, + wpa_s->own_addr); + if (res == NULL) { + os_free(kay_ctx); + return -1; + } + + wpa_s->kay = res; + + return 0; +} + + +void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->kay) + return; + + ieee802_1x_kay_deinit(wpa_s->kay); + wpa_s->kay = NULL; +} + + +static int ieee802_1x_auth_get_session_id(struct wpa_supplicant *wpa_s, + const u8 *addr, u8 *sid, size_t *len) +{ + const u8 *session_id; + size_t id_len, need_len; + + session_id = eapol_sm_get_session_id(wpa_s->eapol, &id_len); + if (session_id == NULL) { + wpa_printf(MSG_DEBUG, + "Failed to get SessionID from EAPOL state machines"); + return -1; + } + + need_len = 1 + 2 * SSL3_RANDOM_SIZE; + if (need_len > id_len) { + wpa_printf(MSG_DEBUG, "EAP Session-Id not long enough"); + return -1; + } + + os_memcpy(sid, session_id, need_len); + *len = need_len; + + return 0; +} + + +static int ieee802_1x_auth_get_msk(struct wpa_supplicant *wpa_s, const u8 *addr, + u8 *msk, size_t *len) +{ + u8 key[EAP_MSK_LEN]; + size_t keylen; + struct eapol_sm *sm; + int res; + + sm = wpa_s->eapol; + if (sm == NULL) + return -1; + + keylen = EAP_MSK_LEN; + res = eapol_sm_get_key(sm, key, keylen); + if (res) { + wpa_printf(MSG_DEBUG, + "Failed to get MSK from EAPOL state machines"); + return -1; + } + + if (keylen > *len) + keylen = *len; + os_memcpy(msk, key, keylen); + *len = keylen; + + return 0; +} + + +void * ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s, + const u8 *peer_addr) +{ + u8 *sid; + size_t sid_len = 128; + struct mka_key_name *ckn; + struct mka_key *cak; + struct mka_key *msk; + void *res = NULL; + + if (!wpa_s->kay || wpa_s->kay->policy == DO_NOT_SECURE) + return NULL; + + wpa_printf(MSG_DEBUG, + "IEEE 802.1X: External notification - Create MKA for " + MACSTR, MAC2STR(peer_addr)); + + msk = os_zalloc(sizeof(*msk)); + sid = os_zalloc(sid_len); + ckn = os_zalloc(sizeof(*ckn)); + cak = os_zalloc(sizeof(*cak)); + if (!msk || !sid || !ckn || !cak) + goto fail; + + msk->len = DEFAULT_KEY_LEN; + if (ieee802_1x_auth_get_msk(wpa_s, wpa_s->bssid, msk->key, &msk->len)) { + wpa_printf(MSG_ERROR, "IEEE 802.1X: Could not get MSK"); + goto fail; + } + + if (ieee802_1x_auth_get_session_id(wpa_s, wpa_s->bssid, sid, &sid_len)) + { + wpa_printf(MSG_ERROR, + "IEEE 802.1X: Could not get EAP Session Id"); + goto fail; + } + + /* Derive CAK from MSK */ + cak->len = DEFAULT_KEY_LEN; + if (ieee802_1x_cak_128bits_aes_cmac(msk->key, wpa_s->own_addr, + peer_addr, cak->key)) { + wpa_printf(MSG_ERROR, + "IEEE 802.1X: Deriving CAK failed"); + goto fail; + } + wpa_hexdump_key(MSG_DEBUG, "Derived CAK", cak->key, cak->len); + + /* Derive CKN from MSK */ + ckn->len = DEFAULT_CKN_LEN; + if (ieee802_1x_ckn_128bits_aes_cmac(msk->key, wpa_s->own_addr, + peer_addr, sid, sid_len, + ckn->name)) { + wpa_printf(MSG_ERROR, + "IEEE 802.1X: Deriving CKN failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "Derived CKN", ckn->name, ckn->len); + + res = ieee802_1x_kay_create_mka(wpa_s->kay, ckn, cak, 0, + EAP_EXCHANGE, FALSE); + +fail: + if (msk) { + os_memset(msk, 0, sizeof(*msk)); + os_free(msk); + } + os_free(sid); + os_free(ckn); + if (cak) { + os_memset(cak, 0, sizeof(*cak)); + os_free(cak); + } + + return res; +} diff --git a/wpa_supplicant/wpas_kay.h b/wpa_supplicant/wpas_kay.h new file mode 100644 index 0000000000000..b7236d0776c44 --- /dev/null +++ b/wpa_supplicant/wpas_kay.h @@ -0,0 +1,41 @@ +/* + * IEEE 802.1X-2010 KaY Interface + * Copyright (c) 2013-2014, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPAS_KAY_H +#define WPAS_KAY_H + +#ifdef CONFIG_MACSEC + +int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +void * ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s, + const u8 *peer_addr); +void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s); + +#else /* CONFIG_MACSEC */ + +static inline int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + return 0; +} + +static inline void * +ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s, + const u8 *peer_addr) +{ + return NULL; +} + +static inline void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s) +{ +} + +#endif /* CONFIG_MACSEC */ + +#endif /* WPAS_KAY_H */ diff --git a/wpa_supplicant/wpas_module_tests.c b/wpa_supplicant/wpas_module_tests.c new file mode 100644 index 0000000000000..6af1678a4dfbb --- /dev/null +++ b/wpa_supplicant/wpas_module_tests.c @@ -0,0 +1,108 @@ +/* + * wpa_supplicant module tests + * Copyright (c) 2014, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "wpa_supplicant_i.h" +#include "blacklist.h" + + +static int wpas_blacklist_module_tests(void) +{ + struct wpa_supplicant wpa_s; + int ret = -1; + + os_memset(&wpa_s, 0, sizeof(wpa_s)); + + wpa_blacklist_clear(&wpa_s); + + if (wpa_blacklist_get(NULL, NULL) != NULL || + wpa_blacklist_get(NULL, (u8 *) "123456") != NULL || + wpa_blacklist_get(&wpa_s, NULL) != NULL || + wpa_blacklist_get(&wpa_s, (u8 *) "123456") != NULL) + goto fail; + + if (wpa_blacklist_add(NULL, NULL) == 0 || + wpa_blacklist_add(NULL, (u8 *) "123456") == 0 || + wpa_blacklist_add(&wpa_s, NULL) == 0) + goto fail; + + if (wpa_blacklist_del(NULL, NULL) == 0 || + wpa_blacklist_del(NULL, (u8 *) "123456") == 0 || + wpa_blacklist_del(&wpa_s, NULL) == 0 || + wpa_blacklist_del(&wpa_s, (u8 *) "123456") == 0) + goto fail; + + if (wpa_blacklist_add(&wpa_s, (u8 *) "111111") < 0 || + wpa_blacklist_add(&wpa_s, (u8 *) "111111") < 0 || + wpa_blacklist_add(&wpa_s, (u8 *) "222222") < 0 || + wpa_blacklist_add(&wpa_s, (u8 *) "333333") < 0 || + wpa_blacklist_add(&wpa_s, (u8 *) "444444") < 0 || + wpa_blacklist_del(&wpa_s, (u8 *) "333333") < 0 || + wpa_blacklist_del(&wpa_s, (u8 *) "xxxxxx") == 0 || + wpa_blacklist_get(&wpa_s, (u8 *) "xxxxxx") != NULL || + wpa_blacklist_get(&wpa_s, (u8 *) "111111") == NULL || + wpa_blacklist_get(&wpa_s, (u8 *) "222222") == NULL || + wpa_blacklist_get(&wpa_s, (u8 *) "444444") == NULL || + wpa_blacklist_del(&wpa_s, (u8 *) "111111") < 0 || + wpa_blacklist_del(&wpa_s, (u8 *) "222222") < 0 || + wpa_blacklist_del(&wpa_s, (u8 *) "444444") < 0 || + wpa_blacklist_add(&wpa_s, (u8 *) "111111") < 0 || + wpa_blacklist_add(&wpa_s, (u8 *) "222222") < 0 || + wpa_blacklist_add(&wpa_s, (u8 *) "333333") < 0) + goto fail; + + ret = 0; +fail: + wpa_blacklist_clear(&wpa_s); + + if (ret) + wpa_printf(MSG_ERROR, "blacklist module test failure"); + + return ret; +} + + +int wpas_module_tests(void) +{ + int ret = 0; + + wpa_printf(MSG_INFO, "wpa_supplicant module tests"); + + if (wpas_blacklist_module_tests() < 0) + ret = -1; + +#ifdef CONFIG_WPS + { + int wps_module_tests(void); + if (wps_module_tests() < 0) + ret = -1; + } +#endif /* CONFIG_WPS */ + + { + int utils_module_tests(void); + if (utils_module_tests() < 0) + ret = -1; + } + + { + int common_module_tests(void); + if (common_module_tests() < 0) + ret = -1; + } + + { + int crypto_module_tests(void); + if (crypto_module_tests() < 0) + ret = -1; + } + + return ret; +} diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index 8ab5f64bd00ed..eabe986541c38 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -1,6 +1,6 @@ /* * wpa_supplicant / WPS integration - * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2014, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -52,8 +52,30 @@ static void wpas_wps_clear_ap_info(struct wpa_supplicant *wpa_s) } +static void wpas_wps_assoc_with_cred(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + int use_fast_assoc = timeout_ctx != NULL; + + wpa_printf(MSG_DEBUG, "WPS: Continuing association after eapol_cb"); + if (!use_fast_assoc || + wpa_supplicant_fast_associate(wpa_s) != 1) + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +static void wpas_wps_assoc_with_cred_cancel(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(wpas_wps_assoc_with_cred, wpa_s, (void *) 0); + eloop_cancel_timeout(wpas_wps_assoc_with_cred, wpa_s, (void *) 1); +} + + int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) { + if (wpas_p2p_wps_eapol_cb(wpa_s) > 0) + return 1; + if (!wpa_s->wps_success && wpa_s->current_ssid && eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) { @@ -84,9 +106,14 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) !(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { int disabled = wpa_s->current_ssid->disabled; unsigned int freq = wpa_s->assoc_freq; + struct wpa_bss *bss; + struct wpa_ssid *ssid = NULL; + int use_fast_assoc = 0; + wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - " "try to associate with the received credential " "(freq=%u)", freq); + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); if (disabled) { @@ -98,7 +125,35 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) wpa_s->wps_freq = freq; wpa_s->normal_scans = 0; wpa_s->reassociate = 1; - wpa_supplicant_req_scan(wpa_s, 0, 0); + + wpa_printf(MSG_DEBUG, "WPS: Checking whether fast association " + "without a new scan can be used"); + bss = wpa_supplicant_pick_network(wpa_s, &ssid); + if (bss) { + struct wpabuf *wps; + struct wps_parse_attr attr; + + wps = wpa_bss_get_vendor_ie_multi(bss, + WPS_IE_VENDOR_TYPE); + if (wps && wps_parse_msg(wps, &attr) == 0 && + attr.wps_state && + *attr.wps_state == WPS_STATE_CONFIGURED) + use_fast_assoc = 1; + wpabuf_free(wps); + } + + /* + * Complete the next step from an eloop timeout to allow pending + * driver events related to the disconnection to be processed + * first. This makes it less likely for disconnection event to + * cause problems with the following connection. + */ + wpa_printf(MSG_DEBUG, "WPS: Continue association from timeout"); + wpas_wps_assoc_with_cred_cancel(wpa_s); + eloop_register_timeout(0, 10000, + wpas_wps_assoc_with_cred, wpa_s, + use_fast_assoc ? (void *) 1 : + (void *) 0); return 1; } @@ -106,6 +161,7 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) wpa_printf(MSG_DEBUG, "WPS: Registration completed - waiting " "for external credential processing"); wpas_clear_wps(wpa_s); + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); return 1; @@ -196,12 +252,107 @@ static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s, } +static void wpas_wps_remove_dup_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid *new_ssid) +{ + struct wpa_ssid *ssid, *next; + + for (ssid = wpa_s->conf->ssid, next = ssid ? ssid->next : NULL; ssid; + ssid = next, next = ssid ? ssid->next : NULL) { + /* + * new_ssid has already been added to the list in + * wpas_wps_add_network(), so skip it. + */ + if (ssid == new_ssid) + continue; + + if (ssid->bssid_set || new_ssid->bssid_set) { + if (ssid->bssid_set != new_ssid->bssid_set) + continue; + if (os_memcmp(ssid->bssid, new_ssid->bssid, ETH_ALEN) != + 0) + continue; + } + + /* compare SSID */ + if (ssid->ssid_len == 0 || ssid->ssid_len != new_ssid->ssid_len) + continue; + + if (ssid->ssid && new_ssid->ssid) { + if (os_memcmp(ssid->ssid, new_ssid->ssid, + ssid->ssid_len) != 0) + continue; + } else if (ssid->ssid || new_ssid->ssid) + continue; + + /* compare security parameters */ + if (ssid->auth_alg != new_ssid->auth_alg || + ssid->key_mgmt != new_ssid->key_mgmt || + (ssid->group_cipher != new_ssid->group_cipher && + !(ssid->group_cipher & new_ssid->group_cipher & + WPA_CIPHER_CCMP))) + continue; + + /* + * Some existing WPS APs will send two creds in case they are + * configured for mixed mode operation (WPA+WPA2 and TKIP+CCMP). + * Try to merge these two creds if they are received in the same + * M8 message. + */ + if (ssid->wps_run && ssid->wps_run == new_ssid->wps_run && + wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) { + if (new_ssid->passphrase && ssid->passphrase && + os_strcmp(new_ssid->passphrase, ssid->passphrase) != + 0) { + wpa_printf(MSG_DEBUG, + "WPS: M8 Creds with different passphrase - do not merge"); + continue; + } + + if (new_ssid->psk_set && + (!ssid->psk_set || + os_memcmp(new_ssid->psk, ssid->psk, 32) != 0)) { + wpa_printf(MSG_DEBUG, + "WPS: M8 Creds with different PSK - do not merge"); + continue; + } + + if ((new_ssid->passphrase && !ssid->passphrase) || + (!new_ssid->passphrase && ssid->passphrase)) { + wpa_printf(MSG_DEBUG, + "WPS: M8 Creds with different passphrase/PSK type - do not merge"); + continue; + } + + wpa_printf(MSG_DEBUG, + "WPS: Workaround - merge likely WPA/WPA2-mixed mode creds in same M8 message"); + new_ssid->proto |= ssid->proto; + new_ssid->pairwise_cipher |= ssid->pairwise_cipher; + } else { + /* + * proto and pairwise_cipher difference matter for + * non-mixed-mode creds. + */ + if (ssid->proto != new_ssid->proto || + ssid->pairwise_cipher != new_ssid->pairwise_cipher) + continue; + } + + /* Remove the duplicated older network entry. */ + wpa_printf(MSG_DEBUG, "Remove duplicate network %d", ssid->id); + wpas_notify_network_removed(wpa_s, ssid); + if (wpa_s->current_ssid == ssid) + wpa_s->current_ssid = NULL; + wpa_config_remove_network(wpa_s->conf, ssid->id); + } +} + + static int wpa_supplicant_wps_cred(void *ctx, const struct wps_credential *cred) { struct wpa_supplicant *wpa_s = 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; @@ -247,7 +398,6 @@ static int wpa_supplicant_wps_cred(void *ctx, } if (auth_type != WPS_AUTH_OPEN && - auth_type != WPS_AUTH_SHARED && auth_type != WPS_AUTH_WPAPSK && auth_type != WPS_AUTH_WPA2PSK) { wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for " @@ -295,10 +445,22 @@ static int wpa_supplicant_wps_cred(void *ctx, ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) return -1; + if (wpa_s->current_ssid) { + /* + * Should the GO issue multiple credentials for some + * reason, each credential should be marked as a + * temporary P2P group similarly to the one that gets + * marked as such based on the pre-configured values + * used for the WPS network block. + */ + ssid->p2p_group = wpa_s->current_ssid->p2p_group; + ssid->temporary = wpa_s->current_ssid->temporary; + } wpas_notify_network_added(wpa_s, ssid); } wpa_config_set_network_defaults(ssid); + ssid->wps_run = wpa_s->wps_run; os_free(ssid->ssid); ssid->ssid = os_malloc(cred->ssid_len); @@ -310,43 +472,16 @@ static int wpa_supplicant_wps_cred(void *ctx, switch (cred->encr_type) { case WPS_ENCR_NONE: break; - case WPS_ENCR_WEP: - if (cred->key_len <= 0) - break; - if (cred->key_len != 5 && cred->key_len != 13 && - cred->key_len != 10 && cred->key_len != 26) { - wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key length " - "%lu", (unsigned long) cred->key_len); - return -1; - } - if (cred->key_idx > NUM_WEP_KEYS) { - wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key index %d", - cred->key_idx); - return -1; - } - if (cred->key_idx) - key_idx = cred->key_idx - 1; - if (cred->key_len == 10 || cred->key_len == 26) { - if (hexstr2bin((char *) cred->key, - ssid->wep_key[key_idx], - cred->key_len / 2) < 0) { - wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key " - "%d", key_idx); - return -1; - } - ssid->wep_key_len[key_idx] = cred->key_len / 2; - } else { - os_memcpy(ssid->wep_key[key_idx], cred->key, - cred->key_len); - ssid->wep_key_len[key_idx] = cred->key_len; - } - ssid->wep_tx_keyidx = key_idx; - break; case WPS_ENCR_TKIP: ssid->pairwise_cipher = WPA_CIPHER_TKIP; break; case WPS_ENCR_AES: ssid->pairwise_cipher = WPA_CIPHER_CCMP; + if (wpa_s->drv_capa_known && + (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP)) { + ssid->pairwise_cipher |= WPA_CIPHER_GCMP; + ssid->group_cipher |= WPA_CIPHER_GCMP; + } break; } @@ -366,11 +501,6 @@ static int wpa_supplicant_wps_cred(void *ctx, } #endif /* CONFIG_WPS_REG_DISABLE_OPEN */ break; - case WPS_AUTH_SHARED: - ssid->auth_alg = WPA_AUTH_ALG_SHARED; - ssid->key_mgmt = WPA_KEY_MGMT_NONE; - ssid->proto = 0; - break; case WPS_AUTH_WPAPSK: ssid->auth_alg = WPA_AUTH_ALG_OPEN; ssid->key_mgmt = WPA_KEY_MGMT_PSK; @@ -412,8 +542,7 @@ static int wpa_supplicant_wps_cred(void *ctx, wpas_wps_security_workaround(wpa_s, ssid, cred); - if (cred->ap_channel) - wpa_s->wps_ap_channel = cred->ap_channel; + wpas_wps_remove_dup_network(wpa_s, ssid); #ifndef CONFIG_NO_CONFIG_WRITE if (wpa_s->conf->update_config && @@ -434,15 +563,6 @@ 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) { @@ -461,18 +581,20 @@ static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s, * 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, + eloop_register_timeout(0, 0, wpas_p2p_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 wpas_wps_clear_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_printf(MSG_DEBUG, "WPS: Clear WPS network from timeout"); + wpas_clear_wps(wpa_s); +} + static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail) @@ -482,13 +604,13 @@ static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s, 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]); + wps_ei_str(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]); + wps_ei_str(fail->error_indication)); } else { wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d", @@ -498,11 +620,16 @@ static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s, "msg=%d config_error=%d", fail->msg, fail->config_error); } - wpas_clear_wps(wpa_s); + + /* + * Need to allow WPS processing to complete, e.g., by sending WSC_NACK. + */ + wpa_printf(MSG_DEBUG, "WPS: Register timeout to clear WPS network"); + eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL); + eloop_register_timeout(0, 100000, wpas_wps_clear_timeout, wpa_s, NULL); + wpas_notify_wps_event_fail(wpa_s, fail); -#ifdef CONFIG_P2P wpas_p2p_wps_failed(wpa_s, fail); -#endif /* CONFIG_P2P */ } @@ -549,6 +676,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); + if (wpa_s->current_ssid) + wpas_clear_temp_disabled(wpa_s, wpa_s->current_ssid, 1); + wpa_s->extra_blacklist_count = 0; /* * Enable the networks disabled during wpas_wps_reassoc after 10 @@ -558,9 +688,7 @@ static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s) eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s, NULL); -#ifdef CONFIG_P2P wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0); -#endif /* CONFIG_P2P */ } @@ -711,6 +839,12 @@ static void wpa_supplicant_wps_event(void *ctx, enum wps_event event, break; case WPS_EV_PBC_TIMEOUT: break; + case WPS_EV_PBC_ACTIVE: + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ACTIVE); + break; + case WPS_EV_PBC_DISABLE: + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_DISABLE); + break; case WPS_EV_ER_AP_ADD: wpa_supplicant_wps_event_er_ap_add(wpa_s, &data->ap); break; @@ -739,6 +873,17 @@ static void wpa_supplicant_wps_event(void *ctx, enum wps_event event, } +static int wpa_supplicant_wps_rf_band(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (!wpa_s->current_ssid || !wpa_s->assoc_freq) + return 0; + + return (wpa_s->assoc_freq > 2484) ? WPS_RF_50GHZ : WPS_RF_24GHZ; +} + + enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid) { if (eap_is_wps_pbc_enrollee(&ssid->eap) || @@ -754,21 +899,25 @@ static void wpas_clear_wps(struct wpa_supplicant *wpa_s) int id; struct wpa_ssid *ssid, *remove_ssid = NULL, *prev_current; + wpa_s->after_wps = 0; + wpa_s->known_wps_freq = 0; + prev_current = wpa_s->current_ssid; /* Enable the networks disabled during wpas_wps_reassoc */ wpas_wps_reenable_networks(wpa_s); eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL); /* Remove any existing WPS network from configuration */ ssid = wpa_s->conf->ssid; while (ssid) { if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { if (ssid == wpa_s->current_ssid) { - wpa_s->current_ssid = NULL; - if (ssid != NULL) - wpas_notify_network_changed(wpa_s); + wpa_s->own_disconnect_req = 1; + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); } id = ssid->id; remove_ssid = ssid; @@ -800,7 +949,8 @@ static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx) static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s, - int registrar, const u8 *bssid) + int registrar, const u8 *dev_addr, + const u8 *bssid) { struct wpa_ssid *ssid; @@ -820,6 +970,11 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s, return NULL; } +#ifdef CONFIG_P2P + if (dev_addr) + os_memcpy(ssid->go_p2p_dev_addr, dev_addr, ETH_ALEN); +#endif /* CONFIG_P2P */ + if (bssid) { #ifndef CONFIG_P2P struct wpa_bss *bss; @@ -865,24 +1020,16 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s, } -static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s, - struct wpa_ssid *selected, const u8 *bssid) +static void wpas_wps_temp_disable(struct wpa_supplicant *wpa_s, + struct wpa_ssid *selected) { struct wpa_ssid *ssid; - struct wpa_bss *bss; - wpa_s->known_wps_freq = 0; - if (bssid) { - bss = wpa_bss_get_bssid(wpa_s, bssid); - if (bss && bss->freq > 0) { - wpa_s->known_wps_freq = 1; - wpa_s->wps_freq = bss->freq; - } - } - - if (wpa_s->current_ssid) + if (wpa_s->current_ssid) { + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); + } /* Mark all other networks disabled and trigger reassociation */ ssid = wpa_s->conf->ssid; @@ -906,12 +1053,41 @@ static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s, } ssid = ssid->next; } +} + + +static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s, + struct wpa_ssid *selected, const u8 *bssid, + int freq) +{ + struct wpa_bss *bss; + + wpa_s->wps_run++; + if (wpa_s->wps_run == 0) + wpa_s->wps_run++; + wpa_s->after_wps = 0; + wpa_s->known_wps_freq = 0; + if (freq) { + wpa_s->after_wps = 5; + wpa_s->wps_freq = freq; + } else if (bssid) { + bss = wpa_bss_get_bssid_latest(wpa_s, bssid); + if (bss && bss->freq > 0) { + wpa_s->known_wps_freq = 1; + wpa_s->wps_freq = bss->freq; + } + } + + wpas_wps_temp_disable(wpa_s, selected); + wpa_s->disconnected = 0; wpa_s->reassociate = 1; wpa_s->scan_runs = 0; wpa_s->normal_scans = 0; wpa_s->wps_success = 0; wpa_s->blacklist_cleared = 0; + + wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); } @@ -920,8 +1096,16 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, int p2p_group) { struct wpa_ssid *ssid; + +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + wpa_printf(MSG_DEBUG, + "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled"); + return -1; + } +#endif /* CONFIG_AP */ wpas_clear_wps(wpa_s); - ssid = wpas_wps_add_network(wpa_s, 0, bssid); + ssid = wpas_wps_add_network(wpa_s, 0, NULL, bssid); if (ssid == NULL) return -1; ssid->temporary = 1; @@ -938,29 +1122,60 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, } } #endif /* CONFIG_P2P */ - wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0); + if (wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0) < 0) + return -1; 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, bssid); + wpas_wps_reassoc(wpa_s, ssid, bssid, 0); return 0; } -int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, - const char *pin, int p2p_group, u16 dev_pw_id) +static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, + const u8 *dev_addr, const u8 *bssid, + const char *pin, int p2p_group, u16 dev_pw_id, + const u8 *peer_pubkey_hash, + const u8 *ssid_val, size_t ssid_len, int freq) { struct wpa_ssid *ssid; - char val[128]; + char val[128 + 2 * WPS_OOB_PUBKEY_HASH_LEN]; unsigned int rpin = 0; + char hash[2 * WPS_OOB_PUBKEY_HASH_LEN + 10]; +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + wpa_printf(MSG_DEBUG, + "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled"); + return -1; + } +#endif /* CONFIG_AP */ wpas_clear_wps(wpa_s); - ssid = wpas_wps_add_network(wpa_s, 0, bssid); - if (ssid == NULL) + if (bssid && is_zero_ether_addr(bssid)) + bssid = NULL; + ssid = wpas_wps_add_network(wpa_s, 0, dev_addr, bssid); + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Could not add network"); return -1; + } ssid->temporary = 1; ssid->p2p_group = p2p_group; + if (ssid_val) { + ssid->ssid = os_malloc(ssid_len); + if (ssid->ssid) { + os_memcpy(ssid->ssid, ssid_val, ssid_len); + ssid->ssid_len = ssid_len; + } + } + if (peer_pubkey_hash) { + os_memcpy(hash, " pkhash=", 8); + wpa_snprintf_hex_uppercase(hash + 8, sizeof(hash) - 8, + peer_pubkey_hash, + WPS_OOB_PUBKEY_HASH_LEN); + } else { + hash[0] = '\0'; + } #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); @@ -974,24 +1189,38 @@ int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, } #endif /* CONFIG_P2P */ if (pin) - os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u\"", - pin, dev_pw_id); - else { + os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u%s\"", + pin, dev_pw_id, hash); + else if (pin == NULL && dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) { + os_snprintf(val, sizeof(val), "\"dev_pw_id=%u%s\"", + dev_pw_id, hash); + } else { rpin = wps_generate_pin(); - os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u\"", - rpin, dev_pw_id); + os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u%s\"", + rpin, dev_pw_id, hash); + } + if (wpa_config_set(ssid, "phase1", val, 0) < 0) { + wpa_printf(MSG_DEBUG, "WPS: Failed to set phase1 '%s'", val); + 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); wpa_s->wps_ap_iter = 1; - wpas_wps_reassoc(wpa_s, ssid, bssid); + wpas_wps_reassoc(wpa_s, ssid, bssid, freq); return rpin; } +int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *pin, int p2p_group, u16 dev_pw_id) +{ + return wpas_wps_start_dev_pw(wpa_s, NULL, bssid, pin, p2p_group, + dev_pw_id, NULL, NULL, 0, 0); +} + + /* Cancel the wps pbc/pin requests */ int wpas_wps_cancel(struct wpa_supplicant *wpa_s) { @@ -1010,14 +1239,20 @@ int wpas_wps_cancel(struct wpa_supplicant *wpa_s) } else if (wpa_s->wpa_state >= WPA_ASSOCIATED) { wpa_printf(MSG_DEBUG, "WPS: Cancel operation - " "deauthenticate"); + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); wpas_clear_wps(wpa_s); } else { wpas_wps_reenable_networks(wpa_s); wpas_wps_clear_ap_info(wpa_s); + if (eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL) > + 0) + wpas_clear_wps(wpa_s); } + wpa_s->after_wps = 0; + return 0; } @@ -1030,17 +1265,24 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, char *pos, *end; int res; +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + wpa_printf(MSG_DEBUG, + "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled"); + return -1; + } +#endif /* CONFIG_AP */ if (!pin) return -1; wpas_clear_wps(wpa_s); - ssid = wpas_wps_add_network(wpa_s, 1, bssid); + ssid = wpas_wps_add_network(wpa_s, 1, NULL, bssid); if (ssid == NULL) return -1; ssid->temporary = 1; pos = val; end = pos + sizeof(val); res = os_snprintf(pos, end - pos, "\"pin=%s", pin); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return -1; pos += res; if (settings) { @@ -1048,28 +1290,38 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, "new_encr=%s new_key=%s", settings->ssid_hex, settings->auth, settings->encr, settings->key_hex); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return -1; pos += res; } res = os_snprintf(pos, end - pos, "\""); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) + return -1; + if (wpa_config_set(ssid, "phase1", val, 0) < 0) 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, bssid); + wpas_wps_reassoc(wpa_s, ssid, bssid, 0); return 0; } -static int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk, +static int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr, + const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len) { - wpa_printf(MSG_DEBUG, "WPS: Received new WPA/WPA2-PSK from WPS for " - "STA " MACSTR, MAC2STR(mac_addr)); + if (is_zero_ether_addr(p2p_dev_addr)) { + wpa_printf(MSG_DEBUG, + "Received new WPA/WPA2-PSK from WPS for STA " MACSTR, + MAC2STR(mac_addr)); + } else { + wpa_printf(MSG_DEBUG, + "Received new WPA/WPA2-PSK from WPS for STA " MACSTR + " P2P Device Addr " MACSTR, + MAC2STR(mac_addr), MAC2STR(p2p_dev_addr)); + } wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len); /* TODO */ @@ -1094,7 +1346,7 @@ static void wpas_wps_pin_needed_cb(void *ctx, const u8 *uuid_e, dev->model_number, dev->serial_number, wps_dev_type_bin2str(dev->pri_dev_type, devtype, sizeof(devtype))); - if (len > 0 && len < (int) sizeof(txt)) + if (!os_snprintf_error(sizeof(txt), len)) wpa_printf(MSG_INFO, "%s", txt); } @@ -1118,7 +1370,6 @@ static void wpas_wps_set_sel_reg_cb(void *ctx, int sel_reg, u16 dev_passwd_id, 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) { @@ -1133,7 +1384,6 @@ static u16 wps_fix_config_methods(u16 config_methods) "virtual_push_button for WPS 2.0 compliance"); config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON; } -#endif /* CONFIG_WPS2 */ return config_methods; } @@ -1142,7 +1392,9 @@ static u16 wps_fix_config_methods(u16 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); + char buf[50]; + const char *src; + if (is_nil_uuid(wpa_s->conf->uuid)) { struct wpa_supplicant *first; first = wpa_s->global->ifaces; @@ -1153,18 +1405,18 @@ static void wpas_wps_set_uuid(struct wpa_supplicant *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); + src = "from the first interface"; } 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); + src = "based on MAC address"; } } 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); + src = "based on configuration"; } + + uuid_bin2str(wps->uuid, buf, sizeof(buf)); + wpa_dbg(wpa_s, MSG_DEBUG, "WPS: UUID %s: %s", src, buf); } @@ -1198,6 +1450,7 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s) wps->cred_cb = wpa_supplicant_wps_cred; wps->event_cb = wpa_supplicant_wps_event; + wps->rf_band_cb = wpa_supplicant_wps_rf_band; wps->cb_ctx = wpa_s; wps->dev.device_name = wpa_s->conf->device_name; @@ -1268,18 +1521,39 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s) } +#ifdef CONFIG_WPS_ER +static void wpas_wps_nfc_clear(struct wps_context *wps) +{ + wps->ap_nfc_dev_pw_id = 0; + wpabuf_free(wps->ap_nfc_dh_pubkey); + wps->ap_nfc_dh_pubkey = NULL; + wpabuf_free(wps->ap_nfc_dh_privkey); + wps->ap_nfc_dh_privkey = NULL; + wpabuf_free(wps->ap_nfc_dev_pw); + wps->ap_nfc_dev_pw = NULL; +} +#endif /* CONFIG_WPS_ER */ + + void wpas_wps_deinit(struct wpa_supplicant *wpa_s) { + wpas_wps_assoc_with_cred_cancel(wpa_s); eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL); wpas_wps_clear_ap_info(wpa_s); +#ifdef CONFIG_P2P + eloop_cancel_timeout(wpas_p2p_pbc_overlap_cb, wpa_s, NULL); +#endif /* CONFIG_P2P */ + if (wpa_s->wps == NULL) return; #ifdef CONFIG_WPS_ER wps_er_deinit(wpa_s->wps_er, NULL, NULL); wpa_s->wps_er = NULL; + wpas_wps_nfc_clear(wpa_s->wps); #endif /* CONFIG_WPS_ER */ wps_registrar_deinit(wpa_s->wps->registrar); @@ -1460,6 +1734,10 @@ int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, uuid = wps_get_uuid_e(ie); wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS", uuid, UUID_LEN); + if (os_memcmp(selected->bssid, bss->bssid, ETH_ALEN) == 0) { + wpabuf_free(ie); + continue; + } if (sel_uuid == NULL || uuid == NULL || os_memcmp(sel_uuid, uuid, UUID_LEN) != 0) { ret = 1; /* PBC overlap */ @@ -1563,13 +1841,12 @@ int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter) } -int wpas_wps_er_stop(struct wpa_supplicant *wpa_s) +void wpas_wps_er_stop(struct wpa_supplicant *wpa_s) { #ifdef CONFIG_WPS_ER wps_er_deinit(wpa_s->wps_er, NULL, NULL); wpa_s->wps_er = NULL; #endif /* CONFIG_WPS_ER */ - return 0; } @@ -1578,91 +1855,131 @@ 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; - - if (os_strcmp(uuid, "any") == 0) - any = 1; - else if (uuid_str2bin(uuid, u)) + const u8 *use_uuid = NULL; + u8 addr_buf[ETH_ALEN]; + + if (os_strcmp(uuid, "any") == 0) { + } else if (uuid_str2bin(uuid, u) == 0) { + use_uuid = u; + } else if (hwaddr_aton(uuid, addr_buf) == 0) { + use_uuid = wps_er_get_sta_uuid(wpa_s->wps_er, addr_buf); + if (use_uuid == NULL) + return -1; + } else return -1; return wps_registrar_add_pin(wpa_s->wps->registrar, addr, - any ? NULL : u, + use_uuid, (const u8 *) pin, os_strlen(pin), 300); } int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid) { - u8 u[UUID_LEN]; + u8 u[UUID_LEN], *use_uuid = NULL; + u8 addr[ETH_ALEN], *use_addr = NULL; - if (uuid_str2bin(uuid, u)) + if (uuid_str2bin(uuid, u) == 0) + use_uuid = u; + else if (hwaddr_aton(uuid, addr) == 0) + use_addr = addr; + else return -1; - return wps_er_pbc(wpa_s->wps_er, u); + return wps_er_pbc(wpa_s->wps_er, use_uuid, use_addr); } int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid, const char *pin) { - u8 u[UUID_LEN]; + u8 u[UUID_LEN], *use_uuid = NULL; + u8 addr[ETH_ALEN], *use_addr = NULL; - if (uuid_str2bin(uuid, u)) + if (uuid_str2bin(uuid, u) == 0) + use_uuid = u; + else if (hwaddr_aton(uuid, addr) == 0) + use_addr = addr; + else return -1; - return wps_er_learn(wpa_s->wps_er, u, (const u8 *) pin, + + return wps_er_learn(wpa_s->wps_er, use_uuid, use_addr, (const u8 *) pin, os_strlen(pin)); } -int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid, - int id) +static int wpas_wps_network_to_cred(struct wpa_ssid *ssid, + struct wps_credential *cred) { - 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)); + 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; + 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) ? + 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; + cred->encr_type = WPS_ENCR_AES; else - cred.encr_type = WPS_ENCR_TKIP; + cred->encr_type = WPS_ENCR_TKIP; if (ssid->passphrase) { - cred.key_len = os_strlen(ssid->passphrase); - if (cred.key_len >= 64) + cred->key_len = os_strlen(ssid->passphrase); + if (cred->key_len >= 64) return -1; - os_memcpy(cred.key, ssid->passphrase, cred.key_len); + 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); + 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; + cred->auth_type = WPS_AUTH_OPEN; + cred->encr_type = WPS_ENCR_NONE; } - return wps_er_set_config(wpa_s->wps_er, u, &cred); + + return 0; +} + + +int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid, + int id) +{ + u8 u[UUID_LEN], *use_uuid = NULL; + u8 addr[ETH_ALEN], *use_addr = NULL; + struct wpa_ssid *ssid; + struct wps_credential cred; + int ret; + + if (uuid_str2bin(uuid, u) == 0) + use_uuid = u; + else if (hwaddr_aton(uuid, addr) == 0) + use_addr = addr; + else + return -1; + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL || ssid->ssid == NULL) + return -1; + + if (wpas_wps_network_to_cred(ssid, &cred) < 0) + return -1; + ret = wps_er_set_config(wpa_s->wps_er, use_uuid, use_addr, &cred); + os_memset(&cred, 0, sizeof(cred)); + return ret; } 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]; + u8 u[UUID_LEN], *use_uuid = NULL; + u8 addr[ETH_ALEN], *use_addr = NULL; struct wps_credential cred; size_t len; - if (uuid_str2bin(uuid, u)) + if (uuid_str2bin(uuid, u) == 0) + use_uuid = u; + else if (hwaddr_aton(uuid, addr) == 0) + use_addr = addr; + else return -1; if (settings->ssid_hex == NULL || settings->auth == NULL || settings->encr == NULL || settings->key_hex == NULL) @@ -1692,8 +2009,10 @@ int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid, if (os_strcmp(settings->encr, "NONE") == 0) cred.encr_type = WPS_ENCR_NONE; +#ifdef CONFIG_TESTING_OPTIONS else if (os_strcmp(settings->encr, "WEP") == 0) cred.encr_type = WPS_ENCR_WEP; +#endif /* CONFIG_TESTING_OPTIONS */ else if (os_strcmp(settings->encr, "TKIP") == 0) cred.encr_type = WPS_ENCR_TKIP; else if (os_strcmp(settings->encr, "CCMP") == 0) @@ -1701,8 +2020,8 @@ int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid, else return -1; - return wps_er_config(wpa_s->wps_er, u, (const u8 *) pin, - os_strlen(pin), &cred); + return wps_er_config(wpa_s->wps_er, use_uuid, use_addr, + (const u8 *) pin, os_strlen(pin), &cred); } @@ -1711,15 +2030,20 @@ struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s, int ndef, const char *uuid) { struct wpabuf *ret; - u8 u[UUID_LEN]; + u8 u[UUID_LEN], *use_uuid = NULL; + u8 addr[ETH_ALEN], *use_addr = NULL; if (!wpa_s->wps_er) return NULL; - if (uuid_str2bin(uuid, u)) + if (uuid_str2bin(uuid, u) == 0) + use_uuid = u; + else if (hwaddr_aton(uuid, addr) == 0) + use_addr = addr; + else return NULL; - ret = wps_er_nfc_config_token(wpa_s->wps_er, u); + ret = wps_er_nfc_config_token(wpa_s->wps_er, use_uuid, use_addr); if (ndef && ret) { struct wpabuf *tmp; tmp = ndef_build_wifi(ret); @@ -1759,19 +2083,6 @@ int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s) } -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; @@ -1827,8 +2138,69 @@ void wpas_wps_update_config(struct wpa_supplicant *wpa_s) #ifdef CONFIG_WPS_NFC +#ifdef CONFIG_WPS_ER +static struct wpabuf * +wpas_wps_network_config_token(struct wpa_supplicant *wpa_s, int ndef, + struct wpa_ssid *ssid) +{ + struct wpabuf *ret; + struct wps_credential cred; + + if (wpas_wps_network_to_cred(ssid, &cred) < 0) + return NULL; + + ret = wps_er_config_token_from_cred(wpa_s->wps, &cred); + + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; +} +#endif /* CONFIG_WPS_ER */ + + +struct wpabuf * wpas_wps_nfc_config_token(struct wpa_supplicant *wpa_s, + int ndef, const char *id_str) +{ +#ifdef CONFIG_WPS_ER + if (id_str) { + int id; + char *end = NULL; + struct wpa_ssid *ssid; + + id = strtol(id_str, &end, 10); + if (end && *end) + return NULL; + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) + return NULL; + return wpas_wps_network_config_token(wpa_s, ndef, ssid); + } +#endif /* CONFIG_WPS_ER */ +#ifdef CONFIG_AP + if (wpa_s->ap_iface) + return wpas_ap_wps_nfc_config_token(wpa_s, ndef); +#endif /* CONFIG_AP */ + return NULL; +} + + struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef) { + if (wpa_s->conf->wps_nfc_pw_from_config) { + return wps_nfc_token_build(ndef, + wpa_s->conf->wps_nfc_dev_pw_id, + wpa_s->conf->wps_nfc_dh_pubkey, + wpa_s->conf->wps_nfc_dev_pw); + } + return wps_nfc_token_gen(ndef, &wpa_s->conf->wps_nfc_dev_pw_id, &wpa_s->conf->wps_nfc_dh_pubkey, &wpa_s->conf->wps_nfc_dh_privkey, @@ -1836,15 +2208,32 @@ struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef) } -int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid) +int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *go_dev_addr, + const u8 *bssid, + const struct wpabuf *dev_pw, u16 dev_pw_id, + int p2p_group, const u8 *peer_pubkey_hash, + const u8 *ssid, size_t ssid_len, int freq) { struct wps_context *wps = wpa_s->wps; char pw[32 * 2 + 1]; + if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) { + dev_pw = wpa_s->conf->wps_nfc_dev_pw; + dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id; + } + if (wpa_s->conf->wps_nfc_dh_pubkey == NULL || - wpa_s->conf->wps_nfc_dh_privkey == NULL || - wpa_s->conf->wps_nfc_dev_pw == NULL) + wpa_s->conf->wps_nfc_dh_privkey == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Missing DH params - " + "cannot start NFC-triggered connection"); return -1; + } + + if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Missing Device Password (id=%u) - " + "cannot start NFC-triggered connection", dev_pw_id); + return -1; + } dh5_free(wps->dh_ctx); wpabuf_free(wps->dh_pubkey); @@ -1857,24 +2246,42 @@ int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid) wps->dh_pubkey = NULL; wpabuf_free(wps->dh_privkey); wps->dh_privkey = NULL; + wpa_printf(MSG_DEBUG, "WPS: Failed to get DH priv/pub key"); return -1; } wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey); - if (wps->dh_ctx == NULL) + if (wps->dh_ctx == NULL) { + wpabuf_free(wps->dh_pubkey); + wps->dh_pubkey = NULL; + wpabuf_free(wps->dh_privkey); + wps->dh_privkey = NULL; + wpa_printf(MSG_DEBUG, "WPS: Failed to initialize DH context"); return -1; + } - wpa_snprintf_hex_uppercase(pw, sizeof(pw), - wpabuf_head(wpa_s->conf->wps_nfc_dev_pw), - wpabuf_len(wpa_s->conf->wps_nfc_dev_pw)); - return wpas_wps_start_pin(wpa_s, bssid, pw, 0, - wpa_s->conf->wps_nfc_dev_pw_id); + if (dev_pw) { + wpa_snprintf_hex_uppercase(pw, sizeof(pw), + wpabuf_head(dev_pw), + wpabuf_len(dev_pw)); + } + return wpas_wps_start_dev_pw(wpa_s, go_dev_addr, bssid, + dev_pw ? pw : NULL, + p2p_group, dev_pw_id, peer_pubkey_hash, + ssid, ssid_len, freq); } static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s, struct wps_parse_attr *attr) { - wpa_s->wps_ap_channel = 0; + /* + * Disable existing networks temporarily to allow the newly learned + * credential to be preferred. Enable the temporarily disabled networks + * after 10 seconds. + */ + wpas_wps_temp_disable(wpa_s, NULL); + eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s, + NULL); if (wps_oob_use_cred(wpa_s->wps, attr) < 0) return -1; @@ -1882,12 +2289,8 @@ static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s, if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) return 0; - wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network " - "based on the received credential added"); - wpa_s->normal_scans = 0; - wpa_supplicant_reinit_autoscan(wpa_s); - if (wpa_s->wps_ap_channel) { - u16 chan = wpa_s->wps_ap_channel; + if (attr->ap_channel) { + u16 chan = WPA_GET_BE16(attr->ap_channel); int freq = 0; if (chan >= 1 && chan <= 13) @@ -1898,14 +2301,21 @@ static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s, freq = 5000 + 5 * chan; if (freq) { - wpa_printf(MSG_DEBUG, "WPS: Credential indicated " - "AP channel %u -> %u MHz", chan, freq); + wpa_printf(MSG_DEBUG, "WPS: Credential container indicated AP channel %u -> %u MHz", + chan, freq); wpa_s->after_wps = 5; wpa_s->wps_freq = freq; } } + + wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network " + "based on the received credential added"); + wpa_s->normal_scans = 0; + wpa_supplicant_reinit_autoscan(wpa_s); wpa_s->disconnected = 0; wpa_s->reassociate = 1; + + wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); return 0; @@ -1949,7 +2359,7 @@ static int wpas_wps_nfc_tag_process(struct wpa_supplicant *wpa_s, int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s, - const struct wpabuf *data) + const struct wpabuf *data, int forced_freq) { const struct wpabuf *wps = data; struct wpabuf *tmp = NULL; @@ -1962,6 +2372,15 @@ int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s, /* Assume this contains full NDEF record */ tmp = ndef_parse_wifi(data); if (tmp == NULL) { +#ifdef CONFIG_P2P + tmp = ndef_parse_p2p(data); + if (tmp) { + ret = wpas_p2p_nfc_tag_process(wpa_s, tmp, + forced_freq); + wpabuf_free(tmp); + return ret; + } +#endif /* CONFIG_P2P */ wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF"); return -1; } @@ -1974,53 +2393,337 @@ int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s, } -struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s) +struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s, + int ndef) { - return ndef_build_wifi_hr(); + struct wpabuf *ret; + + if (wpa_s->conf->wps_nfc_dh_pubkey == NULL && + wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey, + &wpa_s->conf->wps_nfc_dh_privkey) < 0) + return NULL; + + ret = wps_build_nfc_handover_req(wpa_s->wps, + wpa_s->conf->wps_nfc_dh_pubkey); + + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; } -struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s) +#ifdef CONFIG_WPS_NFC + +static struct wpabuf * +wpas_wps_er_nfc_handover_sel(struct wpa_supplicant *wpa_s, int ndef, + const char *uuid) { +#ifdef CONFIG_WPS_ER + struct wpabuf *ret; + u8 u[UUID_LEN], *use_uuid = NULL; + u8 addr[ETH_ALEN], *use_addr = NULL; + struct wps_context *wps = wpa_s->wps; + + if (wps == NULL) + return NULL; + + if (uuid == NULL) + return NULL; + if (uuid_str2bin(uuid, u) == 0) + use_uuid = u; + else if (hwaddr_aton(uuid, addr) == 0) + use_addr = addr; + else + return NULL; + + if (wpa_s->conf->wps_nfc_dh_pubkey == NULL) { + if (wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey, + &wpa_s->conf->wps_nfc_dh_privkey) < 0) + return NULL; + } + + wpas_wps_nfc_clear(wps); + wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER; + wps->ap_nfc_dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey); + wps->ap_nfc_dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey); + if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) { + wpas_wps_nfc_clear(wps); + return NULL; + } + + ret = wps_er_nfc_handover_sel(wpa_s->wps_er, wpa_s->wps, use_uuid, + use_addr, wpa_s->conf->wps_nfc_dh_pubkey); + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; +#else /* CONFIG_WPS_ER */ return NULL; +#endif /* CONFIG_WPS_ER */ } +#endif /* CONFIG_WPS_NFC */ -int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s, - const struct wpabuf *data) +struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s, + int ndef, int cr, const char *uuid) { - /* TODO */ - return -1; + struct wpabuf *ret; + if (!cr) + return NULL; + ret = wpas_ap_wps_nfc_handover_sel(wpa_s, ndef); + if (ret) + return ret; + return wpas_wps_er_nfc_handover_sel(wpa_s, ndef, uuid); } -int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s, - const struct wpabuf *data) +static int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s, + const struct wpabuf *data) { struct wpabuf *wps; - int ret; + int ret = -1; + u16 wsc_len; + const u8 *pos; + struct wpabuf msg; + struct wps_parse_attr attr; + u16 dev_pw_id; + const u8 *bssid = NULL; + int freq = 0; wps = ndef_parse_wifi(data); if (wps == NULL) return -1; wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc " "payload from NFC connection handover"); - wpa_hexdump_buf_key(MSG_DEBUG, "WPS: NFC payload", wps); - ret = wpas_wps_nfc_tag_process(wpa_s, wps); + wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps); + if (wpabuf_len(wps) < 2) { + wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Select " + "Message"); + goto out; + } + pos = wpabuf_head(wps); + wsc_len = WPA_GET_BE16(pos); + if (wsc_len > wpabuf_len(wps) - 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) " + "in Wi-Fi Handover Select Message", wsc_len); + goto out; + } + pos += 2; + + wpa_hexdump(MSG_DEBUG, + "WPS: WSC attributes in Wi-Fi Handover Select Message", + pos, wsc_len); + if (wsc_len < wpabuf_len(wps) - 2) { + wpa_hexdump(MSG_DEBUG, + "WPS: Ignore extra data after WSC attributes", + pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len); + } + + wpabuf_set(&msg, pos, wsc_len); + ret = wps_parse_msg(&msg, &attr); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in " + "Wi-Fi Handover Select Message"); + goto out; + } + + if (attr.oob_dev_password == NULL || + attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) { + wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password " + "included in Wi-Fi Handover Select Message"); + ret = -1; + goto out; + } + + if (attr.ssid == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No SSID included in Wi-Fi Handover " + "Select Message"); + ret = -1; + goto out; + } + + wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", attr.ssid, attr.ssid_len); + + if (attr.mac_addr) { + bssid = attr.mac_addr; + wpa_printf(MSG_DEBUG, "WPS: MAC Address (BSSID): " MACSTR, + MAC2STR(bssid)); + } + + if (attr.rf_bands) + wpa_printf(MSG_DEBUG, "WPS: RF Bands: %d", *attr.rf_bands); + + if (attr.ap_channel) { + u16 chan = WPA_GET_BE16(attr.ap_channel); + + wpa_printf(MSG_DEBUG, "WPS: AP Channel: %d", chan); + + if (chan >= 1 && chan <= 13 && + (attr.rf_bands == NULL || *attr.rf_bands & WPS_RF_24GHZ)) + freq = 2407 + 5 * chan; + else if (chan == 14 && + (attr.rf_bands == NULL || + *attr.rf_bands & WPS_RF_24GHZ)) + freq = 2484; + else if (chan >= 30 && + (attr.rf_bands == NULL || + *attr.rf_bands & WPS_RF_50GHZ)) + freq = 5000 + 5 * chan; + + if (freq) { + wpa_printf(MSG_DEBUG, + "WPS: AP indicated channel %u -> %u MHz", + chan, freq); + } + } + + wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password", + attr.oob_dev_password, attr.oob_dev_password_len); + dev_pw_id = WPA_GET_BE16(attr.oob_dev_password + + WPS_OOB_PUBKEY_HASH_LEN); + if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID " + "%u in Wi-Fi Handover Select Message", dev_pw_id); + ret = -1; + goto out; + } + wpa_hexdump(MSG_DEBUG, "WPS: AP Public Key hash", + attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN); + + ret = wpas_wps_start_nfc(wpa_s, NULL, bssid, NULL, dev_pw_id, 0, + attr.oob_dev_password, + attr.ssid, attr.ssid_len, freq); + +out: wpabuf_free(wps); + return ret; +} + + +int wpas_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, + const struct wpabuf *req, + const struct wpabuf *sel) +{ + wpa_printf(MSG_DEBUG, "NFC: WPS connection handover reported"); + wpa_hexdump_buf_key(MSG_DEBUG, "WPS: Carrier record in request", req); + wpa_hexdump_buf_key(MSG_DEBUG, "WPS: Carrier record in select", sel); + return wpas_wps_nfc_rx_handover_sel(wpa_s, sel); +} + + +int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, + const struct wpabuf *req, + const struct wpabuf *sel) +{ + struct wpabuf *wps; + int ret = -1; + u16 wsc_len; + const u8 *pos; + struct wpabuf msg; + struct wps_parse_attr attr; + u16 dev_pw_id; + /* + * Enrollee/station is always initiator of the NFC connection handover, + * so use the request message here to find Enrollee public key hash. + */ + wps = ndef_parse_wifi(req); + if (wps == NULL) + return -1; + wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc " + "payload from NFC connection handover"); + wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps); + if (wpabuf_len(wps) < 2) { + wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request " + "Message"); + goto out; + } + pos = wpabuf_head(wps); + wsc_len = WPA_GET_BE16(pos); + if (wsc_len > wpabuf_len(wps) - 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) " + "in rt Wi-Fi Handover Request Message", wsc_len); + goto out; + } + pos += 2; + + wpa_hexdump(MSG_DEBUG, + "WPS: WSC attributes in Wi-Fi Handover Request Message", + pos, wsc_len); + if (wsc_len < wpabuf_len(wps) - 2) { + wpa_hexdump(MSG_DEBUG, + "WPS: Ignore extra data after WSC attributes", + pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len); + } + + wpabuf_set(&msg, pos, wsc_len); + ret = wps_parse_msg(&msg, &attr); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in " + "Wi-Fi Handover Request Message"); + goto out; + } + + if (attr.oob_dev_password == NULL || + attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) { + wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password " + "included in Wi-Fi Handover Request Message"); + ret = -1; + goto out; + } + + if (attr.uuid_e == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi " + "Handover Request Message"); + ret = -1; + goto out; + } + + wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN); + + wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password", + attr.oob_dev_password, attr.oob_dev_password_len); + dev_pw_id = WPA_GET_BE16(attr.oob_dev_password + + WPS_OOB_PUBKEY_HASH_LEN); + if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID " + "%u in Wi-Fi Handover Request Message", dev_pw_id); + ret = -1; + goto out; + } + wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash", + attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN); + + ret = wps_registrar_add_nfc_pw_token(wpa_s->wps->registrar, + attr.oob_dev_password, + DEV_PW_NFC_CONNECTION_HANDOVER, + NULL, 0, 1); + +out: + wpabuf_free(wps); return ret; } #endif /* CONFIG_WPS_NFC */ -extern int wpa_debug_level; - static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s) { size_t i; - struct os_time now; + struct os_reltime now; if (wpa_debug_level > MSG_DEBUG) return; @@ -2028,7 +2731,7 @@ static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s) if (wpa_s->wps_ap == NULL) return; - os_get_time(&now); + os_get_reltime(&now); for (i = 0; i < wpa_s->num_wps_ap; i++) { struct wps_ap_info *ap = &wpa_s->wps_ap[i]; @@ -2132,11 +2835,14 @@ void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s, void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid) { struct wps_ap_info *ap; + + wpa_s->after_wps = 0; + if (!wpa_s->wps_ap_iter) return; ap = wpas_wps_get_ap_info(wpa_s, bssid); if (ap == NULL) return; ap->tries++; - os_get_time(&ap->last_attempt); + os_get_reltime(&ap->last_attempt); } diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h index dd0dc6036b1a3..683bd50e4bab0 100644 --- a/wpa_supplicant/wps_supplicant.h +++ b/wpa_supplicant/wps_supplicant.h @@ -47,7 +47,7 @@ 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, const char *filter); -int wpas_wps_er_stop(struct wpa_supplicant *wpa_s); +void wpas_wps_er_stop(struct wpa_supplicant *wpa_s); 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); @@ -60,18 +60,27 @@ int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid, struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s, int ndef, const char *uuid); 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); +struct wpabuf * wpas_wps_nfc_config_token(struct wpa_supplicant *wpa_s, + int ndef, const char *id_str); struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef); -int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid); +int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *dev_addr, + const u8 *bssid, + const struct wpabuf *dev_pw, u16 dev_pw_id, + int p2p_group, const u8 *peer_pubkey_hash, + const u8 *ssid, size_t ssid_len, int freq); int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s, - const struct wpabuf *data); -struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s); -struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s); -int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s, - const struct wpabuf *data); -int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s, - const struct wpabuf *data); + const struct wpabuf *data, int forced_freq); +struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s, + int ndef); +struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s, + int ndef, int cr, const char *uuid); +int wpas_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, + const struct wpabuf *req, + const struct wpabuf *sel); +int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, + const struct wpabuf *req, + const struct wpabuf *sel); void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res); void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid); |