diff options
Diffstat (limited to 'contrib/wpa/wpa_supplicant')
70 files changed, 14249 insertions, 2249 deletions
diff --git a/contrib/wpa/wpa_supplicant/Android.mk b/contrib/wpa/wpa_supplicant/Android.mk index a8d6a7f944e9..a6809956d86f 100644 --- a/contrib/wpa/wpa_supplicant/Android.mk +++ b/contrib/wpa/wpa_supplicant/Android.mk @@ -92,7 +92,10 @@ OBJS += eap_register.c OBJS += src/utils/common.c OBJS += src/utils/wpa_debug.c OBJS += src/utils/wpabuf.c +OBJS += src/utils/bitfield.c OBJS += wmm_ac.c +OBJS += op_classes.c +OBJS += rrm.c OBJS_p = wpa_passphrase.c OBJS_p += src/utils/common.c OBJS_p += src/utils/wpa_debug.c @@ -221,8 +224,6 @@ 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 L_CFLAGS += -DCONFIG_MESH @@ -238,6 +239,47 @@ NEED_ECC=y NEED_DH_GROUPS=y endif +ifdef CONFIG_DPP +L_CFLAGS += -DCONFIG_DPP +OBJS += src/common/dpp.c +OBJS += dpp_supplicant.c +NEED_AES_SIV=y +NEED_HMAC_SHA256_KDF=y +NEED_HMAC_SHA384_KDF=y +NEED_HMAC_SHA512_KDF=y +NEED_SHA256=y +NEED_SHA384=y +NEED_SHA512=y +NEED_JSON=y +NEED_GAS_SERVER=y +NEED_BASE64=y +endif + +ifdef CONFIG_OWE +L_CFLAGS += -DCONFIG_OWE +NEED_ECC=y +NEED_HMAC_SHA256_KDF=y +NEED_HMAC_SHA384_KDF=y +NEED_HMAC_SHA512_KDF=y +NEED_SHA256=y +NEED_SHA384=y +NEED_SHA512=y +endif + +ifdef CONFIG_FILS +L_CFLAGS += -DCONFIG_FILS +NEED_SHA384=y +NEED_AES_SIV=y +ifdef CONFIG_FILS_SK_PFS +L_CFLAGS += -DCONFIG_FILS_SK_PFS +NEED_ECC=y +endif +endif + +ifdef CONFIG_MBO +CONFIG_WNM=y +endif + ifdef CONFIG_WNM L_CFLAGS += -DCONFIG_WNM OBJS += wnm_sta.c @@ -254,15 +296,14 @@ ifdef CONFIG_TDLS_TESTING L_CFLAGS += -DCONFIG_TDLS_TESTING endif -ifdef CONFIG_PEERKEY -L_CFLAGS += -DCONFIG_PEERKEY +ifdef CONFIG_PMKSA_CACHE_EXTERNAL +L_CFLAGS += -DCONFIG_PMKSA_CACHE_EXTERNAL endif ifndef CONFIG_NO_WPA OBJS += src/rsn_supp/wpa.c OBJS += src/rsn_supp/preauth.c OBJS += src/rsn_supp/pmksa_cache.c -OBJS += src/rsn_supp/peerkey.c OBJS += src/rsn_supp/wpa_ie.c OBJS += src/common/wpa_common.c NEED_AES=y @@ -294,7 +335,6 @@ OBJS += src/p2p/p2p_invitation.c OBJS += src/p2p/p2p_dev_disc.c OBJS += src/p2p/p2p_group.c OBJS += src/ap/p2p_hostapd.c -OBJS += src/utils/bitfield.c L_CFLAGS += -DCONFIG_P2P NEED_GAS=y NEED_OFFCHANNEL=y @@ -640,6 +680,7 @@ L_CFLAGS += -DEAP_PWD OBJS += src/eap_peer/eap_pwd.c src/eap_common/eap_pwd_common.c CONFIG_IEEE8021X_EAPOL=y NEED_SHA256=y +NEED_ECC=y endif ifdef CONFIG_EAP_EKE @@ -811,13 +852,20 @@ OBJS += src/ap/ieee802_11_ht.c ifdef CONFIG_IEEE80211AC OBJS += src/ap/ieee802_11_vht.c endif +ifdef CONFIG_IEEE80211AX +OBJS += src/ap/ieee802_11_he.c endif -ifdef CONFIG_WNM +endif +ifdef CONFIG_WNM_AP +L_CFLAGS += -DCONFIG_WNM_AP OBJS += src/ap/wnm_ap.c endif ifdef CONFIG_MBO OBJS += src/ap/mbo_ap.c endif +ifdef CONFIG_FILS +OBJS += src/ap/fils_hlp.c +endif ifdef CONFIG_CTRL_IFACE OBJS += src/ap/ctrl_iface_ap.c endif @@ -832,11 +880,9 @@ L_CFLAGS += -DCONFIG_IEEE80211N ifdef CONFIG_IEEE80211AC L_CFLAGS += -DCONFIG_IEEE80211AC endif +ifdef CONFIG_IEEE80211AX +L_CFLAGS += -DCONFIG_IEEE80211AX endif - -ifdef CONFIG_MBO -OBJS += mbo.c -L_CFLAGS += -DCONFIG_MBO endif ifdef NEED_AP_MLME @@ -852,6 +898,10 @@ L_CFLAGS += -DEAP_SERVER_WSC OBJS += src/ap/wps_hostapd.c OBJS += src/eap_server/eap_server_wsc.c endif +ifdef CONFIG_DPP +OBJS += src/ap/dpp_hostapd.c +OBJS += src/ap/gas_query_ap.c +endif ifdef CONFIG_INTERWORKING OBJS += src/ap/gas_serv.c endif @@ -860,18 +910,21 @@ OBJS += src/ap/hs20.c endif endif +ifdef CONFIG_MBO +OBJS += mbo.c +L_CFLAGS += -DCONFIG_MBO +endif + +ifdef CONFIG_TESTING_OPTIONS +L_CFLAGS += -DCONFIG_TESTING_OPTIONS +endif + ifdef NEED_RSN_AUTHENTICATOR L_CFLAGS += -DCONFIG_NO_RADIUS NEED_AES_WRAP=y OBJS += src/ap/wpa_auth.c OBJS += src/ap/wpa_auth_ie.c OBJS += src/ap/pmksa_cache_auth.c -ifdef CONFIG_IEEE80211R -OBJS += src/ap/wpa_auth_ft.c -endif -ifdef CONFIG_PEERKEY -OBJS += src/ap/peerkey_auth.c -endif endif ifdef CONFIG_ACS @@ -971,25 +1024,40 @@ ifdef CONFIG_TLS_ADD_DL LIBS += -ldl LIBS_p += -ldl endif +ifndef CONFIG_TLS_DEFAULT_CIPHERS +CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW" +endif +L_CFLAGS += -DTLS_DEFAULT_CIPHERS=\"$(CONFIG_TLS_DEFAULT_CIPHERS)\" endif ifeq ($(CONFIG_TLS), gnutls) +ifndef CONFIG_CRYPTO +# default to libgcrypt +CONFIG_CRYPTO=gnutls +endif ifdef TLS_FUNCS OBJS += src/crypto/tls_gnutls.c LIBS += -lgnutls -lgpg-error endif -OBJS += src/crypto/crypto_gnutls.c -OBJS_p += src/crypto/crypto_gnutls.c +OBJS += src/crypto/crypto_$(CONFIG_CRYPTO).c +OBJS_p += src/crypto/crypto_$(CONFIG_CRYPTO).c ifdef NEED_FIPS186_2_PRF OBJS += src/crypto/fips_prf_internal.c OBJS += src/crypto/sha1-internal.c endif +ifeq ($(CONFIG_CRYPTO), gnutls) LIBS += -lgcrypt LIBS_p += -lgcrypt -CONFIG_INTERNAL_SHA256=y CONFIG_INTERNAL_RC4=y CONFIG_INTERNAL_DH_GROUP5=y endif +ifeq ($(CONFIG_CRYPTO), nettle) +LIBS += -lnettle -lgmp +LIBS_p += -lnettle -lgmp +CONFIG_INTERNAL_RC4=y +CONFIG_INTERNAL_DH_GROUP5=y +endif +endif ifeq ($(CONFIG_TLS), internal) ifndef CONFIG_CRYPTO @@ -1131,6 +1199,12 @@ endif ifdef NEED_AES_EAX AESOBJS += src/crypto/aes-eax.c NEED_AES_CTR=y +NEED_AES_OMAC1=y +endif +ifdef NEED_AES_SIV +AESOBJS += src/crypto/aes-siv.c +NEED_AES_CTR=y +NEED_AES_OMAC1=y endif ifdef NEED_AES_CTR AESOBJS += src/crypto/aes-ctr.c @@ -1163,9 +1237,6 @@ ifdef CONFIG_INTERNAL_AES AESOBJS += src/crypto/aes-internal-enc.c endif endif -ifdef NEED_AES_SIV -AESOBJS += src/crypto/aes-siv.c -endif ifdef NEED_AES OBJS += $(AESOBJS) endif @@ -1173,8 +1244,10 @@ endif SHA1OBJS = ifdef NEED_SHA1 ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), gnutls) SHA1OBJS += src/crypto/sha1.c endif +endif SHA1OBJS += src/crypto/sha1-prf.c ifdef CONFIG_INTERNAL_SHA1 SHA1OBJS += src/crypto/sha1-internal.c @@ -1200,9 +1273,11 @@ endif MD5OBJS = ifndef CONFIG_FIPS ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), gnutls) MD5OBJS += src/crypto/md5.c endif endif +endif ifdef NEED_MD5 ifdef CONFIG_INTERNAL_MD5 MD5OBJS += src/crypto/md5-internal.c @@ -1240,8 +1315,10 @@ SHA256OBJS = # none by default ifdef NEED_SHA256 L_CFLAGS += -DCONFIG_SHA256 ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), gnutls) SHA256OBJS += src/crypto/sha256.c endif +endif SHA256OBJS += src/crypto/sha256-prf.c ifdef CONFIG_INTERNAL_SHA256 SHA256OBJS += src/crypto/sha256-internal.c @@ -1261,12 +1338,34 @@ ifdef NEED_HMAC_SHA256_KDF L_CFLAGS += -DCONFIG_HMAC_SHA256_KDF SHA256OBJS += src/crypto/sha256-kdf.c endif +ifdef NEED_HMAC_SHA384_KDF +L_CFLAGS += -DCONFIG_HMAC_SHA384_KDF +SHA256OBJS += src/crypto/sha384-kdf.c +endif +ifdef NEED_HMAC_SHA512_KDF +L_CFLAGS += -DCONFIG_HMAC_SHA512_KDF +SHA256OBJS += src/crypto/sha512-kdf.c +endif OBJS += $(SHA256OBJS) endif ifdef NEED_SHA384 L_CFLAGS += -DCONFIG_SHA384 +ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), gnutls) +OBJS += src/crypto/sha384.c +endif +endif OBJS += src/crypto/sha384-prf.c endif +ifdef NEED_SHA512 +L_CFLAGS += -DCONFIG_SHA512 +ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), gnutls) +OBJS += src/crypto/sha512.c +endif +endif +OBJS += src/crypto/sha512-prf.c +endif ifdef NEED_DH_GROUPS OBJS += src/crypto/dh_groups.c @@ -1490,6 +1589,12 @@ OBJS += src/utils/ext_password.c L_CFLAGS += -DCONFIG_EXT_PASSWORD endif +ifdef NEED_GAS_SERVER +OBJS += src/common/gas_server.c +L_CFLAGS += -DCONFIG_GAS_SERVER +NEED_GAS=y +endif + ifdef NEED_GAS OBJS += src/common/gas.c OBJS += gas_query.c @@ -1502,6 +1607,11 @@ OBJS += offchannel.c L_CFLAGS += -DCONFIG_OFFCHANNEL endif +ifdef NEED_JSON +OBJS += src/utils/json.c +L_CFLAGS += -DCONFIG_JSON +endif + OBJS += src/drivers/driver_common.c OBJS += wpa_supplicant.c events.c blacklist.c wpas_glue.c scan.c @@ -1580,9 +1690,7 @@ endif # With BoringSSL we need libkeystore-engine in order to provide access to # keystore keys. -ifneq (,$(wildcard external/boringssl/flavor.mk)) LOCAL_SHARED_LIBRARIES += libkeystore-engine -endif ifdef CONFIG_DRIVER_NL80211 ifneq ($(wildcard external/libnl),) diff --git a/contrib/wpa/wpa_supplicant/ChangeLog b/contrib/wpa/wpa_supplicant/ChangeLog index f28055f4093e..bf4daaa4cb1e 100644 --- a/contrib/wpa/wpa_supplicant/ChangeLog +++ b/contrib/wpa/wpa_supplicant/ChangeLog @@ -1,5 +1,75 @@ ChangeLog for wpa_supplicant +2018-12-02 - v2.7 + * fixed WPA packet number reuse with replayed messages and key + reinstallation + [https://w1.fi/security/2017-1/] (CVE-2017-13077, CVE-2017-13078, + CVE-2017-13079, CVE-2017-13080, CVE-2017-13081, CVE-2017-13082, + CVE-2017-13086, CVE-2017-13087, CVE-2017-13088) + * fixed unauthenticated EAPOL-Key decryption in wpa_supplicant + [https://w1.fi/security/2018-1/] (CVE-2018-14526) + * added support for FILS (IEEE 802.11ai) shared key authentication + * added support for OWE (Opportunistic Wireless Encryption, RFC 8110; + and transition mode defined by WFA) + * added support for DPP (Wi-Fi Device Provisioning Protocol) + * added support for RSA 3k key case with Suite B 192-bit level + * fixed Suite B PMKSA caching not to update PMKID during each 4-way + handshake + * fixed EAP-pwd pre-processing with PasswordHashHash + * added EAP-pwd client support for salted passwords + * fixed a regression in TDLS prohibited bit validation + * started to use estimated throughput to avoid undesired signal + strength based roaming decision + * MACsec/MKA: + - new macsec_linux driver interface support for the Linux + kernel macsec module + - number of fixes and extensions + * added support for external persistent storage of PMKSA cache + (PMKSA_GET/PMKSA_ADD control interface commands; and + MESH_PMKSA_GET/MESH_PMKSA_SET for the mesh case) + * fixed mesh channel configuration pri/sec switch case + * added support for beacon report + * large number of other fixes, cleanup, and extensions + * added support for randomizing local address for GAS queries + (gas_rand_mac_addr parameter) + * fixed EAP-SIM/AKA/AKA' ext auth cases within TLS tunnel + * added option for using random WPS UUID (auto_uuid=1) + * added SHA256-hash support for OCSP certificate matching + * fixed EAP-AKA' to add AT_KDF into Synchronization-Failure + * fixed a regression in RSN pre-authentication candidate selection + * added option to configure allowed group management cipher suites + (group_mgmt network profile parameter) + * removed all PeerKey functionality + * fixed nl80211 AP and mesh mode configuration regression with + Linux 4.15 and newer + * added ap_isolate configuration option for AP mode + * added support for nl80211 to offload 4-way handshake into the driver + * added support for using wolfSSL cryptographic library + * SAE + - added support for configuring SAE password separately of the + WPA2 PSK/passphrase + - fixed PTK and EAPOL-Key integrity and key-wrap algorithm selection + for SAE; + note: this is not backwards compatible, i.e., both the AP and + station side implementations will need to be update at the same + time to maintain interoperability + - added support for Password Identifier + - fixed FT-SAE PMKID matching + * Hotspot 2.0 + - added support for fetching of Operator Icon Metadata ANQP-element + - added support for Roaming Consortium Selection element + - added support for Terms and Conditions + - added support for OSEN connection in a shared RSN BSS + - added support for fetching Venue URL information + * added support for using OpenSSL 1.1.1 + * FT + - disabled PMKSA caching with FT since it is not fully functional + - added support for SHA384 based AKM + - added support for BIP ciphers BIP-CMAC-256, BIP-GMAC-128, + BIP-GMAC-256 in addition to previously supported BIP-CMAC-128 + - fixed additional IE inclusion in Reassociation Request frame when + using FT protocol + 2016-10-02 - v2.6 * fixed WNM Sleep Mode processing when PMF is not enabled [http://w1.fi/security/2015-6/] (CVE-2015-5310) diff --git a/contrib/wpa/wpa_supplicant/README b/contrib/wpa/wpa_supplicant/README index 11ab01a9c171..2a3265f21eaa 100644 --- a/contrib/wpa/wpa_supplicant/README +++ b/contrib/wpa/wpa_supplicant/README @@ -1,7 +1,7 @@ WPA Supplicant ============== -Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> and contributors +Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> and contributors All Rights Reserved. This program is licensed under the BSD license (the one with @@ -83,7 +83,7 @@ Supported WPA/IEEE 802.11i features: authentication) (following methods are supported, but since they do not generate keying material, they cannot be used with WPA or IEEE 802.1X WEP keying) - * EAP-MD5-Challenge + * EAP-MD5-Challenge * EAP-MSCHAPv2 * EAP-GTC * EAP-OTP @@ -965,6 +965,17 @@ 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. +It should be noted that the interface used between wpa_supplicant and +wpa_priv does not include all the capabilities of the wpa_supplicant +driver interface and at times, this interface lacks update especially +for recent addition. Consequently, use of wpa_priv does come with the +price of somewhat reduced available functionality. The next section +describing how wpa_supplicant can be used with reduced privileges +without having to handle the complexity of separate wpa_priv. While that +approve does not provide separation for network admin capabilities, it +does allow other root privileges to be dropped without the drawbacks of +the wpa_priv process. + Linux capabilities instead of privileged process ------------------------------------------------ diff --git a/contrib/wpa/wpa_supplicant/README-HS20 b/contrib/wpa/wpa_supplicant/README-HS20 index e4eed2074f91..334287101c92 100644 --- a/contrib/wpa/wpa_supplicant/README-HS20 +++ b/contrib/wpa/wpa_supplicant/README-HS20 @@ -197,6 +197,20 @@ Credentials can be pre-configured for automatic network selection: # pre-configured with the credential since the NAI Realm information # may not be available or fetched. # +# required_roaming_consortium: Required Roaming Consortium OI +# If required_roaming_consortium_len is non-zero, this field contains the +# Roaming Consortium OI that is required to be advertised by the AP for +# the credential to be considered matching. +# +# roaming_consortiums: Roaming Consortium OI(s) memberships +# This string field contains one or more comma delimited OIs (hexdump) +# identifying the roaming consortiums of which the provider is a member. +# The list is sorted from the most preferred one to the least preferred +# one. A match between the Roaming Consortium OIs advertised by an AP and +# the OIs in this list indicates that successful authentication is +# possible. +# (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/RoamingConsortiumOI) +# # eap: Pre-configured EAP method # This optional field can be used to specify which EAP method will be # used with this credential. If not set, the EAP method is selected @@ -295,6 +309,7 @@ Credentials can be pre-configured for automatic network selection: # ca_cert="/etc/wpa_supplicant/ca.pem" # domain="example.com" # roaming_consortium=223344 +# roaming_consortiums="112233,4455667788,aabbcc" # eap=TTLS # phase2="auth=MSCHAPV2" #} @@ -591,7 +606,7 @@ network={ Hotspot 2.0 connection with external network selection ------------------------------------------------------ -When an component controlling wpa_supplicant takes care of Interworking +When a component controlling wpa_supplicant takes care of Interworking network selection, following configuration and network profile parameters can be used to configure a temporary network profile for a Hotspot 2.0 connection (e.g., with SET, ADD_NETWORK, SET_NETWORK, and @@ -613,6 +628,7 @@ network={ eap=TTLS phase2="auth=MSCHAPV2" update_identifier=54321 + roaming_consortium_selection=112233 #ocsp=2 } @@ -628,4 +644,5 @@ update_identifier: PPS/UpdateIdentifier ca_cert: from the downloaded trust root based on PPS information eap: Credential/UsernamePassword/EAPMethod or NAI Realm list phase2: Credential/UsernamePassword/EAPMethod or NAI Realm list +roaming_consortium_selection: Matching OI from HomeSP/RoamingConsortiumOI ocsp: Credential/CheckAAAServerCertStatus diff --git a/contrib/wpa/wpa_supplicant/android.config b/contrib/wpa/wpa_supplicant/android.config index 02505bb991aa..c97f591311d3 100644 --- a/contrib/wpa/wpa_supplicant/android.config +++ b/contrib/wpa/wpa_supplicant/android.config @@ -1,9 +1,9 @@ # Example wpa_supplicant build time configuration # # This file lists the configuration options that are used when building the -# hostapd binary. All lines starting with # are ignored. Configuration option -# lines must be commented out complete, if they are not to be included, i.e., -# just setting VARIABLE=n is not disabling that variable. +# wpa_supplicant binary. All lines starting with # are ignored. Configuration +# option lines must be commented out complete, if they are not to be included, +# i.e., just setting VARIABLE=n is not disabling that variable. # # This file is included in Makefile, so variables like CFLAGS and LIBS can also # be modified from here. In most cases, these lines should use += in order not @@ -91,10 +91,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 @@ -152,6 +151,9 @@ CONFIG_WPS_NFC=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 @@ -176,8 +178,10 @@ CONFIG_SMARTCARD=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. @@ -254,6 +258,9 @@ CONFIG_ELOOP=eloop # Should we use epoll instead of select? Select is used by default. #CONFIG_ELOOP_EPOLL=y +# Should we use kqueue instead of select? Select is used by default. +#CONFIG_ELOOP_KQUEUE=y + # Select layer 2 packet implementation # linux = Linux packet socket (default) # pcap = libpcap/libdnet/WinPcap @@ -263,8 +270,11 @@ CONFIG_ELOOP=eloop # none = Empty template CONFIG_L2_PACKET=linux -# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) -CONFIG_PEERKEY=y +# Disable Linux packet socket workaround applicable for station interface +# in a bridge for EAPOL frames. This should be uncommented only if the kernel +# is known to not have the regression issue in packet socket behavior with +# bridge interfaces (commit 'bridge: respect RFC2863 operational state')'). +#CONFIG_NO_LINUX_PACKET_SOCKET_WAR=y # IEEE 802.11w (management frame protection), also known as PMF # Driver support is also needed for IEEE 802.11w. @@ -291,6 +301,10 @@ CONFIG_IEEE80211W=y # will be used) #CONFIG_TLSV12=y +# Select which ciphers to use by default with OpenSSL if the user does not +# specify them. +#CONFIG_TLS_DEFAULT_CIPHERS="DEFAULT:!EXP:!LOW" + # If CONFIG_TLS=internal is used, additional library and include paths are # needed for LibTomMath. Alternatively, an integrated, minimal version of # LibTomMath can be used. See beginning of libtommath.c for details on benefits @@ -349,7 +363,7 @@ CONFIG_IEEE80211W=y # amount of memory/flash. #CONFIG_DYNAMIC_EAP_METHODS=y -# IEEE Std 802.11r-2008 (Fast BSS Transition) +# IEEE Std 802.11r-2008 (Fast BSS Transition) for station mode CONFIG_IEEE80211R=y # Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt) @@ -424,11 +438,21 @@ CONFIG_ANDROID_LOG=y # disabled. This will save some in binary size and CPU use. However, this # should only be considered for builds that are known to be used on devices # that meet the requirements described above. -#CONFIG_NO_RANDOM_POOL=y + +# Wpa_supplicant's random pool is not necessary on Android. Randomness is +# already provided by the entropymixer service which ensures sufficient +# entropy is maintained across reboots. Commit b410eb1913 'Initialize +# /dev/urandom earlier in boot' seeds /dev/urandom with that entropy before +# either wpa_supplicant or hostapd are run. +CONFIG_NO_RANDOM_POOL=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 @@ -442,6 +466,9 @@ CONFIG_INTERWORKING=y # Hotspot 2.0 CONFIG_HS20=y +# Enable interface matching in wpa_supplicant +#CONFIG_MATCH_IFACE=y + # Disable roaming in wpa_supplicant CONFIG_NO_ROAMING=y @@ -489,4 +516,36 @@ CONFIG_WIFI_DISPLAY=y # Support Multi Band Operation #CONFIG_MBO=y +# Fast Initial Link Setup (FILS) (IEEE 802.11ai) +# Note: This is an experimental and not yet complete implementation. This +# should not be enabled for production use. +#CONFIG_FILS=y + +# Support RSN on IBSS networks +# This is needed to be able to use mode=1 network profile with proto=RSN and +# key_mgmt=WPA-PSK (i.e., full key management instead of WPA-None). +#CONFIG_IBSS_RSN=y + +# External PMKSA cache control +# This can be used to enable control interface commands that allow the current +# PMKSA cache entries to be fetched and new entries to be added. +#CONFIG_PMKSA_CACHE_EXTERNAL=y + +# Mesh Networking (IEEE 802.11s) +#CONFIG_MESH=y + +# Background scanning modules +# These can be used to request wpa_supplicant to perform background scanning +# operations for roaming within an ESS (same SSID). See the bgscan parameter in +# the wpa_supplicant.conf file for more details. +# Periodic background scans based on signal strength +#CONFIG_BGSCAN_SIMPLE=y +# Learn channels used by the network and try to avoid bgscans on other +# channels (experimental) +#CONFIG_BGSCAN_LEARN=y + +# Opportunistic Wireless Encryption (OWE) +# Experimental implementation of draft-harkins-owe-07.txt +#CONFIG_OWE=y + include $(wildcard $(LOCAL_PATH)/android_config_*.inc) diff --git a/contrib/wpa/wpa_supplicant/ap.c b/contrib/wpa/wpa_supplicant/ap.c index 5afb772ba192..ea846a0fad4b 100644 --- a/contrib/wpa/wpa_supplicant/ap.c +++ b/contrib/wpa/wpa_supplicant/ap.c @@ -46,23 +46,50 @@ static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx); #ifdef CONFIG_IEEE80211N static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, struct hostapd_config *conf, struct hostapd_hw_modes *mode) { #ifdef CONFIG_P2P u8 center_chan = 0; u8 channel = conf->channel; +#endif /* CONFIG_P2P */ if (!conf->secondary_channel) goto no_vht; + /* Use the maximum oper channel width if it's given. */ + if (ssid->max_oper_chwidth) + conf->vht_oper_chwidth = ssid->max_oper_chwidth; + + ieee80211_freq_to_chan(ssid->vht_center_freq2, + &conf->vht_oper_centr_freq_seg1_idx); + + if (!ssid->p2p_group) { + if (!ssid->vht_center_freq1 || + conf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT) + goto no_vht; + ieee80211_freq_to_chan(ssid->vht_center_freq1, + &conf->vht_oper_centr_freq_seg0_idx); + wpa_printf(MSG_DEBUG, "VHT seg0 index %d for AP", + conf->vht_oper_centr_freq_seg0_idx); + return; + } + +#ifdef CONFIG_P2P switch (conf->vht_oper_chwidth) { case VHT_CHANWIDTH_80MHZ: case VHT_CHANWIDTH_80P80MHZ: center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel); + wpa_printf(MSG_DEBUG, + "VHT center channel %u for 80 or 80+80 MHz bandwidth", + center_chan); break; case VHT_CHANWIDTH_160MHZ: center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel); + wpa_printf(MSG_DEBUG, + "VHT center channel %u for 160 MHz bandwidth", + center_chan); break; default: /* @@ -72,10 +99,17 @@ static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s, */ conf->vht_oper_chwidth = VHT_CHANWIDTH_160MHZ; center_chan = wpas_p2p_get_vht160_center(wpa_s, mode, channel); - if (!center_chan) { + if (center_chan) { + wpa_printf(MSG_DEBUG, + "VHT center channel %u for auto-selected 160 MHz bandwidth", + center_chan); + } else { conf->vht_oper_chwidth = VHT_CHANWIDTH_80MHZ; center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel); + wpa_printf(MSG_DEBUG, + "VHT center channel %u for auto-selected 80 MHz bandwidth", + center_chan); } break; } @@ -83,15 +117,17 @@ static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s, goto no_vht; conf->vht_oper_centr_freq_seg0_idx = center_chan; + wpa_printf(MSG_DEBUG, "VHT seg0 index %d for P2P GO", + conf->vht_oper_centr_freq_seg0_idx); return; +#endif /* CONFIG_P2P */ no_vht: - conf->vht_oper_centr_freq_seg0_idx = - channel + conf->secondary_channel * 2; -#else /* CONFIG_P2P */ + wpa_printf(MSG_DEBUG, + "No VHT higher bandwidth support for the selected channel %d", + conf->channel); conf->vht_oper_centr_freq_seg0_idx = conf->channel + conf->secondary_channel * 2; -#endif /* CONFIG_P2P */ conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT; } #endif /* CONFIG_IEEE80211N */ @@ -123,6 +159,11 @@ int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, if (wpa_s->hw.modes) { struct hostapd_hw_modes *mode = NULL; int i, no_ht = 0; + + wpa_printf(MSG_DEBUG, + "Determining HT/VHT options based on driver capabilities (freq=%u chan=%u)", + ssid->frequency, conf->channel); + for (i = 0; i < wpa_s->hw.num_modes; i++) { if (wpa_s->hw.modes[i].mode == conf->hw_mode) { mode = &wpa_s->hw.modes[i]; @@ -131,27 +172,54 @@ int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, } #ifdef CONFIG_HT_OVERRIDES - if (ssid->disable_ht) { + if (ssid->disable_ht) + ssid->ht = 0; +#endif /* CONFIG_HT_OVERRIDES */ + + if (!ssid->ht) { + wpa_printf(MSG_DEBUG, + "HT not enabled in network profile"); conf->ieee80211n = 0; conf->ht_capab = 0; no_ht = 1; } -#endif /* CONFIG_HT_OVERRIDES */ if (!no_ht && mode && mode->ht_capab) { + wpa_printf(MSG_DEBUG, + "Enable HT support (p2p_group=%d 11a=%d ht40_hw_capab=%d ssid->ht40=%d)", + ssid->p2p_group, + conf->hw_mode == HOSTAPD_MODE_IEEE80211A, + !!(mode->ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET), + ssid->ht40); conf->ieee80211n = 1; #ifdef CONFIG_P2P - if (conf->hw_mode == HOSTAPD_MODE_IEEE80211A && + if (ssid->p2p_group && + conf->hw_mode == HOSTAPD_MODE_IEEE80211A && (mode->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && - ssid->ht40) + ssid->ht40) { conf->secondary_channel = wpas_p2p_get_ht40_mode(wpa_s, mode, conf->channel); + wpa_printf(MSG_DEBUG, + "HT secondary channel offset %d for P2P group", + conf->secondary_channel); + } +#endif /* CONFIG_P2P */ + + if (!ssid->p2p_group && + (mode->ht_capab & + HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { + conf->secondary_channel = ssid->ht40; + wpa_printf(MSG_DEBUG, + "HT secondary channel offset %d for AP", + conf->secondary_channel); + } + if (conf->secondary_channel) conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; -#endif /* CONFIG_P2P */ /* * white-list capabilities that won't cause issues @@ -168,7 +236,8 @@ int wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s, if (mode->vht_capab && ssid->vht) { conf->ieee80211ac = 1; - wpas_conf_ap_vht(wpa_s, conf, mode); + conf->vht_capab |= mode->vht_capab; + wpas_conf_ap_vht(wpa_s, ssid, conf, mode); } } } @@ -229,11 +298,13 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_ACS */ - if (ieee80211_is_dfs(ssid->frequency) && wpa_s->conf->country[0]) { + if (ieee80211_is_dfs(ssid->frequency, wpa_s->hw.modes, + wpa_s->hw.num_modes) && 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]; + conf->country[2] = ' '; } #ifdef CONFIG_P2P @@ -316,17 +387,34 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, for (i = 0; i < NUM_WEP_KEYS; i++) { if (ssid->wep_key_len[i] == 0) continue; - wep->key[i] = os_malloc(ssid->wep_key_len[i]); + wep->key[i] = os_memdup(ssid->wep_key[i], + ssid->wep_key_len[i]); if (wep->key[i] == NULL) return -1; - os_memcpy(wep->key[i], ssid->wep_key[i], - ssid->wep_key_len[i]); wep->len[i] = ssid->wep_key_len[i]; } wep->idx = ssid->wep_tx_keyidx; wep->keys_set = 1; } + if (wpa_s->conf->go_interworking) { + wpa_printf(MSG_DEBUG, + "P2P: Enable Interworking with access_network_type: %d", + wpa_s->conf->go_access_network_type); + bss->interworking = wpa_s->conf->go_interworking; + bss->access_network_type = wpa_s->conf->go_access_network_type; + bss->internet = wpa_s->conf->go_internet; + if (wpa_s->conf->go_venue_group) { + wpa_printf(MSG_DEBUG, + "P2P: Venue group: %d Venue type: %d", + wpa_s->conf->go_venue_group, + wpa_s->conf->go_venue_type); + bss->venue_group = wpa_s->conf->go_venue_group; + bss->venue_type = wpa_s->conf->go_venue_type; + bss->venue_info_set = 1; + } + } + if (ssid->ap_max_inactivity) bss->ap_max_inactivity = ssid->ap_max_inactivity; @@ -461,6 +549,9 @@ no_wps: else bss->max_num_sta = wpa_s->conf->max_num_sta; + if (!bss->isolate) + bss->isolate = wpa_s->conf->ap_isolate; + bss->disassoc_low_ack = wpa_s->conf->disassoc_low_ack; if (wpa_s->conf->ap_vendor_elements) { @@ -585,9 +676,18 @@ static void wpas_ap_configured_cb(void *ctx) { struct wpa_supplicant *wpa_s = ctx; + wpa_printf(MSG_DEBUG, "AP interface setup completed - state %s", + hostapd_state_text(wpa_s->ap_iface->state)); + if (wpa_s->ap_iface->state == HAPD_IFACE_DISABLED) { + wpa_supplicant_ap_deinit(wpa_s); + return; + } + #ifdef CONFIG_ACS - if (wpa_s->current_ssid && wpa_s->current_ssid->acs) + if (wpa_s->current_ssid && wpa_s->current_ssid->acs) { wpa_s->assoc_freq = wpa_s->ap_iface->freq; + wpa_s->current_ssid->frequency = wpa_s->ap_iface->freq; + } #endif /* CONFIG_ACS */ wpa_supplicant_set_state(wpa_s, WPA_COMPLETED); @@ -662,7 +762,8 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, else params.uapsd = -1; - if (ieee80211_is_dfs(params.freq.freq)) + if (ieee80211_is_dfs(params.freq.freq, wpa_s->hw.modes, + wpa_s->hw.num_modes)) params.freq.freq = 0; /* set channel after CAC */ if (params.p2p) @@ -692,13 +793,6 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, return -1; } - /* Use the maximum oper channel width if it's given. */ - if (ssid->max_oper_chwidth) - conf->vht_oper_chwidth = ssid->max_oper_chwidth; - - ieee80211_freq_to_chan(ssid->vht_center_freq2, - &conf->vht_oper_centr_freq_seg1_idx); - os_memcpy(wpa_s->ap_iface->conf->wmm_ac_params, wpa_s->conf->wmm_ac_params, sizeof(wpa_s->conf->wmm_ac_params)); @@ -777,6 +871,14 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, os_memcpy(wpa_s->bssid, wpa_s->own_addr, ETH_ALEN); wpa_s->assoc_freq = ssid->frequency; +#if defined(CONFIG_P2P) && defined(CONFIG_ACS) + if (wpa_s->p2p_go_do_acs) { + wpa_s->ap_iface->conf->channel = 0; + wpa_s->ap_iface->conf->hw_mode = wpa_s->p2p_go_acs_band; + ssid->acs = 1; + } +#endif /* CONFIG_P2P && CONFIG_ACS */ + if (hostapd_setup_interface(wpa_s->ap_iface)) { wpa_printf(MSG_ERROR, "Failed to initialize AP interface"); wpa_supplicant_ap_deinit(wpa_s); @@ -1436,12 +1538,49 @@ void wpas_ap_pmksa_cache_flush(struct wpa_supplicant *wpa_s) if (wpa_s->ifmsh) hostapd_ctrl_iface_pmksa_flush(wpa_s->ifmsh->bss[0]); } + + +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL +#ifdef CONFIG_MESH + +int wpas_ap_pmksa_cache_list_mesh(struct wpa_supplicant *wpa_s, const u8 *addr, + char *buf, size_t len) +{ + return hostapd_ctrl_iface_pmksa_list_mesh(wpa_s->ifmsh->bss[0], addr, + &buf[0], len); +} + + +int wpas_ap_pmksa_cache_add_external(struct wpa_supplicant *wpa_s, char *cmd) +{ + struct external_pmksa_cache *entry; + void *pmksa_cache; + + pmksa_cache = hostapd_ctrl_iface_pmksa_create_entry(wpa_s->own_addr, + cmd); + if (!pmksa_cache) + return -1; + + entry = os_zalloc(sizeof(struct external_pmksa_cache)); + if (!entry) + return -1; + + entry->pmksa_cache = pmksa_cache; + + dl_list_add(&wpa_s->mesh_external_pmksa_cache, &entry->list); + + return 0; +} + +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ + #endif /* CONFIG_CTRL_IFACE */ #ifdef NEED_AP_MLME -void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s, - struct dfs_event *radar) +void wpas_ap_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; @@ -1453,8 +1592,8 @@ void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s, } -void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s, - struct dfs_event *radar) +void wpas_ap_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; @@ -1465,8 +1604,8 @@ void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s, } -void wpas_event_dfs_cac_finished(struct wpa_supplicant *wpa_s, - struct dfs_event *radar) +void wpas_ap_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; @@ -1477,8 +1616,8 @@ void wpas_event_dfs_cac_finished(struct wpa_supplicant *wpa_s, } -void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s, - struct dfs_event *radar) +void wpas_ap_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; @@ -1489,8 +1628,8 @@ void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s, } -void wpas_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s, - struct dfs_event *radar) +void wpas_ap_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; diff --git a/contrib/wpa/wpa_supplicant/ap.h b/contrib/wpa/wpa_supplicant/ap.h index 5a59ddcc1c93..447b551863a3 100644 --- a/contrib/wpa/wpa_supplicant/ap.h +++ b/contrib/wpa/wpa_supplicant/ap.h @@ -85,17 +85,20 @@ int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s); int wpas_ap_pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, size_t len); void wpas_ap_pmksa_cache_flush(struct wpa_supplicant *wpa_s); +int wpas_ap_pmksa_cache_list_mesh(struct wpa_supplicant *wpa_s, const u8 *addr, + char *buf, size_t len); +int wpas_ap_pmksa_cache_add_external(struct wpa_supplicant *wpa_s, char *cmd); -void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s, +void wpas_ap_event_dfs_radar_detected(struct wpa_supplicant *wpa_s, + struct dfs_event *radar); +void wpas_ap_event_dfs_cac_started(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); +void wpas_ap_event_dfs_cac_finished(struct wpa_supplicant *wpa_s, + struct dfs_event *radar); +void wpas_ap_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s, + struct dfs_event *radar); +void wpas_ap_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s, + struct dfs_event *radar); void ap_periodic(struct wpa_supplicant *wpa_s); diff --git a/contrib/wpa/wpa_supplicant/autoscan.c b/contrib/wpa/wpa_supplicant/autoscan.c index 072a1d5414ae..5056a9300a87 100644 --- a/contrib/wpa/wpa_supplicant/autoscan.c +++ b/contrib/wpa/wpa_supplicant/autoscan.c @@ -47,11 +47,16 @@ int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan) struct sched_scan_plan *scan_plans; /* Give preference to scheduled scan plans if supported/configured */ - if (wpa_s->sched_scan_plans) + if (wpa_s->sched_scan_plans) { + wpa_printf(MSG_DEBUG, + "autoscan: sched_scan_plans set - use it instead"); return 0; + } - if (wpa_s->autoscan && wpa_s->autoscan_priv) + if (wpa_s->autoscan && wpa_s->autoscan_priv) { + wpa_printf(MSG_DEBUG, "autoscan: Already initialized"); return 0; + } if (name == NULL) return 0; diff --git a/contrib/wpa/wpa_supplicant/bgscan.c b/contrib/wpa/wpa_supplicant/bgscan.c index 798b43c3fdf7..1ea640114c8e 100644 --- a/contrib/wpa/wpa_supplicant/bgscan.c +++ b/contrib/wpa/wpa_supplicant/bgscan.c @@ -34,8 +34,6 @@ int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, const struct bgscan_ops *ops = NULL; bgscan_deinit(wpa_s); - if (name == NULL) - return -1; params = os_strchr(name, ':'); if (params == NULL) { diff --git a/contrib/wpa/wpa_supplicant/bgscan_learn.c b/contrib/wpa/wpa_supplicant/bgscan_learn.c index a320cc43068c..cb732f709b9e 100644 --- a/contrib/wpa/wpa_supplicant/bgscan_learn.c +++ b/contrib/wpa/wpa_supplicant/bgscan_learn.c @@ -320,9 +320,6 @@ static int bgscan_learn_get_params(struct bgscan_learn_data *data, { const char *pos; - if (params == NULL) - return 0; - data->short_interval = atoi(params); pos = os_strchr(params, ':'); diff --git a/contrib/wpa/wpa_supplicant/bgscan_simple.c b/contrib/wpa/wpa_supplicant/bgscan_simple.c index a467cc5b9271..41a26df0d635 100644 --- a/contrib/wpa/wpa_supplicant/bgscan_simple.c +++ b/contrib/wpa/wpa_supplicant/bgscan_simple.c @@ -56,12 +56,7 @@ static void bgscan_simple_timeout(void *eloop_ctx, void *timeout_ctx) } else { if (data->scan_interval == data->short_interval) { data->short_scan_count++; - /* - * Spend at most the duration of a long scan interval - * scanning at the short scan interval. After that, - * revert to the long scan interval. - */ - if (data->short_scan_count > data->max_short_scans) { + if (data->short_scan_count >= data->max_short_scans) { data->scan_interval = data->long_interval; wpa_printf(MSG_DEBUG, "bgscan simple: Backing " "off to long scan interval"); @@ -85,9 +80,6 @@ static int bgscan_simple_get_params(struct bgscan_simple_data *data, { const char *pos; - if (params == NULL) - return 0; - data->short_interval = atoi(params); pos = os_strchr(params, ':'); diff --git a/contrib/wpa/wpa_supplicant/bss.c b/contrib/wpa/wpa_supplicant/bss.c index 3a8778db9058..3a41db98e5ba 100644 --- a/contrib/wpa/wpa_supplicant/bss.c +++ b/contrib/wpa/wpa_supplicant/bss.c @@ -93,6 +93,7 @@ static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp) ANQP_DUP(nai_realm); ANQP_DUP(anqp_3gpp); ANQP_DUP(domain_name); + ANQP_DUP(fils_realm_info); #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_HS20 ANQP_DUP(hs20_capability_list); @@ -101,6 +102,8 @@ static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp) ANQP_DUP(hs20_connection_capability); ANQP_DUP(hs20_operating_class); ANQP_DUP(hs20_osu_providers_list); + ANQP_DUP(hs20_operator_icon_metadata); + ANQP_DUP(hs20_osu_providers_nai_list); #endif /* CONFIG_HS20 */ #undef ANQP_DUP @@ -168,6 +171,7 @@ static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp) wpabuf_free(anqp->nai_realm); wpabuf_free(anqp->anqp_3gpp); wpabuf_free(anqp->domain_name); + wpabuf_free(anqp->fils_realm_info); while ((elem = dl_list_first(&anqp->anqp_elems, struct wpa_bss_anqp_elem, list))) { @@ -183,6 +187,8 @@ static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp) wpabuf_free(anqp->hs20_connection_capability); wpabuf_free(anqp->hs20_operating_class); wpabuf_free(anqp->hs20_osu_providers_list); + wpabuf_free(anqp->hs20_operator_icon_metadata); + wpabuf_free(anqp->hs20_osu_providers_nai_list); #endif /* CONFIG_HS20 */ os_free(anqp); @@ -267,9 +273,9 @@ struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid, } -static void calculate_update_time(const struct os_reltime *fetch_time, - unsigned int age_ms, - struct os_reltime *update_time) +void calculate_update_time(const struct os_reltime *fetch_time, + unsigned int age_ms, + struct os_reltime *update_time) { os_time_t usec; @@ -595,6 +601,42 @@ wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, { u32 changes; + if (bss->last_update_idx == wpa_s->bss_update_idx) { + struct os_reltime update_time; + + /* + * Some drivers (e.g., cfg80211) include multiple BSS entries + * for the same BSS if that BSS's channel changes. The BSS list + * implementation in wpa_supplicant does not do that and we need + * to filter out the obsolete results here to make sure only the + * most current BSS information remains in the table. + */ + wpa_printf(MSG_DEBUG, "BSS: " MACSTR + " has multiple entries in the scan results - select the most current one", + MAC2STR(bss->bssid)); + calculate_update_time(fetch_time, res->age, &update_time); + wpa_printf(MSG_DEBUG, + "Previous last_update: %u.%06u (freq %d%s)", + (unsigned int) bss->last_update.sec, + (unsigned int) bss->last_update.usec, + bss->freq, + (bss->flags & WPA_BSS_ASSOCIATED) ? " assoc" : ""); + wpa_printf(MSG_DEBUG, "New last_update: %u.%06u (freq %d%s)", + (unsigned int) update_time.sec, + (unsigned int) update_time.usec, + res->freq, + (res->flags & WPA_SCAN_ASSOCIATED) ? " assoc" : ""); + if ((bss->flags & WPA_BSS_ASSOCIATED) || + (!(res->flags & WPA_SCAN_ASSOCIATED) && + !os_reltime_before(&bss->last_update, &update_time))) { + wpa_printf(MSG_DEBUG, + "Ignore this BSS entry since the previous update looks more current"); + return bss; + } + wpa_printf(MSG_DEBUG, + "Accept this BSS entry since it looks more current than the previous update"); + } + changes = wpa_bss_compare_res(bss, res); if (changes & WPA_BSS_FREQ_CHANGED_FLAG) wpa_printf(MSG_DEBUG, "BSS: " MACSTR " changed freq %d --> %d", @@ -1279,3 +1321,19 @@ int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates) *rates = r; return len; } + + +#ifdef CONFIG_FILS +const u8 * wpa_bss_get_fils_cache_id(struct wpa_bss *bss) +{ + const u8 *ie; + + if (bss) { + ie = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION); + if (ie && ie[1] >= 4 && WPA_GET_LE16(ie + 2) & BIT(7)) + return ie + 4; + } + + return NULL; +} +#endif /* CONFIG_FILS */ diff --git a/contrib/wpa/wpa_supplicant/bss.h b/contrib/wpa/wpa_supplicant/bss.h index 84e8fb07461e..5251b2c354e3 100644 --- a/contrib/wpa/wpa_supplicant/bss.h +++ b/contrib/wpa/wpa_supplicant/bss.h @@ -40,6 +40,7 @@ struct wpa_bss_anqp { struct wpabuf *nai_realm; struct wpabuf *anqp_3gpp; struct wpabuf *domain_name; + struct wpabuf *fils_realm_info; struct dl_list anqp_elems; /* list of struct wpa_bss_anqp_elem */ #endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_HS20 @@ -49,6 +50,8 @@ struct wpa_bss_anqp { struct wpabuf *hs20_connection_capability; struct wpabuf *hs20_operating_class; struct wpabuf *hs20_osu_providers_list; + struct wpabuf *hs20_operator_icon_metadata; + struct wpabuf *hs20_osu_providers_nai_list; #endif /* CONFIG_HS20 */ }; @@ -144,6 +147,7 @@ int wpa_bss_get_max_rate(const struct wpa_bss *bss); 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); +const u8 * wpa_bss_get_fils_cache_id(struct wpa_bss *bss); static inline int bss_is_dmg(const struct wpa_bss *bss) { @@ -167,4 +171,8 @@ static inline void wpa_bss_update_level(struct wpa_bss *bss, int new_level) bss->level = new_level; } +void calculate_update_time(const struct os_reltime *fetch_time, + unsigned int age_ms, + struct os_reltime *update_time); + #endif /* BSS_H */ diff --git a/contrib/wpa/wpa_supplicant/config.c b/contrib/wpa/wpa_supplicant/config.c index dd922caf80af..c43960697dc3 100644 --- a/contrib/wpa/wpa_supplicant/config.c +++ b/contrib/wpa/wpa_supplicant/config.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / Configuration parser and common functions - * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2018, 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 "common.h" #include "utils/uuid.h" #include "utils/ip_addr.h" +#include "common/ieee802_1x_defs.h" #include "crypto/sha1.h" #include "rsn_supp/wpa.h" #include "eap_peer/eap.h" @@ -396,6 +397,50 @@ static char * wpa_config_write_bssid(const struct parse_data *data, #endif /* NO_CONFIG_WRITE */ +static int wpa_config_parse_bssid_hint(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 || + os_strcmp(value, "any") == 0) { + ssid->bssid_hint_set = 0; + wpa_printf(MSG_MSGDUMP, "BSSID hint any"); + return 0; + } + if (hwaddr_aton(value, ssid->bssid_hint)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID hint '%s'.", + line, value); + return -1; + } + ssid->bssid_hint_set = 1; + wpa_hexdump(MSG_MSGDUMP, "BSSID hint", ssid->bssid_hint, ETH_ALEN); + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_bssid_hint(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + char *value; + int res; + + if (!ssid->bssid_hint_set) + return NULL; + + value = os_malloc(20); + if (!value) + return NULL; + res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid_hint)); + if (os_snprintf_error(20, res)) { + os_free(value); + return NULL; + } + return value; +} +#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) @@ -690,6 +735,10 @@ static int wpa_config_parse_key_mgmt(const struct parse_data *data, val |= WPA_KEY_MGMT_FT_PSK; else if (os_strcmp(start, "FT-EAP") == 0) val |= WPA_KEY_MGMT_FT_IEEE8021X; +#ifdef CONFIG_SHA384 + else if (os_strcmp(start, "FT-EAP-SHA384") == 0) + val |= WPA_KEY_MGMT_FT_IEEE8021X_SHA384; +#endif /* CONFIG_SHA384 */ #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W else if (os_strcmp(start, "WPA-PSK-SHA256") == 0) @@ -719,6 +768,26 @@ static int wpa_config_parse_key_mgmt(const struct parse_data *data, else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0) val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; #endif /* CONFIG_SUITEB192 */ +#ifdef CONFIG_FILS + else if (os_strcmp(start, "FILS-SHA256") == 0) + val |= WPA_KEY_MGMT_FILS_SHA256; + else if (os_strcmp(start, "FILS-SHA384") == 0) + val |= WPA_KEY_MGMT_FILS_SHA384; +#ifdef CONFIG_IEEE80211R + else if (os_strcmp(start, "FT-FILS-SHA256") == 0) + val |= WPA_KEY_MGMT_FT_FILS_SHA256; + else if (os_strcmp(start, "FT-FILS-SHA384") == 0) + val |= WPA_KEY_MGMT_FT_FILS_SHA384; +#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_FILS */ +#ifdef CONFIG_OWE + else if (os_strcmp(start, "OWE") == 0) + val |= WPA_KEY_MGMT_OWE; +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + else if (os_strcmp(start, "DPP") == 0) + val |= WPA_KEY_MGMT_DPP; +#endif /* CONFIG_DPP */ else { wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", line, start); @@ -827,6 +896,18 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, } pos += ret; } + +#ifdef CONFIG_SHA384 + if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { + ret = os_snprintf(pos, end - pos, "%sFT-EAP-SHA384", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } +#endif /* CONFIG_SHA384 */ #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W @@ -921,6 +1002,47 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data, } #endif /* CONFIG_SUITEB192 */ +#ifdef CONFIG_FILS + if (ssid->key_mgmt & WPA_KEY_MGMT_FILS_SHA256) { + ret = os_snprintf(pos, end - pos, "%sFILS-SHA256", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + if (ssid->key_mgmt & WPA_KEY_MGMT_FILS_SHA384) { + ret = os_snprintf(pos, end - pos, "%sFILS-SHA384", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } +#ifdef CONFIG_IEEE80211R + if (ssid->key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) { + ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA256", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } + if (ssid->key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) { + ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA384", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) { + end[-1] = '\0'; + return buf; + } + pos += ret; + } +#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_FILS */ + if (pos == buf) { os_free(buf); buf = NULL; @@ -1042,6 +1164,40 @@ static char * wpa_config_write_group(const struct parse_data *data, #endif /* NO_CONFIG_WRITE */ +static int wpa_config_parse_group_mgmt(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + int val; + + val = wpa_config_parse_cipher(line, value); + if (val == -1) + return -1; + + if (val & ~WPA_ALLOWED_GROUP_MGMT_CIPHERS) { + wpa_printf(MSG_ERROR, + "Line %d: not allowed group management cipher (0x%x).", + line, val); + return -1; + } + + if (ssid->group_mgmt_cipher == val) + return 1; + wpa_printf(MSG_MSGDUMP, "group_mgmt: 0x%x", val); + ssid->group_mgmt_cipher = val; + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_group_mgmt(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return wpa_config_write_cipher(ssid->group_mgmt_cipher); +} +#endif /* NO_CONFIG_WRITE */ + + static int wpa_config_parse_auth_alg(const struct parse_data *data, struct wpa_ssid *ssid, int line, const char *value) @@ -1816,6 +1972,87 @@ static char * wpa_config_write_mesh_basic_rates(const struct parse_data *data, #endif /* CONFIG_MESH */ +#ifdef CONFIG_MACSEC + +static int wpa_config_parse_mka_cak(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + if (hexstr2bin(value, ssid->mka_cak, MACSEC_CAK_LEN) || + value[MACSEC_CAK_LEN * 2] != '\0') { + wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CAK '%s'.", + line, value); + return -1; + } + + ssid->mka_psk_set |= MKA_PSK_SET_CAK; + + wpa_hexdump_key(MSG_MSGDUMP, "MKA-CAK", ssid->mka_cak, MACSEC_CAK_LEN); + return 0; +} + + +static int wpa_config_parse_mka_ckn(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + if (hexstr2bin(value, ssid->mka_ckn, MACSEC_CKN_LEN) || + value[MACSEC_CKN_LEN * 2] != '\0') { + wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.", + line, value); + return -1; + } + + ssid->mka_psk_set |= MKA_PSK_SET_CKN; + + wpa_hexdump_key(MSG_MSGDUMP, "MKA-CKN", ssid->mka_ckn, MACSEC_CKN_LEN); + return 0; +} + + +#ifndef NO_CONFIG_WRITE + +static char * wpa_config_write_mka_cak(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + if (!(ssid->mka_psk_set & MKA_PSK_SET_CAK)) + return NULL; + + return wpa_config_write_string_hex(ssid->mka_cak, MACSEC_CAK_LEN); +} + + +static char * wpa_config_write_mka_ckn(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + if (!(ssid->mka_psk_set & MKA_PSK_SET_CKN)) + return NULL; + return wpa_config_write_string_hex(ssid->mka_ckn, MACSEC_CKN_LEN); +} + +#endif /* NO_CONFIG_WRITE */ + +#endif /* CONFIG_MACSEC */ + + +static int wpa_config_parse_peerkey(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + wpa_printf(MSG_INFO, "NOTE: Obsolete peerkey parameter ignored"); + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_peerkey(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return NULL; +} +#endif /* NO_CONFIG_WRITE */ + + /* Helper macros for network block parser */ #ifdef OFFSET @@ -1907,24 +2144,34 @@ static const struct parse_data ssid_fields[] = { { STR_RANGE(ssid, 0, SSID_MAX_LEN) }, { INT_RANGE(scan_ssid, 0, 1) }, { FUNC(bssid) }, + { FUNC(bssid_hint) }, { FUNC(bssid_blacklist) }, { FUNC(bssid_whitelist) }, { FUNC_KEY(psk) }, { INT(mem_only_psk) }, + { STR_KEY(sae_password) }, + { STR(sae_password_id) }, { FUNC(proto) }, { FUNC(key_mgmt) }, { INT(bg_scan_period) }, { FUNC(pairwise) }, { FUNC(group) }, + { FUNC(group_mgmt) }, { FUNC(auth_alg) }, { FUNC(scan_freq) }, { FUNC(freq_list) }, + { INT_RANGE(ht, 0, 1) }, + { INT_RANGE(vht, 0, 1) }, + { INT_RANGE(ht40, -1, 1) }, { INT_RANGE(max_oper_chwidth, VHT_CHANWIDTH_USE_HT, VHT_CHANWIDTH_80P80MHZ) }, + { INT(vht_center_freq1) }, + { INT(vht_center_freq2) }, #ifdef IEEE8021X_EAPOL { FUNC(eap) }, { STR_LENe(identity) }, { STR_LENe(anonymous_identity) }, + { STR_LENe(imsi_identity) }, { FUNC_KEY(password) }, { STRe(ca_cert) }, { STRe(ca_path) }, @@ -1981,6 +2228,7 @@ static const struct parse_data ssid_fields[] = { #ifdef CONFIG_MESH { INT_RANGE(mode, 0, 5) }, { INT_RANGE(no_auto_peer, 0, 1) }, + { INT_RANGE(mesh_rssi_threshold, -255, 1) }, #else /* CONFIG_MESH */ { INT_RANGE(mode, 0, 4) }, #endif /* CONFIG_MESH */ @@ -1990,7 +2238,7 @@ static const struct parse_data ssid_fields[] = { #ifdef CONFIG_IEEE80211W { INT_RANGE(ieee80211w, 0, 2) }, #endif /* CONFIG_IEEE80211W */ - { INT_RANGE(peerkey, 0, 1) }, + { FUNC(peerkey) /* obsolete - removed */ }, { INT_RANGE(mixed_cell, 0, 1) }, { INT_RANGE(frequency, 0, 65000) }, { INT_RANGE(fixed_freq, 0, 1) }, @@ -2050,13 +2298,28 @@ static const struct parse_data ssid_fields[] = { { INT(beacon_int) }, #ifdef CONFIG_MACSEC { INT_RANGE(macsec_policy, 0, 1) }, + { INT_RANGE(macsec_integ_only, 0, 1) }, + { INT_RANGE(macsec_port, 1, 65534) }, + { INT_RANGE(mka_priority, 0, 255) }, + { FUNC_KEY(mka_cak) }, + { FUNC_KEY(mka_ckn) }, #endif /* CONFIG_MACSEC */ #ifdef CONFIG_HS20 { INT(update_identifier) }, + { STR_RANGE(roaming_consortium_selection, 0, MAX_ROAMING_CONS_OI_LEN) }, #endif /* CONFIG_HS20 */ { INT_RANGE(mac_addr, 0, 2) }, { INT_RANGE(pbss, 0, 2) }, { INT_RANGE(wps_disabled, 0, 1) }, + { INT_RANGE(fils_dh_group, 0, 65535) }, +#ifdef CONFIG_DPP + { STR(dpp_connector) }, + { STR_LEN(dpp_netaccesskey) }, + { INT(dpp_netaccesskey_expiry) }, + { STR_LEN(dpp_csign) }, +#endif /* CONFIG_DPP */ + { INT_RANGE(owe_group, 0, 65535) }, + { INT_RANGE(owe_only, 0, 1) }, }; #undef OFFSET @@ -2168,6 +2431,7 @@ static void eap_peer_config_free(struct eap_peer_config *eap) os_free(eap->eap_methods); bin_clear_free(eap->identity, eap->identity_len); os_free(eap->anonymous_identity); + os_free(eap->imsi_identity); bin_clear_free(eap->password, eap->password_len); os_free(eap->ca_cert); os_free(eap->ca_path); @@ -2226,6 +2490,8 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid) os_free(ssid->ssid); str_clear_free(ssid->passphrase); os_free(ssid->ext_psk); + str_clear_free(ssid->sae_password); + os_free(ssid->sae_password_id); #ifdef IEEE8021X_EAPOL eap_peer_config_free(&ssid->eap); #endif /* IEEE8021X_EAPOL */ @@ -2242,6 +2508,12 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid) #ifdef CONFIG_MESH os_free(ssid->mesh_basic_rates); #endif /* CONFIG_MESH */ +#ifdef CONFIG_HS20 + os_free(ssid->roaming_consortium_selection); +#endif /* CONFIG_HS20 */ + os_free(ssid->dpp_connector); + bin_clear_free(ssid->dpp_netaccesskey, ssid->dpp_netaccesskey_len); + os_free(ssid->dpp_csign); while ((psk = dl_list_first(&ssid->psk_list, struct psk_list_entry, list))) { dl_list_del(&psk->list); @@ -2495,6 +2767,7 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid) ssid->group_cipher = DEFAULT_GROUP; ssid->key_mgmt = DEFAULT_KEY_MGMT; ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD; + ssid->ht = 1; #ifdef IEEE8021X_EAPOL ssid->eapol_flags = DEFAULT_EAPOL_FLAGS; ssid->eap_workaround = DEFAULT_EAP_WORKAROUND; @@ -2506,6 +2779,7 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid) ssid->dot11MeshRetryTimeout = DEFAULT_MESH_RETRY_TIMEOUT; ssid->dot11MeshConfirmTimeout = DEFAULT_MESH_CONFIRM_TIMEOUT; ssid->dot11MeshHoldingTimeout = DEFAULT_MESH_HOLDING_TIMEOUT; + ssid->mesh_rssi_threshold = DEFAULT_MESH_RSSI_THRESHOLD; #endif /* CONFIG_MESH */ #ifdef CONFIG_HT_OVERRIDES ssid->disable_ht = DEFAULT_DISABLE_HT; @@ -2538,6 +2812,9 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid) #ifdef CONFIG_IEEE80211W ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_MACSEC + ssid->mka_priority = DEFAULT_PRIO_NOT_KEY_SERVER; +#endif /* CONFIG_MACSEC */ ssid->mac_addr = -1; } @@ -2849,11 +3126,64 @@ static int wpa_config_set_cred_req_conn_capab(struct wpa_cred *cred, } +static int wpa_config_set_cred_roaming_consortiums(struct wpa_cred *cred, + const char *value) +{ + u8 roaming_consortiums[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN]; + size_t roaming_consortiums_len[MAX_ROAMING_CONS]; + unsigned int num_roaming_consortiums = 0; + const char *pos, *end; + size_t len; + + os_memset(roaming_consortiums, 0, sizeof(roaming_consortiums)); + os_memset(roaming_consortiums_len, 0, sizeof(roaming_consortiums_len)); + + for (pos = value;;) { + end = os_strchr(pos, ','); + len = end ? (size_t) (end - pos) : os_strlen(pos); + if (!end && len == 0) + break; + if (len == 0 || (len & 1) != 0 || + len / 2 > MAX_ROAMING_CONS_OI_LEN || + hexstr2bin(pos, + roaming_consortiums[num_roaming_consortiums], + len / 2) < 0) { + wpa_printf(MSG_INFO, + "Invalid roaming_consortiums entry: %s", + pos); + return -1; + } + roaming_consortiums_len[num_roaming_consortiums] = len / 2; + num_roaming_consortiums++; + + if (!end) + break; + + if (num_roaming_consortiums >= MAX_ROAMING_CONS) { + wpa_printf(MSG_INFO, + "Too many roaming_consortiums OIs"); + return -1; + } + + pos = end + 1; + } + + os_memcpy(cred->roaming_consortiums, roaming_consortiums, + sizeof(roaming_consortiums)); + os_memcpy(cred->roaming_consortiums_len, roaming_consortiums_len, + sizeof(roaming_consortiums_len)); + cred->num_roaming_consortiums = num_roaming_consortiums; + + return 0; +} + + int wpa_config_set_cred(struct wpa_cred *cred, const char *var, const char *value, int line) { char *val; size_t len; + int res; if (os_strcmp(var, "temporary") == 0) { cred->temporary = atoi(value); @@ -3076,6 +3406,16 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var, return 0; } + if (os_strcmp(var, "roaming_consortiums") == 0) { + res = wpa_config_set_cred_roaming_consortiums(cred, val); + if (res < 0) + wpa_printf(MSG_ERROR, + "Line %d: invalid roaming_consortiums", + line); + os_free(val); + return res; + } + if (os_strcmp(var, "excluded_ssid") == 0) { struct excluded_ssid *e; @@ -3387,6 +3727,31 @@ char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var) return buf; } + if (os_strcmp(var, "roaming_consortiums") == 0) { + size_t buflen; + char *buf, *pos; + size_t i; + + if (!cred->num_roaming_consortiums) + return NULL; + buflen = cred->num_roaming_consortiums * + MAX_ROAMING_CONS_OI_LEN * 2 + 1; + buf = os_malloc(buflen); + if (!buf) + return NULL; + pos = buf; + for (i = 0; i < cred->num_roaming_consortiums; i++) { + if (i > 0) + *pos++ = ','; + pos += wpa_snprintf_hex( + pos, buf + buflen - pos, + cred->roaming_consortiums[i], + cred->roaming_consortiums_len[i]); + } + *pos = '\0'; + return buf; + } + if (os_strcmp(var, "excluded_ssid") == 0) { unsigned int i; char *buf, *end, *pos; @@ -3644,6 +4009,7 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, 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->ap_isolate = DEFAULT_AP_ISOLATE; config->access_network_type = DEFAULT_ACCESS_NETWORK_TYPE; config->scan_cur_freq = DEFAULT_SCAN_CUR_FREQ; config->wmm_ac_params[0] = ac_be; @@ -3658,12 +4024,16 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface, #ifdef CONFIG_MBO config->mbo_cell_capa = DEFAULT_MBO_CELL_CAPA; + config->disassoc_imminent_rssi_threshold = + DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD; + config->oce = DEFAULT_OCE_SUPPORT; #endif /* CONFIG_MBO */ if (ctrl_interface) config->ctrl_interface = os_strdup(ctrl_interface); if (driver_param) config->driver_param = os_strdup(driver_param); + config->gas_rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME; return config; } @@ -4269,6 +4639,7 @@ static const struct global_parse_data global_fields[] = { { FUNC_NO_VAR(load_dynamic_eap), 0 }, #ifdef CONFIG_WPS { FUNC(uuid), CFG_CHANGED_UUID }, + { INT_RANGE(auto_uuid, 0, 1), 0 }, { STR_RANGE(device_name, 0, WPS_DEV_NAME_MAX_LEN), CFG_CHANGED_DEVICE_NAME }, { STR_RANGE(manufacturer, 0, 64), CFG_CHANGED_WPS_STRING }, @@ -4318,6 +4689,7 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(filter_ssids, 0, 1), 0 }, { INT_RANGE(filter_rssi, -100, 0), 0 }, { INT(max_num_sta), 0 }, + { INT_RANGE(ap_isolate, 0, 1), 0 }, { INT_RANGE(disassoc_low_ack, 0, 1), 0 }, #ifdef CONFIG_HS20 { INT_RANGE(hs20, 0, 1), 0 }, @@ -4325,6 +4697,11 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(interworking, 0, 1), 0 }, { FUNC(hessid), 0 }, { INT_RANGE(access_network_type, 0, 15), 0 }, + { INT_RANGE(go_interworking, 0, 1), 0 }, + { INT_RANGE(go_access_network_type, 0, 15), 0 }, + { INT_RANGE(go_internet, 0, 1), 0 }, + { INT_RANGE(go_venue_group, 0, 255), 0 }, + { INT_RANGE(go_venue_type, 0, 255), 0 }, { INT_RANGE(pbc_in_m1, 0, 1), 0 }, { STR(autoscan), 0 }, { INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff), @@ -4345,9 +4722,10 @@ static const struct global_parse_data global_fields[] = { { FUNC(freq_list), 0 }, { INT(scan_cur_freq), 0 }, { INT(sched_scan_interval), 0 }, + { INT(sched_scan_start_delay), 0 }, { INT(tdls_external_control), 0}, { STR(osu_dir), 0 }, - { STR(wowlan_triggers), 0 }, + { STR(wowlan_triggers), CFG_CHANGED_WOWLAN_TRIGGERS }, { INT(p2p_search_delay), 0}, { INT(mac_addr), 0 }, { INT(rand_addr_lifetime), 0 }, @@ -4361,16 +4739,23 @@ static const struct global_parse_data global_fields[] = { { INT_RANGE(fst_priority, 1, FST_MAX_PRIO_VALUE), 0 }, { INT_RANGE(fst_llt, 1, FST_MAX_LLT_MS), 0 }, #endif /* CONFIG_FST */ + { INT_RANGE(cert_in_cb, 0, 1), 0 }, { INT_RANGE(wpa_rsc_relaxation, 0, 1), 0 }, { STR(sched_scan_plans), CFG_CHANGED_SCHED_SCAN_PLANS }, #ifdef CONFIG_MBO { STR(non_pref_chan), 0 }, { INT_RANGE(mbo_cell_capa, MBO_CELL_CAPA_AVAILABLE, MBO_CELL_CAPA_NOT_SUPPORTED), 0 }, -#endif /*CONFIG_MBO */ + { INT_RANGE(disassoc_imminent_rssi_threshold, -120, 0), 0 }, + { INT_RANGE(oce, 0, 3), 0 }, +#endif /* CONFIG_MBO */ { INT(gas_address3), 0 }, { INT_RANGE(ftm_responder, 0, 1), 0 }, { INT_RANGE(ftm_initiator, 0, 1), 0 }, + { INT(gas_rand_addr_lifetime), 0 }, + { INT_RANGE(gas_rand_mac_addr, 0, 2), 0 }, + { INT_RANGE(dpp_config_processing, 0, 2), 0 }, + { INT_RANGE(coloc_intf_reporting, 0, 1), 0 }, }; #undef FUNC diff --git a/contrib/wpa/wpa_supplicant/config.h b/contrib/wpa/wpa_supplicant/config.h index 48e64be5da1a..cd7571f59329 100644 --- a/contrib/wpa/wpa_supplicant/config.h +++ b/contrib/wpa/wpa_supplicant/config.h @@ -32,6 +32,7 @@ #define DEFAULT_BSS_EXPIRATION_AGE 180 #define DEFAULT_BSS_EXPIRATION_SCAN_COUNT 2 #define DEFAULT_MAX_NUM_STA 128 +#define DEFAULT_AP_ISOLATE 0 #define DEFAULT_ACCESS_NETWORK_TYPE 15 #define DEFAULT_SCAN_CUR_FREQ 0 #define DEFAULT_P2P_SEARCH_DELAY 500 @@ -41,6 +42,8 @@ #define DEFAULT_P2P_GO_CTWINDOW 0 #define DEFAULT_WPA_RSC_RELAXATION 1 #define DEFAULT_MBO_CELL_CAPA MBO_CELL_CAPA_NOT_SUPPORTED +#define DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD -75 +#define DEFAULT_OCE_SUPPORT OCE_STA #include "config_ssid.h" #include "wps/wps.h" @@ -48,6 +51,9 @@ #include "common/ieee802_11_common.h" +#define MAX_ROAMING_CONS 36 +#define MAX_ROAMING_CONS_OI_LEN 15 + struct wpa_cred { /** * next - Next credential in the list @@ -222,10 +228,43 @@ struct wpa_cred { */ size_t roaming_consortium_len; + /** + * required_roaming_consortium - Required Roaming Consortium OI + * + * If required_roaming_consortium_len is non-zero, this field contains + * the Roaming Consortium OI that is required to be advertised by the AP + * for the credential to be considered matching. + */ u8 required_roaming_consortium[15]; + + /** + * required_roaming_consortium_len - Length of required_roaming_consortium + */ size_t required_roaming_consortium_len; /** + * roaming_consortiums - Roaming Consortium OI(s) memberships + * + * This field contains one or more OIs identifying the roaming + * consortiums of which the provider is a member. The list is sorted + * from the most preferred one to the least preferred one. A match + * between the Roaming Consortium OIs advertised by an AP and the OIs + * in this list indicates that successful authentication is possible. + * (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/RoamingConsortiumOI) + */ + u8 roaming_consortiums[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN]; + + /** + * roaming_consortiums_len - Length on roaming_consortiums[i] + */ + size_t roaming_consortiums_len[MAX_ROAMING_CONS]; + + /** + * num_roaming_consortiums - Number of entries in roaming_consortiums + */ + unsigned int num_roaming_consortiums; + + /** * eap_method - EAP method to use * * Pre-configured EAP method to use with this credential or %NULL to @@ -334,6 +373,7 @@ struct wpa_cred { #define CFG_CHANGED_NFC_PASSWORD_TOKEN BIT(15) #define CFG_CHANGED_P2P_PASSPHRASE_LEN BIT(16) #define CFG_CHANGED_SCHED_SCAN_PLANS BIT(17) +#define CFG_CHANGED_WOWLAN_TRIGGERS BIT(18) /** * struct wpa_config - wpa_supplicant configuration data @@ -625,6 +665,13 @@ struct wpa_config { u8 uuid[16]; /** + * auto_uuid - Automatic UUID behavior + * 0 = generate static value based on the local MAC address (default) + * 1 = generate a random UUID every time wpa_supplicant starts + */ + int auto_uuid; + + /** * device_name - Device Name (WPS) * User-friendly description of device; up to 32 octets encoded in * UTF-8 @@ -833,6 +880,20 @@ struct wpa_config { unsigned int max_num_sta; /** + * ap_isolate - Whether to use client isolation feature + * + * Client isolation can be used to prevent low-level bridging of + * frames between associated stations in the BSS. By default, + * this bridging is allowed (ap_isolate=0); except in P2P GO case, + * where p2p_intra_bss parameter is used to determine whether to allow + * intra-BSS forwarding (ap_isolate = !p2p_intra_bss). + * + * 0 = do not enable AP isolation + * 1 = enable AP isolation + */ + int ap_isolate; + + /** * freq_list - Array of allowed scan frequencies or %NULL for all * * This is an optional zero-terminated array of frequencies in @@ -854,7 +915,7 @@ struct wpa_config { unsigned int changed_parameters; /** - * disassoc_low_ack - Disassocicate stations with massive packet loss + * disassoc_low_ack - Disassociate stations with massive packet loss */ int disassoc_low_ack; @@ -872,6 +933,34 @@ struct wpa_config { */ int access_network_type; + /** + * go_interworking - Whether Interworking for P2P GO is enabled + */ + int go_interworking; + + /** + * go_access_network_type - P2P GO Access Network Type + * + * This indicates which access network type to advertise if Interworking + * is enabled for P2P GO. + */ + int go_access_network_type; + + /** + * go_internet - Interworking: Internet connectivity (0 or 1) + */ + int go_internet; + + /** + * go_venue_group - Interworking: Venue group + */ + int go_venue_group; + + /** + * go_venue_type: Interworking: Venue type + */ + int go_venue_type; + /** * hessid - Homogenous ESS identifier * @@ -1096,6 +1185,15 @@ struct wpa_config { unsigned int sched_scan_interval; /** + * sched_scan_start_delay - Schedule scan start delay before first scan + * + * Delay (in seconds) before scheduling first scan plan cycle. The + * driver may ignore this parameter and start immediately (or at any + * other time), if this feature is not supported. + */ + unsigned int sched_scan_start_delay; + + /** * tdls_external_control - External control for TDLS setup requests * * Enable TDLS mode where external programs are given the control @@ -1291,6 +1389,19 @@ struct wpa_config { * mbo_cell_capa - Cellular capabilities for MBO */ enum mbo_cellular_capa mbo_cell_capa; + + /** + * disassoc_imminent_rssi_threshold - RSSI threshold of candidate AP + * when disassociation imminent is set. + */ + int disassoc_imminent_rssi_threshold; + + /** + * oce - Enable OCE in STA and/or STA-CFON mode + * - Set BIT(0) to enable OCE in non-AP STA mode + * - Set BIT(1) to enable OCE in STA-CFON mode + */ + unsigned int oce; #endif /* CONFIG_MBO */ /** @@ -1328,6 +1439,45 @@ struct wpa_config { * wpa_supplicant. */ int ftm_initiator; + + /** + * gas_rand_addr_lifetime - Lifetime of random MAC address for ANQP in + * seconds + */ + unsigned int gas_rand_addr_lifetime; + + /** + * gas_rand_mac_addr - GAS 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 gas_rand_mac_addr; + + /** + * dpp_config_processing - How to process DPP configuration + * + * 0 = report received configuration to an external program for + * processing; do not generate any network profile internally + * 1 = report received configuration to an external program and generate + * a network profile internally, but do not automatically connect + * to the created (disabled) profile; the network profile id is + * reported to external programs + * 2 = report received configuration to an external program, generate + * a network profile internally, try to connect to the created + * profile automatically + */ + int dpp_config_processing; + + /** + * coloc_intf_reporting - Colocated interference reporting + * + * dot11CoLocIntfReportingActivated + * 0 = disabled (false) + * 1 = enabled (true) + */ + int coloc_intf_reporting; }; diff --git a/contrib/wpa/wpa_supplicant/config_file.c b/contrib/wpa/wpa_supplicant/config_file.c index 7ae16545bebc..09115e19dc2d 100644 --- a/contrib/wpa/wpa_supplicant/config_file.c +++ b/contrib/wpa/wpa_supplicant/config_file.c @@ -19,6 +19,7 @@ #include "config.h" #include "base64.h" #include "uuid.h" +#include "common/ieee802_1x_defs.h" #include "p2p/p2p.h" #include "eap_peer/eap_methods.h" #include "eap_peer/eap.h" @@ -136,9 +137,13 @@ static int wpa_config_validate_network(struct wpa_ssid *ssid, int line) wpa_config_update_psk(ssid); } + if (ssid->disabled == 2) + ssid->p2p_persistent_group = 1; + if ((ssid->group_cipher & WPA_CIPHER_CCMP) && - !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) && - !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) { + !(ssid->pairwise_cipher & (WPA_CIPHER_CCMP | WPA_CIPHER_CCMP_256 | + WPA_CIPHER_GCMP | WPA_CIPHER_GCMP_256 | + WPA_CIPHER_NONE))) { /* Group cipher cannot be stronger than the pairwise cipher. */ wpa_printf(MSG_DEBUG, "Line %d: removed CCMP from group cipher" " list since it was not allowed for pairwise " @@ -308,7 +313,7 @@ static struct wpa_config_blob * wpa_config_read_blob(FILE *f, int *line, encoded_len += len; } - if (!end) { + if (!end || !encoded) { wpa_printf(MSG_ERROR, "Line %d: blob was not terminated " "properly", *line); os_free(encoded); @@ -393,7 +398,8 @@ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp) if (f == NULL) { wpa_printf(MSG_ERROR, "Failed to open config file '%s', " "error: %s", name, strerror(errno)); - os_free(config); + if (config != cfgp) + os_free(config); return NULL; } @@ -459,7 +465,8 @@ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp) #ifndef WPA_IGNORE_CONFIG_ERRORS if (errors) { - wpa_config_free(config); + if (config != cfgp) + wpa_config_free(config); config = NULL; head = NULL; } @@ -499,6 +506,17 @@ static void write_bssid(FILE *f, struct wpa_ssid *ssid) } +static void write_bssid_hint(FILE *f, struct wpa_ssid *ssid) +{ + char *value = wpa_config_get(ssid, "bssid_hint"); + + if (!value) + return; + fprintf(f, "\tbssid_hint=%s\n", value); + os_free(value); +} + + static void write_psk(FILE *f, struct wpa_ssid *ssid) { char *value; @@ -578,6 +596,22 @@ static void write_group(FILE *f, struct wpa_ssid *ssid) } +static void write_group_mgmt(FILE *f, struct wpa_ssid *ssid) +{ + char *value; + + if (!ssid->group_mgmt_cipher) + return; + + value = wpa_config_get(ssid, "group_mgmt"); + if (!value) + return; + if (value[0]) + fprintf(f, "\tgroup_mgmt=%s\n", value); + os_free(value); +} + + static void write_auth_alg(FILE *f, struct wpa_ssid *ssid) { char *value; @@ -662,6 +696,40 @@ static void write_psk_list(FILE *f, struct wpa_ssid *ssid) #endif /* CONFIG_P2P */ +#ifdef CONFIG_MACSEC + +static void write_mka_cak(FILE *f, struct wpa_ssid *ssid) +{ + char *value; + + if (!(ssid->mka_psk_set & MKA_PSK_SET_CAK)) + return; + + value = wpa_config_get(ssid, "mka_cak"); + if (!value) + return; + fprintf(f, "\tmka_cak=%s\n", value); + os_free(value); +} + + +static void write_mka_ckn(FILE *f, struct wpa_ssid *ssid) +{ + char *value; + + if (!(ssid->mka_psk_set & MKA_PSK_SET_CKN)) + return; + + value = wpa_config_get(ssid, "mka_ckn"); + if (!value) + return; + fprintf(f, "\tmka_ckn=%s\n", value); + os_free(value); +} + +#endif /* CONFIG_MACSEC */ + + static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) { int i; @@ -675,15 +743,19 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) STR(ssid); INT(scan_ssid); write_bssid(f, ssid); + write_bssid_hint(f, ssid); write_str(f, "bssid_blacklist", ssid); write_str(f, "bssid_whitelist", ssid); write_psk(f, ssid); INT(mem_only_psk); + STR(sae_password); + STR(sae_password_id); write_proto(f, ssid); write_key_mgmt(f, ssid); INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD); write_pairwise(f, ssid); write_group(f, ssid); + write_group_mgmt(f, ssid); write_auth_alg(f, ssid); STR(bgscan); STR(autoscan); @@ -692,6 +764,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) write_eap(f, ssid); STR(identity); STR(anonymous_identity); + STR(imsi_identity); STR(password); STR(ca_cert); STR(ca_path); @@ -752,11 +825,16 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) #endif /* CONFIG_ACS */ write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1); INT(disabled); - INT(peerkey); INT(mixed_cell); + INT(vht); + INT_DEF(ht, 1); + INT(ht40); INT(max_oper_chwidth); + INT(vht_center_freq1); + INT(vht_center_freq2); INT(pbss); INT(wps_disabled); + INT(fils_dh_group); #ifdef CONFIG_IEEE80211W write_int(f, "ieee80211w", ssid->ieee80211w, MGMT_FRAME_PROTECTION_DEFAULT); @@ -772,9 +850,15 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT(beacon_int); #ifdef CONFIG_MACSEC INT(macsec_policy); + write_mka_cak(f, ssid); + write_mka_ckn(f, ssid); + INT(macsec_integ_only); + INT(macsec_port); + INT_DEF(mka_priority, DEFAULT_PRIO_NOT_KEY_SERVER); #endif /* CONFIG_MACSEC */ #ifdef CONFIG_HS20 INT(update_identifier); + STR(roaming_consortium_selection); #endif /* CONFIG_HS20 */ write_int(f, "mac_addr", ssid->mac_addr, -1); #ifdef CONFIG_MESH @@ -783,10 +867,19 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) INT_DEF(dot11MeshRetryTimeout, DEFAULT_MESH_RETRY_TIMEOUT); INT_DEF(dot11MeshConfirmTimeout, DEFAULT_MESH_CONFIRM_TIMEOUT); INT_DEF(dot11MeshHoldingTimeout, DEFAULT_MESH_HOLDING_TIMEOUT); + INT_DEF(mesh_rssi_threshold, DEFAULT_MESH_RSSI_THRESHOLD); #endif /* CONFIG_MESH */ INT(wpa_ptk_rekey); INT(group_rekey); INT(ignore_broadcast_ssid); +#ifdef CONFIG_DPP + STR(dpp_connector); + STR(dpp_netaccesskey); + INT(dpp_netaccesskey_expiry); + STR(dpp_csign); +#endif /* CONFIG_DPP */ + INT(owe_group); + INT(owe_only); #ifdef CONFIG_HT_OVERRIDES INT_DEF(disable_ht, DEFAULT_DISABLE_HT); INT_DEF(disable_ht40, DEFAULT_DISABLE_HT40); @@ -949,6 +1042,20 @@ static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred) fprintf(f, "\n"); } + if (cred->num_roaming_consortiums) { + size_t j; + + fprintf(f, "\troaming_consortiums=\""); + for (i = 0; i < cred->num_roaming_consortiums; i++) { + if (i > 0) + fprintf(f, ","); + for (j = 0; j < cred->roaming_consortiums_len[i]; j++) + fprintf(f, "%02x", + cred->roaming_consortiums[i][j]); + } + fprintf(f, "\"\n"); + } + if (cred->sim_num != DEFAULT_USER_SELECTED_SIM) fprintf(f, "\tsim_num=%d\n", cred->sim_num); } @@ -1039,6 +1146,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) uuid_bin2str(config->uuid, buf, sizeof(buf)); fprintf(f, "uuid=%s\n", buf); } + if (config->auto_uuid) + fprintf(f, "auto_uuid=%d\n", config->auto_uuid); if (config->device_name) fprintf(f, "device_name=%s\n", config->device_name); if (config->manufacturer) @@ -1076,6 +1185,17 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) } #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P + { + int i; + char _buf[WPS_DEV_TYPE_BUFSIZE], *buf; + + for (i = 0; i < config->num_sec_device_types; i++) { + buf = wps_dev_type_bin2str(config->sec_device_type[i], + _buf, sizeof(_buf)); + if (buf) + fprintf(f, "sec_device_type=%s\n", buf); + } + } if (config->p2p_listen_reg_class) fprintf(f, "p2p_listen_reg_class=%d\n", config->p2p_listen_reg_class); @@ -1175,8 +1295,12 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) config->bss_expiration_scan_count); if (config->filter_ssids) fprintf(f, "filter_ssids=%d\n", config->filter_ssids); + if (config->filter_rssi) + fprintf(f, "filter_rssi=%d\n", config->filter_rssi); if (config->max_num_sta != DEFAULT_MAX_NUM_STA) fprintf(f, "max_num_sta=%u\n", config->max_num_sta); + if (config->ap_isolate != DEFAULT_AP_ISOLATE) + fprintf(f, "ap_isolate=%u\n", config->ap_isolate); if (config->disassoc_low_ack) fprintf(f, "disassoc_low_ack=%d\n", config->disassoc_low_ack); #ifdef CONFIG_HS20 @@ -1191,6 +1315,17 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->access_network_type != DEFAULT_ACCESS_NETWORK_TYPE) fprintf(f, "access_network_type=%d\n", config->access_network_type); + if (config->go_interworking) + fprintf(f, "go_interworking=%d\n", config->go_interworking); + if (config->go_access_network_type) + fprintf(f, "go_access_network_type=%d\n", + config->go_access_network_type); + if (config->go_internet) + fprintf(f, "go_internet=%d\n", config->go_internet); + if (config->go_venue_group) + fprintf(f, "go_venue_group=%d\n", config->go_venue_group); + if (config->go_venue_type) + fprintf(f, "go_venue_type=%d\n", config->go_venue_type); #endif /* CONFIG_INTERWORKING */ if (config->pbc_in_m1) fprintf(f, "pbc_in_m1=%d\n", config->pbc_in_m1); @@ -1226,7 +1361,7 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->sae_groups) { int i; fprintf(f, "sae_groups="); - for (i = 0; config->sae_groups[i] >= 0; i++) { + for (i = 0; config->sae_groups[i] > 0; i++) { fprintf(f, "%s%d", i > 0 ? " " : "", config->sae_groups[i]); } @@ -1264,6 +1399,10 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) fprintf(f, "sched_scan_interval=%u\n", config->sched_scan_interval); + if (config->sched_scan_start_delay) + fprintf(f, "sched_scan_start_delay=%u\n", + config->sched_scan_start_delay); + if (config->external_sim) fprintf(f, "external_sim=%d\n", config->external_sim); @@ -1278,6 +1417,9 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) if (config->bgscan) fprintf(f, "bgscan=\"%s\"\n", config->bgscan); + if (config->autoscan) + fprintf(f, "autoscan=%s\n", config->autoscan); + if (config->p2p_search_delay != DEFAULT_P2P_SEARCH_DELAY) fprintf(f, "p2p_search_delay=%u\n", config->p2p_search_delay); @@ -1335,6 +1477,12 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) fprintf(f, "non_pref_chan=%s\n", config->non_pref_chan); if (config->mbo_cell_capa != DEFAULT_MBO_CELL_CAPA) fprintf(f, "mbo_cell_capa=%u\n", config->mbo_cell_capa); + if (config->disassoc_imminent_rssi_threshold != + DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD) + fprintf(f, "disassoc_imminent_rssi_threshold=%d\n", + config->disassoc_imminent_rssi_threshold); + if (config->oce != DEFAULT_OCE_SUPPORT) + fprintf(f, "oce=%u\n", config->oce); #endif /* CONFIG_MBO */ if (config->gas_address3) @@ -1344,6 +1492,28 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config) fprintf(f, "ftm_responder=%d\n", config->ftm_responder); if (config->ftm_initiator) fprintf(f, "ftm_initiator=%d\n", config->ftm_initiator); + + if (config->osu_dir) + fprintf(f, "osu_dir=%s\n", config->osu_dir); + + if (config->fst_group_id) + fprintf(f, "fst_group_id=%s\n", config->fst_group_id); + if (config->fst_priority) + fprintf(f, "fst_priority=%d\n", config->fst_priority); + if (config->fst_llt) + fprintf(f, "fst_llt=%d\n", config->fst_llt); + + if (config->gas_rand_addr_lifetime != DEFAULT_RAND_ADDR_LIFETIME) + fprintf(f, "gas_rand_addr_lifetime=%u\n", + config->gas_rand_addr_lifetime); + if (config->gas_rand_mac_addr) + fprintf(f, "gas_rand_mac_addr=%d\n", config->gas_rand_mac_addr); + if (config->dpp_config_processing) + fprintf(f, "dpp_config_processing=%d\n", + config->dpp_config_processing); + if (config->coloc_intf_reporting) + fprintf(f, "coloc_intf_reporting=%d\n", + config->coloc_intf_reporting); } #endif /* CONFIG_NO_CONFIG_WRITE */ diff --git a/contrib/wpa/wpa_supplicant/config_ssid.h b/contrib/wpa/wpa_supplicant/config_ssid.h index 010b594af85e..d2a52d760089 100644 --- a/contrib/wpa/wpa_supplicant/config_ssid.h +++ b/contrib/wpa/wpa_supplicant/config_ssid.h @@ -28,6 +28,7 @@ #define DEFAULT_MESH_RETRY_TIMEOUT 40 #define DEFAULT_MESH_CONFIRM_TIMEOUT 40 #define DEFAULT_MESH_HOLDING_TIMEOUT 40 +#define DEFAULT_MESH_RSSI_THRESHOLD 1 /* no change */ #define DEFAULT_DISABLE_HT 0 #define DEFAULT_DISABLE_HT40 0 #define DEFAULT_DISABLE_SGI 0 @@ -146,6 +147,19 @@ struct wpa_ssid { int bssid_set; /** + * bssid_hint - BSSID hint + * + * If set, this is configured to the driver as a preferred initial BSSID + * while connecting to this network. + */ + u8 bssid_hint[ETH_ALEN]; + + /** + * bssid_hint_set - Whether BSSID hint is configured for this network + */ + int bssid_hint_set; + + /** * go_p2p_dev_addr - GO's P2P Device Address or all zeros if not set */ u8 go_p2p_dev_addr[ETH_ALEN]; @@ -170,6 +184,24 @@ struct wpa_ssid { char *passphrase; /** + * sae_password - SAE password + * + * This parameter can be used to set a password for SAE. By default, the + * passphrase value is used if this separate parameter is not used, but + * passphrase follows the WPA-PSK constraints (8..63 characters) even + * though SAE passwords do not have such constraints. + */ + char *sae_password; + + /** + * sae_password_id - SAE password identifier + * + * This parameter can be used to identify a specific SAE password. If + * not included, the default SAE password is used instead. + */ + char *sae_password_id; + + /** * ext_psk - PSK/passphrase name in external storage * * If this is set, PSK/passphrase will be fetched from external storage @@ -196,6 +228,15 @@ struct wpa_ssid { int group_cipher; /** + * group_mgmt_cipher - Bitfield of allowed group management ciphers + * + * This is a bitfield of WPA_CIPHER_AES_128_CMAC and WPA_CIPHER_BIP_* + * values. If 0, no constraint is used for the cipher, i.e., whatever + * the AP uses is accepted. + */ + int group_mgmt_cipher; + + /** * key_mgmt - Bitfield of allowed key management protocols * * WPA_KEY_MGMT_* @@ -392,17 +433,6 @@ struct wpa_ssid { int disabled_for_connect; /** - * peerkey - Whether PeerKey handshake for direct links is allowed - * - * This is only used when both RSN/WPA2 and IEEE 802.11e (QoS) are - * enabled. - * - * 0 = disabled (default) - * 1 = enabled - */ - int peerkey; - - /** * id_str - Network identifier string for external scripts * * This value is passed to external ctrl_iface monitors in @@ -470,12 +500,14 @@ struct wpa_ssid { int dot11MeshConfirmTimeout; /* msec */ int dot11MeshHoldingTimeout; /* msec */ + int ht; int ht40; int vht; - u8 max_oper_chwidth; + int max_oper_chwidth; + unsigned int vht_center_freq1; unsigned int vht_center_freq2; /** @@ -728,10 +760,71 @@ struct wpa_ssid { * determine whether to use a secure session or not. */ int macsec_policy; + + /** + * macsec_integ_only - Determines how MACsec are transmitted + * + * This setting applies only when MACsec is in use, i.e., + * - macsec_policy is enabled + * - the key server has decided to enable MACsec + * + * 0: Encrypt traffic (default) + * 1: Integrity only + */ + int macsec_integ_only; + + /** + * macsec_port - MACsec port (in SCI) + * + * Port component of the SCI. + * + * Range: 1-65534 (default: 1) + */ + int macsec_port; + + /** + * mka_priority - Priority of MKA Actor + * + * Range: 0-255 (default: 255) + */ + int mka_priority; + + /** + * mka_ckn - MKA pre-shared CKN + */ +#define MACSEC_CKN_LEN 32 + u8 mka_ckn[MACSEC_CKN_LEN]; + + /** + * mka_cak - MKA pre-shared CAK + */ +#define MACSEC_CAK_LEN 16 + u8 mka_cak[MACSEC_CAK_LEN]; + +#define MKA_PSK_SET_CKN BIT(0) +#define MKA_PSK_SET_CAK BIT(1) +#define MKA_PSK_SET (MKA_PSK_SET_CKN | MKA_PSK_SET_CAK) + /** + * mka_psk_set - Whether mka_ckn and mka_cak are set + */ + u8 mka_psk_set; #endif /* CONFIG_MACSEC */ #ifdef CONFIG_HS20 int update_identifier; + + /** + * roaming_consortium_selection - Roaming Consortium Selection + * + * The matching Roaming Consortium OI that was used to generate this + * network profile. + */ + u8 *roaming_consortium_selection; + + /** + * roaming_consortium_selection_len - roaming_consortium_selection len + */ + size_t roaming_consortium_selection_len; #endif /* CONFIG_HS20 */ unsigned int wps_run; @@ -758,12 +851,92 @@ struct wpa_ssid { int no_auto_peer; /** + * mesh_rssi_threshold - Set mesh parameter mesh_rssi_threshold (dBm) + * + * -255..-1 = threshold value in dBm + * 0 = not using RSSI threshold + * 1 = do not change driver default + */ + int mesh_rssi_threshold; + + /** * wps_disabled - WPS disabled in AP mode * * 0 = WPS enabled and configured (default) * 1 = WPS disabled */ int wps_disabled; + + /** + * fils_dh_group - FILS DH Group + * + * 0 = PFS disabled with FILS shared key authentication + * 1-65535 DH Group to use for FILS PFS + */ + int fils_dh_group; + + /** + * dpp_connector - DPP Connector (signedConnector as string) + */ + char *dpp_connector; + + /** + * dpp_netaccesskey - DPP netAccessKey (own private key) + */ + u8 *dpp_netaccesskey; + + /** + * dpp_netaccesskey_len - DPP netAccessKey length in octets + */ + size_t dpp_netaccesskey_len; + + /** + * net_access_key_expiry - DPP netAccessKey expiry in UNIX time stamp + * + * 0 indicates no expiration. + */ + unsigned int dpp_netaccesskey_expiry; + + /** + * dpp_csign - C-sign-key (Configurator public key) + */ + u8 *dpp_csign; + + /** + * dpp_csign_len - C-sign-key length in octets + */ + size_t dpp_csign_len; + + /** + * owe_group - OWE DH Group + * + * 0 = use default (19) first and then try all supported groups one by + * one if AP rejects the selected group + * 1-65535 DH Group to use for OWE + * + * Groups 19 (NIST P-256), 20 (NIST P-384), and 21 (NIST P-521) are + * currently supported. + */ + int owe_group; + + /** + * owe_only - OWE-only mode (disable transition mode) + * + * 0 = enable transition mode (allow connection to either OWE or open + * BSS) + * 1 = disable transition mode (allow connection only with OWE) + */ + int owe_only; + + /** + * owe_transition_bss_select_count - OWE transition BSS select count + * + * This is an internally used variable (i.e., not used in external + * configuration) to track the number of selection attempts done for + * OWE BSS in transition mode. This allows fallback to an open BSS if + * the selection attempts for OWE BSS exceed the configured threshold. + */ + int owe_transition_bss_select_count; }; #endif /* CONFIG_SSID_H */ diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface.c b/contrib/wpa/wpa_supplicant/ctrl_iface.c index d814fdf7fd2d..77a3133d8d56 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface.c @@ -20,6 +20,9 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" +#ifdef CONFIG_DPP +#include "common/dpp.h" +#endif /* CONFIG_DPP */ #include "crypto/tls.h" #include "ap/hostapd.h" #include "eap_peer/eap.h" @@ -52,6 +55,7 @@ #include "offchannel.h" #include "drivers/driver.h" #include "mesh.h" +#include "dpp_supplicant.h" static int wpa_supplicant_global_iface_list(struct wpa_global *global, char *buf, int len); @@ -61,6 +65,7 @@ static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, 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) { char *pos; @@ -339,6 +344,75 @@ static int wpas_ctrl_iface_set_lci(struct wpa_supplicant *wpa_s, } +static int +wpas_ctrl_set_relative_rssi(struct wpa_supplicant *wpa_s, const char *cmd) +{ + int relative_rssi; + + if (os_strcmp(cmd, "disable") == 0) { + wpa_s->srp.relative_rssi_set = 0; + return 0; + } + + relative_rssi = atoi(cmd); + if (relative_rssi < 0 || relative_rssi > 100) + return -1; + wpa_s->srp.relative_rssi = relative_rssi; + wpa_s->srp.relative_rssi_set = 1; + return 0; +} + + +static int wpas_ctrl_set_relative_band_adjust(struct wpa_supplicant *wpa_s, + const char *cmd) +{ + char *pos; + int adjust_rssi; + + /* <band>:adjust_value */ + pos = os_strchr(cmd, ':'); + if (!pos) + return -1; + pos++; + adjust_rssi = atoi(pos); + if (adjust_rssi < -100 || adjust_rssi > 100) + return -1; + + if (os_strncmp(cmd, "2G", 2) == 0) + wpa_s->srp.relative_adjust_band = WPA_SETBAND_2G; + else if (os_strncmp(cmd, "5G", 2) == 0) + wpa_s->srp.relative_adjust_band = WPA_SETBAND_5G; + else + return -1; + + wpa_s->srp.relative_adjust_rssi = adjust_rssi; + + return 0; +} + + +static int wpas_ctrl_iface_set_ric_ies(struct wpa_supplicant *wpa_s, + const char *cmd) +{ + struct wpabuf *ric_ies; + + if (*cmd == '\0' || os_strcmp(cmd, "\"\"") == 0) { + wpabuf_free(wpa_s->ric_ies); + wpa_s->ric_ies = NULL; + return 0; + } + + ric_ies = wpabuf_parse_bin(cmd); + if (!ric_ies) + return -1; + + wpabuf_free(wpa_s->ric_ies); + wpa_s->ric_ies = ric_ies; + + return 0; +} + + static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, char *cmd) { @@ -365,16 +439,29 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, -1, -1, -1, atoi(value)); } else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKLifetime") == 0) { if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, - atoi(value))) + atoi(value))) { ret = -1; + } else { + value[-1] = '='; + wpa_config_process_global(wpa_s->conf, cmd, -1); + } } else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKReauthThreshold") == 0) { if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, - atoi(value))) + atoi(value))) { ret = -1; + } else { + value[-1] = '='; + wpa_config_process_global(wpa_s->conf, cmd, -1); + } } else if (os_strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) { - if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, atoi(value))) + if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, + atoi(value))) { ret = -1; + } else { + value[-1] = '='; + wpa_config_process_global(wpa_s->conf, cmd, -1); + } } else if (os_strcasecmp(cmd, "wps_fragment_size") == 0) { wpa_s->wps_fragment_size = atoi(value); #ifdef CONFIG_WPS_TESTING @@ -494,6 +581,59 @@ 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_DPP + } else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) { + os_free(wpa_s->dpp_configurator_params); + wpa_s->dpp_configurator_params = os_strdup(value); + } else if (os_strcasecmp(cmd, "dpp_init_max_tries") == 0) { + wpa_s->dpp_init_max_tries = atoi(value); + } else if (os_strcasecmp(cmd, "dpp_init_retry_time") == 0) { + wpa_s->dpp_init_retry_time = atoi(value); + } else if (os_strcasecmp(cmd, "dpp_resp_wait_time") == 0) { + wpa_s->dpp_resp_wait_time = atoi(value); + } else if (os_strcasecmp(cmd, "dpp_resp_max_tries") == 0) { + wpa_s->dpp_resp_max_tries = atoi(value); + } else if (os_strcasecmp(cmd, "dpp_resp_retry_time") == 0) { + wpa_s->dpp_resp_retry_time = atoi(value); +#ifdef CONFIG_TESTING_OPTIONS + } else if (os_strcasecmp(cmd, "dpp_pkex_own_mac_override") == 0) { + if (hwaddr_aton(value, dpp_pkex_own_mac_override)) + ret = -1; + } else if (os_strcasecmp(cmd, "dpp_pkex_peer_mac_override") == 0) { + if (hwaddr_aton(value, dpp_pkex_peer_mac_override)) + ret = -1; + } else if (os_strcasecmp(cmd, "dpp_pkex_ephemeral_key_override") == 0) { + size_t hex_len = os_strlen(value); + + if (hex_len > + 2 * sizeof(dpp_pkex_ephemeral_key_override)) + ret = -1; + else if (hexstr2bin(value, dpp_pkex_ephemeral_key_override, + hex_len / 2)) + ret = -1; + else + dpp_pkex_ephemeral_key_override_len = hex_len / 2; + } else if (os_strcasecmp(cmd, "dpp_protocol_key_override") == 0) { + size_t hex_len = os_strlen(value); + + if (hex_len > 2 * sizeof(dpp_protocol_key_override)) + ret = -1; + else if (hexstr2bin(value, dpp_protocol_key_override, + hex_len / 2)) + ret = -1; + else + dpp_protocol_key_override_len = hex_len / 2; + } else if (os_strcasecmp(cmd, "dpp_nonce_override") == 0) { + size_t hex_len = os_strlen(value); + + if (hex_len > 2 * sizeof(dpp_nonce_override)) + ret = -1; + else if (hexstr2bin(value, dpp_nonce_override, hex_len / 2)) + ret = -1; + else + dpp_nonce_override_len = hex_len / 2; +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_DPP */ #ifdef CONFIG_TESTING_OPTIONS } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) { wpa_s->ext_mgmt_frame_handling = !!atoi(value); @@ -515,9 +655,54 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, wpa_s->ignore_auth_resp = !!atoi(value); } else if (os_strcasecmp(cmd, "ignore_assoc_disallow") == 0) { wpa_s->ignore_assoc_disallow = !!atoi(value); + wpa_drv_ignore_assoc_disallow(wpa_s, + wpa_s->ignore_assoc_disallow); } else if (os_strcasecmp(cmd, "reject_btm_req_reason") == 0) { wpa_s->reject_btm_req_reason = atoi(value); + } else if (os_strcasecmp(cmd, "get_pref_freq_list_override") == 0) { + os_free(wpa_s->get_pref_freq_list_override); + if (!value[0]) + wpa_s->get_pref_freq_list_override = NULL; + else + wpa_s->get_pref_freq_list_override = os_strdup(value); + } else if (os_strcasecmp(cmd, "sae_commit_override") == 0) { + wpabuf_free(wpa_s->sae_commit_override); + if (value[0] == '\0') + wpa_s->sae_commit_override = NULL; + else + wpa_s->sae_commit_override = wpabuf_parse_bin(value); +#ifdef CONFIG_DPP + } else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) { + os_free(wpa_s->dpp_config_obj_override); + if (value[0] == '\0') + wpa_s->dpp_config_obj_override = NULL; + else + wpa_s->dpp_config_obj_override = os_strdup(value); + } else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) { + os_free(wpa_s->dpp_discovery_override); + if (value[0] == '\0') + wpa_s->dpp_discovery_override = NULL; + else + wpa_s->dpp_discovery_override = os_strdup(value); + } else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) { + os_free(wpa_s->dpp_groups_override); + if (value[0] == '\0') + wpa_s->dpp_groups_override = NULL; + else + wpa_s->dpp_groups_override = os_strdup(value); + } else if (os_strcasecmp(cmd, + "dpp_ignore_netaccesskey_mismatch") == 0) { + wpa_s->dpp_ignore_netaccesskey_mismatch = atoi(value); + } else if (os_strcasecmp(cmd, "dpp_test") == 0) { + dpp_test = atoi(value); +#endif /* CONFIG_DPP */ #endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_FILS + } else if (os_strcasecmp(cmd, "disable_fils") == 0) { + wpa_s->disable_fils = !!atoi(value); + wpa_drv_disable_fils(wpa_s, wpa_s->disable_fils); + wpa_supplicant_set_default_scan_ies(wpa_s); +#endif /* CONFIG_FILS */ #ifndef CONFIG_NO_CONFIG_BLOBS } else if (os_strcmp(cmd, "blob") == 0) { ret = wpas_ctrl_set_blob(wpa_s, value); @@ -527,11 +712,53 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, #ifdef CONFIG_MBO } else if (os_strcasecmp(cmd, "non_pref_chan") == 0) { ret = wpas_mbo_update_non_pref_chan(wpa_s, value); + if (ret == 0) { + value[-1] = '='; + wpa_config_process_global(wpa_s->conf, cmd, -1); + } } else if (os_strcasecmp(cmd, "mbo_cell_capa") == 0) { wpas_mbo_update_cell_capa(wpa_s, atoi(value)); + } else if (os_strcasecmp(cmd, "oce") == 0) { + wpa_s->conf->oce = atoi(value); + if (wpa_s->conf->oce) { + if ((wpa_s->conf->oce & OCE_STA) && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA)) + wpa_s->enable_oce = OCE_STA; + + if ((wpa_s->conf->oce & OCE_STA_CFON) && + (wpa_s->drv_flags & + WPA_DRIVER_FLAGS_OCE_STA_CFON)) { + /* TODO: Need to add STA-CFON support */ + wpa_printf(MSG_ERROR, + "OCE STA-CFON feature is not yet supported"); + return -1; + } + } else { + wpa_s->enable_oce = 0; + } + wpa_supplicant_set_default_scan_ies(wpa_s); #endif /* CONFIG_MBO */ } else if (os_strcasecmp(cmd, "lci") == 0) { ret = wpas_ctrl_iface_set_lci(wpa_s, value); + } else if (os_strcasecmp(cmd, "tdls_trigger_control") == 0) { + ret = wpa_drv_set_tdls_mode(wpa_s, atoi(value)); + } else if (os_strcasecmp(cmd, "relative_rssi") == 0) { + ret = wpas_ctrl_set_relative_rssi(wpa_s, value); + } else if (os_strcasecmp(cmd, "relative_band_adjust") == 0) { + ret = wpas_ctrl_set_relative_band_adjust(wpa_s, value); + } else if (os_strcasecmp(cmd, "ric_ies") == 0) { + ret = wpas_ctrl_iface_set_ric_ies(wpa_s, value); + } else if (os_strcasecmp(cmd, "roaming") == 0) { + ret = wpa_drv_roaming(wpa_s, atoi(value), NULL); +#ifdef CONFIG_WNM + } else if (os_strcasecmp(cmd, "coloc_intf_elems") == 0) { + struct wpabuf *elems; + + elems = wpabuf_parse_bin(value); + if (!elems) + return -1; + wnm_set_coloc_intf_elems(wpa_s, elems); +#endif /* CONFIG_WNM */ } else { value[-1] = '='; ret = wpa_config_process_global(wpa_s->conf, cmd, -1); @@ -577,6 +804,12 @@ static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s, #endif /* CONFIG_TESTING_GET_GTK */ } else if (os_strcmp(cmd, "tls_library") == 0) { res = tls_get_library_version(buf, buflen); +#ifdef CONFIG_TESTING_OPTIONS + } else if (os_strcmp(cmd, "anonce") == 0) { + return wpa_snprintf_hex(buf, buflen, + wpa_sm_get_anonce(wpa_s->wpa), + WPA_NONCE_LEN); +#endif /* CONFIG_TESTING_OPTIONS */ } else { res = wpa_config_get_value(cmd, wpa_s->conf, buf, buflen); } @@ -610,27 +843,6 @@ static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s, #endif /* IEEE8021X_EAPOL */ -#ifdef CONFIG_PEERKEY -/* MLME-STKSTART.request(peer) */ -static int wpa_supplicant_ctrl_iface_stkstart( - struct wpa_supplicant *wpa_s, char *addr) -{ - u8 peer[ETH_ALEN]; - - if (hwaddr_aton(addr, peer)) { - wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART: invalid " - "address '%s'", addr); - return -1; - } - - wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART " MACSTR, - MAC2STR(peer)); - - return wpa_sm_stkstart(wpa_s->wpa, peer); -} -#endif /* CONFIG_PEERKEY */ - - #ifdef CONFIG_TDLS static int wpa_supplicant_ctrl_iface_tdls_discover( @@ -1914,6 +2126,7 @@ 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_SME #ifdef CONFIG_SAE if (wpa_s->wpa_state >= WPA_ASSOCIATED && #ifdef CONFIG_AP @@ -1927,6 +2140,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, pos += ret; } #endif /* CONFIG_SAE */ +#endif /* CONFIG_SME */ ret = os_snprintf(pos, end - pos, "wpa_state=%s\n", wpa_supplicant_state_txt(wpa_s->wpa_state)); if (os_snprintf_error(end - pos, ret)) @@ -2048,6 +2262,12 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, pos += res; } +#ifdef CONFIG_MACSEC + res = ieee802_1x_kay_get_status(wpa_s->kay, pos, end - pos); + if (res > 0) + pos += res; +#endif /* CONFIG_MACSEC */ + sess_id = eapol_sm_get_session_id(wpa_s->eapol, &sess_id_len); if (sess_id) { char *start = pos; @@ -2081,6 +2301,13 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_WPS */ + if (wpa_s->ieee80211ac) { + ret = os_snprintf(pos, end - pos, "ieee80211ac=1\n"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + #ifdef ANDROID /* * Allow using the STATUS command with default behavior, say for debug, @@ -2437,6 +2664,59 @@ static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto, } #endif /* CONFIG_SUITEB192 */ +#ifdef CONFIG_FILS + if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA256) { + ret = os_snprintf(pos, end - pos, "%sFILS-SHA256", + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) + return pos; + pos += ret; + } + if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA384) { + ret = os_snprintf(pos, end - pos, "%sFILS-SHA384", + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) + return pos; + pos += ret; + } +#ifdef CONFIG_IEEE80211R + if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) { + ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA256", + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) + return pos; + pos += ret; + } + if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) { + ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA384", + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) + return pos; + pos += ret; + } +#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE + if (data.key_mgmt & WPA_KEY_MGMT_OWE) { + ret = os_snprintf(pos, end - pos, "%sOWE", + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) + return pos; + pos += ret; + } +#endif /* CONFIG_OWE */ + +#ifdef CONFIG_DPP + if (data.key_mgmt & WPA_KEY_MGMT_DPP) { + ret = os_snprintf(pos, end - pos, "%sDPP", + pos == start ? "" : "+"); + if (os_snprintf_error(end - pos, ret)) + return pos; + pos += ret; + } +#endif /* CONFIG_DPP */ + if (data.key_mgmt & WPA_KEY_MGMT_OSEN) { ret = os_snprintf(pos, end - pos, "%sOSEN", pos == start ? "" : "+"); @@ -2512,7 +2792,7 @@ static int wpa_supplicant_ctrl_iface_scan_result( { char *pos, *end; int ret; - const u8 *ie, *ie2, *osen_ie, *p2p, *mesh; + const u8 *ie, *ie2, *osen_ie, *p2p, *mesh, *owe; mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID); p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE); @@ -2543,6 +2823,14 @@ static int wpa_supplicant_ctrl_iface_scan_result( if (osen_ie) pos = wpa_supplicant_ie_txt(pos, end, "OSEN", osen_ie, 2 + osen_ie[1]); + owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE); + if (owe) { + ret = os_snprintf(pos, end - pos, + ie2 ? "[OWE-TRANS]" : "[OWE-TRANS-OPEN]"); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); if (!ie && !ie2 && !osen_ie && (bss->caps & IEEE80211_CAP_PRIVACY)) { ret = os_snprintf(pos, end - pos, "[WEP]"); @@ -2608,6 +2896,14 @@ static int wpa_supplicant_ctrl_iface_scan_result( pos += ret; } #endif /* CONFIG_HS20 */ +#ifdef CONFIG_FILS + if (wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION)) { + ret = os_snprintf(pos, end - pos, "[FILS]"); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } +#endif /* CONFIG_FILS */ #ifdef CONFIG_FST if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) { ret = os_snprintf(pos, end - pos, "[FST]"); @@ -2835,9 +3131,8 @@ static int wpa_supplicant_ctrl_iface_select_network( 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; + os_free(wpa_s->select_network_scan_freqs); + wpa_s->select_network_scan_freqs = freqs; } } @@ -3012,6 +3307,7 @@ static int wpa_supplicant_ctrl_iface_update_network( return 0; /* No change to the previously configured value */ if (os_strcmp(name, "bssid") != 0 && + os_strcmp(name, "bssid_hint") != 0 && os_strcmp(name, "priority") != 0) { wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); @@ -3647,6 +3943,50 @@ static int ctrl_iface_get_capability_key_mgmt(int res, char *strict, pos += ret; } #endif /* CONFIG_SUITEB192 */ +#ifdef CONFIG_OWE + if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OWE) { + ret = os_snprintf(pos, end - pos, " OWE"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_DPP) { + ret = os_snprintf(pos, end - pos, " DPP"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_DPP */ +#ifdef CONFIG_FILS + if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256) { + ret = os_snprintf(pos, end - pos, " FILS-SHA256"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384) { + ret = os_snprintf(pos, end - pos, " FILS-SHA384"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#ifdef CONFIG_IEEE80211R + if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256) { + ret = os_snprintf(pos, end - pos, " FT-FILS-SHA256"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384) { + ret = os_snprintf(pos, end - pos, " FT-FILS-SHA384"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_FILS */ return pos - buf; } @@ -3749,6 +4089,26 @@ static int ctrl_iface_get_capability_auth_alg(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + if (wpa_is_fils_supported(wpa_s)) { + ret = os_snprintf(pos, end - pos, "%sFILS_SK_WITHOUT_PFS", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + +#ifdef CONFIG_FILS_SK_PFS + if (wpa_is_fils_sk_pfs_supported(wpa_s)) { + ret = os_snprintf(pos, end - pos, "%sFILS_SK_WITH_PFS", + pos == buf ? "" : " "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_FILS_SK_PFS */ +#endif /* CONFIG_FILS */ + return pos - buf; } @@ -4006,6 +4366,27 @@ static int wpa_supplicant_ctrl_iface_get_capability( } #endif /* CONFIG_ACS */ +#ifdef CONFIG_FILS + if (os_strcmp(field, "fils") == 0) { +#ifdef CONFIG_FILS_SK_PFS + if (wpa_is_fils_supported(wpa_s) && + wpa_is_fils_sk_pfs_supported(wpa_s)) { + res = os_snprintf(buf, buflen, "FILS FILS-SK-PFS"); + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } +#endif /* CONFIG_FILS_SK_PFS */ + + if (wpa_is_fils_supported(wpa_s)) { + res = os_snprintf(buf, buflen, "FILS"); + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } + } +#endif /* CONFIG_FILS */ + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", field); @@ -4048,13 +4429,85 @@ static char * anqp_add_hex(char *pos, char *end, const char *title, #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_FILS +static int print_fils_indication(struct wpa_bss *bss, char *pos, char *end) +{ + char *start = pos; + const u8 *ie, *ie_end; + u16 info, realms; + int ret; + + ie = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION); + if (!ie) + return 0; + ie_end = ie + 2 + ie[1]; + ie += 2; + if (ie_end - ie < 2) + return -1; + + info = WPA_GET_LE16(ie); + ie += 2; + ret = os_snprintf(pos, end - pos, "fils_info=%04x\n", info); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + + if (info & BIT(7)) { + /* Cache Identifier Included */ + if (ie_end - ie < 2) + return -1; + ret = os_snprintf(pos, end - pos, "fils_cache_id=%02x%02x\n", + ie[0], ie[1]); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + ie += 2; + } + + if (info & BIT(8)) { + /* HESSID Included */ + if (ie_end - ie < ETH_ALEN) + return -1; + ret = os_snprintf(pos, end - pos, "fils_hessid=" MACSTR "\n", + MAC2STR(ie)); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + ie += ETH_ALEN; + } + + realms = (info & (BIT(3) | BIT(4) | BIT(5))) >> 3; + if (realms) { + if (ie_end - ie < realms * 2) + return -1; + ret = os_snprintf(pos, end - pos, "fils_realms="); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + + ret = wpa_snprintf_hex(pos, end - pos, ie, realms * 2); + if (ret <= 0) + return 0; + pos += ret; + ie += realms * 2; + ret = os_snprintf(pos, end - pos, "\n"); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + } + + return pos - start; +} +#endif /* CONFIG_FILS */ + + static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, unsigned long mask, char *buf, size_t buflen) { size_t i; int ret; char *pos, *end; - const u8 *ie, *ie2, *osen_ie; + const u8 *ie, *ie2, *osen_ie, *mesh, *owe; pos = buf; end = buf + buflen; @@ -4163,18 +4616,30 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, return 0; pos += ret; + mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID); + 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, + pos = wpa_supplicant_ie_txt(pos, end, + mesh ? "RSN" : "WPA2", ie2, 2 + ie2[1]); osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE); if (osen_ie) pos = wpa_supplicant_ie_txt(pos, end, "OSEN", osen_ie, 2 + osen_ie[1]); + owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE); + if (owe) { + ret = os_snprintf( + pos, end - pos, + ie2 ? "[OWE-TRANS]" : "[OWE-TRANS-OPEN]"); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + } pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); if (!ie && !ie2 && !osen_ie && (bss->caps & IEEE80211_CAP_PRIVACY)) { @@ -4183,6 +4648,14 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, return 0; pos += ret; } + + if (mesh) { + ret = os_snprintf(pos, end - pos, "[MESH]"); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + } + if (bss_is_dmg(bss)) { const char *s; ret = os_snprintf(pos, end - pos, "[DMG]"); @@ -4236,6 +4709,14 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, pos += ret; } #endif /* CONFIG_HS20 */ +#ifdef CONFIG_FILS + if (wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION)) { + ret = os_snprintf(pos, end - pos, "[FILS]"); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + } +#endif /* CONFIG_FILS */ ret = os_snprintf(pos, end - pos, "\n"); if (os_snprintf_error(end - pos, ret)) @@ -4320,6 +4801,8 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, pos = anqp_add_hex(pos, end, "anqp_3gpp", anqp->anqp_3gpp); pos = anqp_add_hex(pos, end, "anqp_domain_name", anqp->domain_name); + pos = anqp_add_hex(pos, end, "anqp_fils_realm_info", + anqp->fils_realm_info); #ifdef CONFIG_HS20 pos = anqp_add_hex(pos, end, "hs20_capability_list", anqp->hs20_capability_list); @@ -4333,6 +4816,10 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, anqp->hs20_operating_class); pos = anqp_add_hex(pos, end, "hs20_osu_providers_list", anqp->hs20_osu_providers_list); + pos = anqp_add_hex(pos, end, "hs20_operator_icon_metadata", + anqp->hs20_operator_icon_metadata); + pos = anqp_add_hex(pos, end, "hs20_osu_providers_nai_list", + anqp->hs20_osu_providers_nai_list); #endif /* CONFIG_HS20 */ dl_list_for_each(elem, &anqp->anqp_elems, @@ -4381,6 +4868,44 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, } #endif /* CONFIG_FST */ + if (mask & WPA_BSS_MASK_UPDATE_IDX) { + ret = os_snprintf(pos, end - pos, "update_idx=%u\n", + bss->last_update_idx); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + } + + if ((mask & WPA_BSS_MASK_BEACON_IE) && bss->beacon_ie_len) { + ret = os_snprintf(pos, end - pos, "beacon_ie="); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + + ie = (const u8 *) (bss + 1); + ie += bss->ie_len; + for (i = 0; i < bss->beacon_ie_len; i++) { + ret = os_snprintf(pos, end - pos, "%02x", *ie++); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + } + + ret = os_snprintf(pos, end - pos, "\n"); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + } + +#ifdef CONFIG_FILS + if (mask & WPA_BSS_MASK_FILS_INDICATION) { + ret = print_fils_indication(bss, pos, end); + if (ret < 0) + return 0; + pos += ret; + } +#endif /* CONFIG_FILS */ + if (mask & WPA_BSS_MASK_DELIM) { ret = os_snprintf(pos, end - pos, "====\n"); if (os_snprintf_error(end - pos, ret)) @@ -4471,6 +4996,8 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, bss = dl_list_entry(next, struct wpa_bss, list_id); } + } else if (os_strncmp(cmd, "CURRENT", 7) == 0) { + bss = wpa_s->current_bss; #ifdef CONFIG_P2P } else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) { if (hwaddr_aton(cmd + 13, bssid) == 0) @@ -5768,13 +6295,21 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) int ht40 = wpa_s->conf->p2p_go_ht40 || vht; int max_oper_chwidth, chwidth = 0, freq2 = 0; char *token, *context = NULL; +#ifdef CONFIG_ACS + int acs = 0; +#endif /* CONFIG_ACS */ while ((token = str_token(cmd, " ", &context))) { - if (sscanf(token, "freq=%d", &freq) == 1 || - sscanf(token, "freq2=%d", &freq2) == 1 || + if (sscanf(token, "freq2=%d", &freq2) == 1 || sscanf(token, "persistent=%d", &group_id) == 1 || sscanf(token, "max_oper_chwidth=%d", &chwidth) == 1) { continue; +#ifdef CONFIG_ACS + } else if (os_strcmp(token, "freq=acs") == 0) { + acs = 1; +#endif /* CONFIG_ACS */ + } else if (sscanf(token, "freq=%d", &freq) == 1) { + continue; } else if (os_strcmp(token, "ht40") == 0) { ht40 = 1; } else if (os_strcmp(token, "vht") == 0) { @@ -5790,6 +6325,24 @@ static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd) } } +#ifdef CONFIG_ACS + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) && + (acs || freq == 2 || freq == 5)) { + if (freq == 2 && wpa_s->best_24_freq <= 0) { + wpa_s->p2p_go_acs_band = HOSTAPD_MODE_IEEE80211G; + wpa_s->p2p_go_do_acs = 1; + freq = 0; + } else if (freq == 5 && wpa_s->best_5_freq <= 0) { + wpa_s->p2p_go_acs_band = HOSTAPD_MODE_IEEE80211A; + wpa_s->p2p_go_do_acs = 1; + freq = 0; + } else { + wpa_s->p2p_go_acs_band = HOSTAPD_MODE_IEEE80211ANY; + wpa_s->p2p_go_do_acs = 1; + } + } +#endif /* CONFIG_ACS */ + max_oper_chwidth = parse_freq(chwidth, freq2); if (max_oper_chwidth < 0) return -1; @@ -5827,10 +6380,24 @@ static int p2p_ctrl_group_member(struct wpa_supplicant *wpa_s, const char *cmd, } +static int wpas_find_p2p_dev_addr_bss(struct wpa_global *global, + const u8 *p2p_dev_addr) +{ + struct wpa_supplicant *wpa_s; + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (wpa_bss_get_p2p_dev_addr(wpa_s, p2p_dev_addr)) + return 1; + } + + return 0; +} + + static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { - u8 addr[ETH_ALEN], *addr_ptr; + u8 addr[ETH_ALEN], *addr_ptr, group_capab; int next, res; const struct p2p_peer_info *info; char *pos, *end; @@ -5859,6 +6426,16 @@ static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd, info = p2p_get_peer_info(wpa_s->global->p2p, addr_ptr, next); if (info == NULL) return -1; + group_capab = info->group_capab; + + if (group_capab && + !wpas_find_p2p_dev_addr_bss(wpa_s->global, info->p2p_device_addr)) { + wpa_printf(MSG_DEBUG, + "P2P: Could not find any BSS with p2p_dev_addr " + MACSTR ", hence override group_capab from 0x%x to 0", + MAC2STR(info->p2p_device_addr), group_capab); + group_capab = 0; + } pos = buf; end = buf + buflen; @@ -5884,7 +6461,7 @@ static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd, info->serial_number, info->config_methods, info->dev_capab, - info->group_capab, + group_capab, info->level); if (os_snprintf_error(end - pos, res)) return pos - buf; @@ -6165,6 +6742,20 @@ static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd) return 0; } + if (os_strcmp(cmd, "override_pref_op_chan") == 0) { + int op_class, chan; + + op_class = atoi(param); + param = os_strchr(param, ':'); + if (!param) + return -1; + param++; + chan = atoi(param); + p2p_set_override_pref_op_chan(wpa_s->global->p2p, op_class, + chan); + return 0; + } + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'", cmd); @@ -6176,6 +6767,12 @@ 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; + +#ifdef CONFIG_TESTING_OPTIONS + os_free(wpa_s->get_pref_freq_list_override); + wpa_s->get_pref_freq_list_override = NULL; +#endif /* CONFIG_TESTING_OPTIONS */ + wpas_p2p_stop_find(wpa_s); wpa_s->parent->p2ps_method_config_any = 0; if (wpa_s->global->p2p) @@ -6383,7 +6980,7 @@ static int get_anqp(struct wpa_supplicant *wpa_s, char *dst) u16 id[MAX_ANQP_INFO_ID]; size_t num_id = 0; u32 subtypes = 0; - int get_cell_pref = 0; + u32 mbo_subtypes = 0; used = hwaddr_aton2(dst, dst_addr); if (used < 0) @@ -6404,9 +7001,10 @@ static int get_anqp(struct wpa_supplicant *wpa_s, char *dst) } else if (os_strncmp(pos, "mbo:", 4) == 0) { #ifdef CONFIG_MBO int num = atoi(pos + 4); - if (num != MBO_ANQP_SUBTYPE_CELL_CONN_PREF) + + if (num <= 0 || num > MAX_MBO_ANQP_SUBTYPE) return -1; - get_cell_pref = 1; + mbo_subtypes |= BIT(num); #else /* CONFIG_MBO */ return -1; #endif /* CONFIG_MBO */ @@ -6421,11 +7019,11 @@ static int get_anqp(struct wpa_supplicant *wpa_s, char *dst) pos++; } - if (num_id == 0) + if (num_id == 0 && !subtypes && !mbo_subtypes) return -1; return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes, - get_cell_pref); + mbo_subtypes); } @@ -6762,6 +7360,9 @@ static int wpa_supplicant_ctrl_iface_autoscan(struct wpa_supplicant *wpa_s, autoscan_init(wpa_s, 1); else if (state == WPA_SCANNING) wpa_supplicant_reinit_autoscan(wpa_s); + else + wpa_printf(MSG_DEBUG, "No autoscan update in state %s", + wpa_supplicant_state_txt(state)); return 0; } @@ -6824,26 +7425,41 @@ static int wpas_ctrl_iface_wnm_sleep(struct wpa_supplicant *wpa_s, char *cmd) static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd) { int query_reason, list = 0; + char *btm_candidates = NULL; query_reason = atoi(cmd); cmd = os_strchr(cmd, ' '); if (cmd) { - cmd++; - if (os_strncmp(cmd, "list", 4) == 0) { + if (os_strncmp(cmd, " list", 5) == 0) list = 1; - } else { - wpa_printf(MSG_DEBUG, "WNM Query: Invalid option %s", - cmd); - return -1; - } + else + btm_candidates = cmd; } wpa_printf(MSG_DEBUG, "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d%s", query_reason, list ? " candidate list" : ""); - return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason, list); + return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason, + btm_candidates, + list); +} + + +static int wpas_ctrl_iface_coloc_intf_report(struct wpa_supplicant *wpa_s, + char *cmd) +{ + struct wpabuf *elems; + int ret; + + elems = wpabuf_parse_bin(cmd); + if (!elems) + return -1; + + ret = wnm_send_coloc_intf_report(wpa_s, 0, elems); + wpabuf_free(elems); + return ret; } #endif /* CONFIG_WNM */ @@ -6879,10 +7495,17 @@ static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf, 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 (si.center_frq1 > 0) { + ret = os_snprintf(pos, end - pos, "CENTER_FRQ1=%d\n", + si.center_frq1); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + + if (si.center_frq2 > 0) { + ret = os_snprintf(pos, end - pos, "CENTER_FRQ2=%d\n", + si.center_frq2); if (os_snprintf_error(end - pos, ret)) return -1; pos += ret; @@ -6930,6 +7553,46 @@ static int wpas_ctrl_iface_signal_monitor(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_TESTING_OPTIONS +int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s, + enum wpa_driver_if_type if_type, + unsigned int *num, + unsigned int *freq_list) +{ + char *pos = wpa_s->get_pref_freq_list_override; + char *end; + unsigned int count = 0; + + /* Override string format: + * <if_type1>:<freq1>,<freq2>,... <if_type2>:... */ + + while (pos) { + if (atoi(pos) == (int) if_type) + break; + pos = os_strchr(pos, ' '); + if (pos) + pos++; + } + if (!pos) + return -1; + pos = os_strchr(pos, ':'); + if (!pos) + return -1; + pos++; + end = os_strchr(pos, ' '); + while (pos && (!end || pos < end) && count < *num) { + freq_list[count++] = atoi(pos); + pos = os_strchr(pos, ','); + if (pos) + pos++; + } + + *num = count; + return 0; +} +#endif /* CONFIG_TESTING_OPTIONS */ + + static int wpas_ctrl_iface_get_pref_freq_list( struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen) { @@ -7116,7 +7779,8 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state"); - wpas_abort_ongoing_scan(wpa_s); + if (wpas_abort_ongoing_scan(wpa_s) == 0) + wpa_s->ignore_post_flush_scan_res = 1; if (wpa_s->wpa_state >= WPA_AUTHENTICATING) { /* @@ -7157,6 +7821,22 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) wpa_s->after_wps = 0; wpa_s->known_wps_freq = 0; +#ifdef CONFIG_DPP + wpas_dpp_deinit(wpa_s); + wpa_s->dpp_init_max_tries = 0; + wpa_s->dpp_init_retry_time = 0; + wpa_s->dpp_resp_wait_time = 0; + wpa_s->dpp_resp_max_tries = 0; + wpa_s->dpp_resp_retry_time = 0; +#ifdef CONFIG_TESTING_OPTIONS + os_memset(dpp_pkex_own_mac_override, 0, ETH_ALEN); + os_memset(dpp_pkex_peer_mac_override, 0, ETH_ALEN); + dpp_pkex_ephemeral_key_override_len = 0; + dpp_protocol_key_override_len = 0; + dpp_nonce_override_len = 0; +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_DPP */ + #ifdef CONFIG_TDLS #ifdef CONFIG_TDLS_TESTING tdls_testing = 0; @@ -7218,13 +7898,29 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) wpa_s->p2p_go_csa_on_inv = 0; wpa_s->ignore_auth_resp = 0; wpa_s->ignore_assoc_disallow = 0; + wpa_s->testing_resend_assoc = 0; wpa_s->reject_btm_req_reason = 0; wpa_sm_set_test_assoc_ie(wpa_s->wpa, NULL); + os_free(wpa_s->get_pref_freq_list_override); + wpa_s->get_pref_freq_list_override = NULL; + wpabuf_free(wpa_s->sae_commit_override); + wpa_s->sae_commit_override = NULL; +#ifdef CONFIG_DPP + os_free(wpa_s->dpp_config_obj_override); + wpa_s->dpp_config_obj_override = NULL; + os_free(wpa_s->dpp_discovery_override); + wpa_s->dpp_discovery_override = NULL; + os_free(wpa_s->dpp_groups_override); + wpa_s->dpp_groups_override = NULL; + dpp_test = DPP_TEST_DISABLED; +#endif /* CONFIG_DPP */ #endif /* CONFIG_TESTING_OPTIONS */ wpa_s->disconnected = 0; os_free(wpa_s->next_scan_freqs); wpa_s->next_scan_freqs = NULL; + os_free(wpa_s->select_network_scan_freqs); + wpa_s->select_network_scan_freqs = NULL; wpa_bss_flush(wpa_s); if (!dl_list_empty(&wpa_s->bss)) { @@ -7242,6 +7938,9 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) #ifdef CONFIG_SME wpa_s->sme.last_unprot_disconnect.sec = 0; #endif /* CONFIG_SME */ + + wpabuf_free(wpa_s->ric_ies); + wpa_s->ric_ies = NULL; } @@ -7539,6 +8238,19 @@ static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params, goto done; } + pos = os_strstr(params, "bssid="); + if (pos) { + u8 bssid[ETH_ALEN]; + + pos += 6; + if (hwaddr_aton(pos, bssid)) { + wpa_printf(MSG_ERROR, "Invalid BSSID %s", pos); + *reply_len = -1; + goto done; + } + os_memcpy(wpa_s->next_scan_bssid, bssid, ETH_ALEN); + } + pos = params; while (pos && *pos != '\0') { if (os_strncmp(pos, "ssid ", 5) == 0) { @@ -7824,6 +8536,124 @@ static int wpas_ctrl_iface_mgmt_rx_process(struct wpa_supplicant *wpa_s, } +static int wpas_ctrl_iface_driver_scan_res(struct wpa_supplicant *wpa_s, + char *param) +{ + struct wpa_scan_res *res; + struct os_reltime now; + char *pos, *end; + int ret = -1; + + if (!param) + return -1; + + if (os_strcmp(param, "START") == 0) { + wpa_bss_update_start(wpa_s); + return 0; + } + + if (os_strcmp(param, "END") == 0) { + wpa_bss_update_end(wpa_s, NULL, 1); + return 0; + } + + if (os_strncmp(param, "BSS ", 4) != 0) + return -1; + param += 3; + + res = os_zalloc(sizeof(*res) + os_strlen(param) / 2); + if (!res) + return -1; + + pos = os_strstr(param, " flags="); + if (pos) + res->flags = strtol(pos + 7, NULL, 16); + + pos = os_strstr(param, " bssid="); + if (pos && hwaddr_aton(pos + 7, res->bssid)) + goto fail; + + pos = os_strstr(param, " freq="); + if (pos) + res->freq = atoi(pos + 6); + + pos = os_strstr(param, " beacon_int="); + if (pos) + res->beacon_int = atoi(pos + 12); + + pos = os_strstr(param, " caps="); + if (pos) + res->caps = strtol(pos + 6, NULL, 16); + + pos = os_strstr(param, " qual="); + if (pos) + res->qual = atoi(pos + 6); + + pos = os_strstr(param, " noise="); + if (pos) + res->noise = atoi(pos + 7); + + pos = os_strstr(param, " level="); + if (pos) + res->level = atoi(pos + 7); + + pos = os_strstr(param, " tsf="); + if (pos) + res->tsf = strtoll(pos + 5, NULL, 16); + + pos = os_strstr(param, " age="); + if (pos) + res->age = atoi(pos + 5); + + pos = os_strstr(param, " est_throughput="); + if (pos) + res->est_throughput = atoi(pos + 16); + + pos = os_strstr(param, " snr="); + if (pos) + res->snr = atoi(pos + 5); + + pos = os_strstr(param, " parent_tsf="); + if (pos) + res->parent_tsf = strtoll(pos + 7, NULL, 16); + + pos = os_strstr(param, " tsf_bssid="); + if (pos && hwaddr_aton(pos + 11, res->tsf_bssid)) + goto fail; + + pos = os_strstr(param, " ie="); + if (pos) { + pos += 4; + end = os_strchr(pos, ' '); + if (!end) + end = pos + os_strlen(pos); + res->ie_len = (end - pos) / 2; + if (hexstr2bin(pos, (u8 *) (res + 1), res->ie_len)) + goto fail; + } + + pos = os_strstr(param, " beacon_ie="); + if (pos) { + pos += 11; + end = os_strchr(pos, ' '); + if (!end) + end = pos + os_strlen(pos); + res->beacon_ie_len = (end - pos) / 2; + if (hexstr2bin(pos, ((u8 *) (res + 1)) + res->ie_len, + res->beacon_ie_len)) + goto fail; + } + + os_get_reltime(&now); + wpa_bss_update_scan_res(wpa_s, res, &now); + ret = 0; +fail: + os_free(res); + + return ret; +} + + static int wpas_ctrl_iface_driver_event(struct wpa_supplicant *wpa_s, char *cmd) { char *pos, *param; @@ -7854,6 +8684,8 @@ static int wpas_ctrl_iface_driver_event(struct wpa_supplicant *wpa_s, char *cmd) wpa_supplicant_event(wpa_s, ev, &event); os_free(event.freq_range.range); return 0; + } else if (os_strcmp(cmd, "SCAN_RES") == 0) { + return wpas_ctrl_iface_driver_scan_res(wpa_s, param); } else { wpa_dbg(wpa_s, MSG_DEBUG, "Testing - unknown driver event: %s", cmd); @@ -8218,6 +9050,79 @@ static int wpas_ctrl_test_assoc_ie(struct wpa_supplicant *wpa_s, return 0; } + +static int wpas_ctrl_reset_pn(struct wpa_supplicant *wpa_s) +{ + u8 zero[WPA_TK_MAX_LEN]; + + if (wpa_s->last_tk_alg == WPA_ALG_NONE) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Reset PN"); + os_memset(zero, 0, sizeof(zero)); + + /* First, use a zero key to avoid any possible duplicate key avoidance + * in the driver. */ + if (wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr, + wpa_s->last_tk_key_idx, 1, zero, 6, + zero, wpa_s->last_tk_len) < 0) + return -1; + + /* Set the previously configured key to reset its TSC/RSC */ + return wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr, + wpa_s->last_tk_key_idx, 1, zero, 6, + wpa_s->last_tk, wpa_s->last_tk_len); +} + + +static int wpas_ctrl_key_request(struct wpa_supplicant *wpa_s, const char *cmd) +{ + const char *pos = cmd; + int error, pairwise; + + error = atoi(pos); + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pairwise = atoi(pos); + wpa_sm_key_request(wpa_s->wpa, error, pairwise); + return 0; +} + + +static int wpas_ctrl_resend_assoc(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_SME + struct wpa_driver_associate_params params; + int ret; + + os_memset(¶ms, 0, sizeof(params)); + params.bssid = wpa_s->bssid; + params.ssid = wpa_s->sme.ssid; + params.ssid_len = wpa_s->sme.ssid_len; + params.freq.freq = wpa_s->sme.freq; + if (wpa_s->last_assoc_req_wpa_ie) { + params.wpa_ie = wpabuf_head(wpa_s->last_assoc_req_wpa_ie); + params.wpa_ie_len = wpabuf_len(wpa_s->last_assoc_req_wpa_ie); + } + params.pairwise_suite = wpa_s->pairwise_cipher; + params.group_suite = wpa_s->group_cipher; + params.mgmt_group_suite = wpa_s->mgmt_group_cipher; + params.key_mgmt_suite = wpa_s->key_mgmt; + params.wpa_proto = wpa_s->wpa_proto; + 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_printf(MSG_INFO, "TESTING: Resend association request"); + ret = wpa_drv_associate(wpa_s, ¶ms); + wpa_s->testing_resend_assoc = 1; + return ret; +#else /* CONFIG_SME */ + return -1; +#endif /* CONFIG_SME */ +} + #endif /* CONFIG_TESTING_OPTIONS */ @@ -8641,6 +9546,248 @@ static void wpas_ctrl_iface_pmksa_flush(struct wpa_supplicant *wpa_s) } +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL + +static int wpas_ctrl_iface_pmksa_get(struct wpa_supplicant *wpa_s, + const char *cmd, char *buf, size_t buflen) +{ + struct rsn_pmksa_cache_entry *entry; + struct wpa_ssid *ssid; + char *pos, *pos2, *end; + int ret; + struct os_reltime now; + + ssid = wpa_config_get_network(wpa_s->conf, atoi(cmd)); + if (!ssid) + return -1; + + pos = buf; + end = buf + buflen; + + os_get_reltime(&now); + + /* + * Entry format: + * <BSSID> <PMKID> <PMK> <reauth_time in seconds> + * <expiration in seconds> <akmp> <opportunistic> + * [FILS Cache Identifier] + */ + + for (entry = wpa_sm_pmksa_cache_head(wpa_s->wpa); entry; + entry = entry->next) { + if (entry->network_ctx != ssid) + continue; + + pos2 = pos; + ret = os_snprintf(pos2, end - pos2, MACSTR " ", + MAC2STR(entry->aa)); + if (os_snprintf_error(end - pos2, ret)) + break; + pos2 += ret; + + pos2 += wpa_snprintf_hex(pos2, end - pos2, entry->pmkid, + PMKID_LEN); + + ret = os_snprintf(pos2, end - pos2, " "); + if (os_snprintf_error(end - pos2, ret)) + break; + pos2 += ret; + + pos2 += wpa_snprintf_hex(pos2, end - pos2, entry->pmk, + entry->pmk_len); + + ret = os_snprintf(pos2, end - pos2, " %d %d %d %d", + (int) (entry->reauth_time - now.sec), + (int) (entry->expiration - now.sec), + entry->akmp, + entry->opportunistic); + if (os_snprintf_error(end - pos2, ret)) + break; + pos2 += ret; + + if (entry->fils_cache_id_set) { + ret = os_snprintf(pos2, end - pos2, " %02x%02x", + entry->fils_cache_id[0], + entry->fils_cache_id[1]); + if (os_snprintf_error(end - pos2, ret)) + break; + pos2 += ret; + } + + ret = os_snprintf(pos2, end - pos2, "\n"); + if (os_snprintf_error(end - pos2, ret)) + break; + pos2 += ret; + + pos = pos2; + } + + return pos - buf; +} + + +static int wpas_ctrl_iface_pmksa_add(struct wpa_supplicant *wpa_s, + char *cmd) +{ + struct rsn_pmksa_cache_entry *entry; + struct wpa_ssid *ssid; + char *pos, *pos2; + int ret = -1; + struct os_reltime now; + int reauth_time = 0, expiration = 0, i; + + /* + * Entry format: + * <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds> + * <expiration in seconds> <akmp> <opportunistic> + * [FILS Cache Identifier] + */ + + ssid = wpa_config_get_network(wpa_s->conf, atoi(cmd)); + if (!ssid) + return -1; + + pos = os_strchr(cmd, ' '); + if (!pos) + return -1; + pos++; + + entry = os_zalloc(sizeof(*entry)); + if (!entry) + return -1; + + if (hwaddr_aton(pos, entry->aa)) + goto fail; + + pos = os_strchr(pos, ' '); + if (!pos) + goto fail; + pos++; + + if (hexstr2bin(pos, entry->pmkid, PMKID_LEN) < 0) + goto fail; + + pos = os_strchr(pos, ' '); + if (!pos) + goto fail; + pos++; + + pos2 = os_strchr(pos, ' '); + if (!pos2) + goto fail; + entry->pmk_len = (pos2 - pos) / 2; + if (entry->pmk_len < PMK_LEN || entry->pmk_len > PMK_LEN_MAX || + hexstr2bin(pos, entry->pmk, entry->pmk_len) < 0) + goto fail; + + pos = os_strchr(pos, ' '); + if (!pos) + goto fail; + pos++; + + if (sscanf(pos, "%d %d %d %d", &reauth_time, &expiration, + &entry->akmp, &entry->opportunistic) != 4) + goto fail; + for (i = 0; i < 4; i++) { + pos = os_strchr(pos, ' '); + if (!pos) { + if (i < 3) + goto fail; + break; + } + pos++; + } + if (pos) { + if (hexstr2bin(pos, entry->fils_cache_id, + FILS_CACHE_ID_LEN) < 0) + goto fail; + entry->fils_cache_id_set = 1; + } + os_get_reltime(&now); + entry->expiration = now.sec + expiration; + entry->reauth_time = now.sec + reauth_time; + + entry->network_ctx = ssid; + + wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry); + entry = NULL; + ret = 0; +fail: + os_free(entry); + return ret; +} + + +#ifdef CONFIG_MESH + +static int wpas_ctrl_iface_mesh_pmksa_get(struct wpa_supplicant *wpa_s, + const char *cmd, char *buf, + size_t buflen) +{ + u8 spa[ETH_ALEN]; + + if (!wpa_s->ifmsh) + return -1; + + if (os_strcasecmp(cmd, "any") == 0) + return wpas_ap_pmksa_cache_list_mesh(wpa_s, NULL, buf, buflen); + + if (hwaddr_aton(cmd, spa)) + return -1; + + return wpas_ap_pmksa_cache_list_mesh(wpa_s, spa, buf, buflen); +} + + +static int wpas_ctrl_iface_mesh_pmksa_add(struct wpa_supplicant *wpa_s, + char *cmd) +{ + /* + * We do not check mesh interface existance because PMKSA should be + * stored before wpa_s->ifmsh creation to suppress commit message + * creation. + */ + return wpas_ap_pmksa_cache_add_external(wpa_s, cmd); +} + +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ + + +#ifdef CONFIG_FILS +static int wpas_ctrl_iface_fils_hlp_req_add(struct wpa_supplicant *wpa_s, + const char *cmd) +{ + struct fils_hlp_req *req; + const char *pos; + + /* format: <dst> <packet starting from ethertype> */ + + req = os_zalloc(sizeof(*req)); + if (!req) + return -1; + + if (hwaddr_aton(cmd, req->dst)) + goto fail; + + pos = os_strchr(cmd, ' '); + if (!pos) + goto fail; + pos++; + req->pkt = wpabuf_parse_bin(pos); + if (!req->pkt) + goto fail; + + dl_list_add_tail(&wpa_s->fils_hlp_req, &req->list); + return 0; +fail: + wpabuf_free(req->pkt); + os_free(req); + return -1; +} +#endif /* CONFIG_FILS */ + + static int wpas_ctrl_cmd_debug_level(const char *cmd) { if (os_strcmp(cmd, "PING") == 0 || @@ -8662,7 +9809,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, 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, "SET_NETWORK ", 12) == 0 || + os_strncmp(buf, "PMKSA_ADD ", 10) == 0 || + os_strncmp(buf, "MESH_PMKSA_ADD ", 15) == 0) { if (wpa_debug_show_keys) wpa_dbg(wpa_s, MSG_DEBUG, "Control interface command '%s'", buf); @@ -8671,7 +9820,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, "Control interface command '%s [REMOVED]'", os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ? - WPA_CTRL_RSP : "SET_NETWORK"); + WPA_CTRL_RSP : + (os_strncmp(buf, "SET_NETWORK ", 12) == 0 ? + "SET_NETWORK" : "key-add")); } 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", @@ -8715,6 +9866,22 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, reply_len = wpas_ctrl_iface_pmksa(wpa_s, reply, reply_size); } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) { wpas_ctrl_iface_pmksa_flush(wpa_s); +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL + } else if (os_strncmp(buf, "PMKSA_GET ", 10) == 0) { + reply_len = wpas_ctrl_iface_pmksa_get(wpa_s, buf + 10, + reply, reply_size); + } else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) { + if (wpas_ctrl_iface_pmksa_add(wpa_s, buf + 10) < 0) + reply_len = -1; +#ifdef CONFIG_MESH + } else if (os_strncmp(buf, "MESH_PMKSA_GET ", 15) == 0) { + reply_len = wpas_ctrl_iface_mesh_pmksa_get(wpa_s, buf + 15, + reply, reply_size); + } else if (os_strncmp(buf, "MESH_PMKSA_ADD ", 15) == 0) { + if (wpas_ctrl_iface_mesh_pmksa_add(wpa_s, buf + 15) < 0) + reply_len = -1; +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ } else if (os_strncmp(buf, "SET ", 4) == 0) { if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4)) reply_len = -1; @@ -8751,11 +9918,6 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8)) reply_len = -1; #endif /* IEEE8021X_EAPOL */ -#ifdef CONFIG_PEERKEY - } else if (os_strncmp(buf, "STKSTART ", 9) == 0) { - if (wpa_supplicant_ctrl_iface_stkstart(wpa_s, buf + 9)) - reply_len = -1; -#endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211R } else if (os_strncmp(buf, "FT_DS ", 6) == 0) { if (wpa_supplicant_ctrl_iface_ft_ds(wpa_s, buf + 6)) @@ -9286,6 +10448,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "WNM_BSS_QUERY ", 14) == 0) { if (wpas_ctrl_iface_wnm_bss_query(wpa_s, buf + 14)) reply_len = -1; + } else if (os_strncmp(buf, "COLOC_INTF_REPORT ", 18) == 0) { + if (wpas_ctrl_iface_coloc_intf_report(wpa_s, buf + 18)) + reply_len = -1; #endif /* CONFIG_WNM */ } else if (os_strcmp(buf, "FLUSH") == 0) { wpa_supplicant_ctrl_iface_flush(wpa_s); @@ -9332,6 +10497,15 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "TEST_ASSOC_IE ", 14) == 0) { if (wpas_ctrl_test_assoc_ie(wpa_s, buf + 14) < 0) reply_len = -1; + } else if (os_strcmp(buf, "RESET_PN") == 0) { + if (wpas_ctrl_reset_pn(wpa_s) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "KEY_REQUEST ", 12) == 0) { + if (wpas_ctrl_key_request(wpa_s, buf + 12) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "RESEND_ASSOC") == 0) { + if (wpas_ctrl_resend_assoc(wpa_s) < 0) + reply_len = -1; #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) @@ -9353,6 +10527,97 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "GET_PREF_FREQ_LIST ", 19) == 0) { reply_len = wpas_ctrl_iface_get_pref_freq_list( wpa_s, buf + 19, reply, reply_size); +#ifdef CONFIG_FILS + } else if (os_strncmp(buf, "FILS_HLP_REQ_ADD ", 17) == 0) { + if (wpas_ctrl_iface_fils_hlp_req_add(wpa_s, buf + 17)) + reply_len = -1; + } else if (os_strcmp(buf, "FILS_HLP_REQ_FLUSH") == 0) { + wpas_flush_fils_hlp_req(wpa_s); +#endif /* CONFIG_FILS */ +#ifdef CONFIG_DPP + } else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) { + int res; + + res = wpas_dpp_qr_code(wpa_s, buf + 12); + if (res < 0) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%d", res); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) { + int res; + + res = wpas_dpp_bootstrap_gen(wpa_s, buf + 18); + if (res < 0) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%d", res); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) { + if (wpas_dpp_bootstrap_remove(wpa_s, buf + 21) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) { + const char *uri; + + uri = wpas_dpp_bootstrap_get_uri(wpa_s, atoi(buf + 22)); + if (!uri) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%s", uri); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_BOOTSTRAP_INFO ", 19) == 0) { + reply_len = wpas_dpp_bootstrap_info(wpa_s, atoi(buf + 19), + reply, reply_size); + } else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) { + if (wpas_dpp_auth_init(wpa_s, buf + 13) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_LISTEN ", 11) == 0) { + if (wpas_dpp_listen(wpa_s, buf + 11) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "DPP_STOP_LISTEN") == 0) { + wpas_dpp_stop(wpa_s); + wpas_dpp_listen_stop(wpa_s); + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) { + int res; + + res = wpas_dpp_configurator_add(wpa_s, buf + 20); + if (res < 0) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%d", res); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) { + if (wpas_dpp_configurator_remove(wpa_s, buf + 24) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_SIGN ", 22) == 0) { + if (wpas_dpp_configurator_sign(wpa_s, buf + 22) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_GET_KEY ", 25) == 0) { + reply_len = wpas_dpp_configurator_get_key(wpa_s, atoi(buf + 25), + reply, reply_size); + } else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) { + int res; + + res = wpas_dpp_pkex_add(wpa_s, buf + 12); + if (res < 0) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%d", res); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) { + if (wpas_dpp_pkex_remove(wpa_s, buf + 16) < 0) + reply_len = -1; +#endif /* CONFIG_DPP */ } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -9662,12 +10927,16 @@ static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global, "P2P_CANCEL", "P2P_PRESENCE_REQ", "P2P_EXT_LISTEN", +#ifdef CONFIG_AP + "STA-FIRST", +#endif /* CONFIG_AP */ NULL }; static const char * prefix[] = { #ifdef ANDROID "DRIVER ", #endif /* ANDROID */ + "GET_CAPABILITY ", "GET_NETWORK ", "REMOVE_NETWORK ", "P2P_FIND ", @@ -9699,6 +10968,10 @@ static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global, "NFC_REPORT_HANDOVER ", "P2P_ASP_PROVISION ", "P2P_ASP_PROVISION_RESP ", +#ifdef CONFIG_AP + "STA ", + "STA-NEXT ", +#endif /* CONFIG_AP */ NULL }; int found = 0; diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c b/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c index 54e0e2fac583..9c0a47e63936 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface_named_pipe.c @@ -319,13 +319,12 @@ static void wpa_supplicant_ctrl_iface_rx(struct wpa_ctrl_dst *dst, size_t len) } os_free(dst->rsp_buf); - dst->rsp_buf = os_malloc(send_len); + dst->rsp_buf = os_memdup(send_buf, send_len); if (dst->rsp_buf == NULL) { ctrl_close_pipe(dst); os_free(reply); return; } - os_memcpy(dst->rsp_buf, send_buf, send_len); os_free(reply); if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap, @@ -739,13 +738,12 @@ static void wpa_supplicant_global_iface_rx(struct wpa_global_dst *dst, } os_free(dst->rsp_buf); - dst->rsp_buf = os_malloc(send_len); + dst->rsp_buf = os_memdup(send_buf, send_len); if (dst->rsp_buf == NULL) { global_close_pipe(dst); os_free(reply); return; } - os_memcpy(dst->rsp_buf, send_buf, send_len); os_free(reply); if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap, diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c b/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c index 0dc0937ff0aa..8a6057a82bfe 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface_udp.c @@ -219,7 +219,7 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx, { struct wpa_supplicant *wpa_s = eloop_ctx; struct ctrl_iface_priv *priv = sock_ctx; - char buf[256], *pos; + char buf[4096], *pos; int res; #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 struct sockaddr_in6 from; @@ -600,7 +600,7 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx, { struct wpa_global *global = eloop_ctx; struct ctrl_iface_global_priv *priv = sock_ctx; - char buf[256], *pos; + char buf[4096], *pos; int res; #ifdef CONFIG_CTRL_IFACE_UDP_IPV6 struct sockaddr_in6 from; diff --git a/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c b/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c index 4db712fff7bb..b88c80a99551 100644 --- a/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c +++ b/contrib/wpa/wpa_supplicant/ctrl_iface_unix.c @@ -103,7 +103,7 @@ static int wpa_supplicant_ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, socklen_t fromlen, int global) { - return ctrl_iface_attach(ctrl_dst, from, fromlen); + return ctrl_iface_attach(ctrl_dst, from, fromlen, NULL); } diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new.c index 27b3012aede8..d4deb0fe35f0 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new.c @@ -793,6 +793,144 @@ nomem: #endif /* CONFIG_WPS */ + +#ifdef CONFIG_MESH + +void wpas_dbus_signal_mesh_group_started(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (!iface || !wpa_s->dbus_new_path) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_MESH, + "MeshGroupStarted"); + if (!msg) + return; + + dbus_message_iter_init_append(msg, &iter); + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_byte_array(&dict_iter, "SSID", + (const char *) ssid->ssid, + ssid->ssid_len) || + !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); +} + + +void wpas_dbus_signal_mesh_group_removed(struct wpa_supplicant *wpa_s, + const u8 *meshid, u8 meshid_len, + int reason) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (!iface || !wpa_s->dbus_new_path) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_MESH, + "MeshGroupRemoved"); + if (!msg) + return; + + dbus_message_iter_init_append(msg, &iter); + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_byte_array(&dict_iter, "SSID", + (const char *) meshid, + meshid_len) || + !wpa_dbus_dict_append_int32(&dict_iter, "DisconnectReason", + reason) || + !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); +} + + +void wpas_dbus_signal_mesh_peer_connected(struct wpa_supplicant *wpa_s, + const u8 *peer_addr) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (!iface || !wpa_s->dbus_new_path) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_MESH, + "MeshPeerConnected"); + if (!msg) + return; + + dbus_message_iter_init_append(msg, &iter); + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_byte_array(&dict_iter, "PeerAddress", + (const char *) peer_addr, + 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); +} + + +void wpas_dbus_signal_mesh_peer_disconnected(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, int reason) +{ + struct wpas_dbus_priv *iface; + DBusMessage *msg; + DBusMessageIter iter, dict_iter; + + iface = wpa_s->global->dbus; + + /* Do nothing if the control interface is not turned on */ + if (!iface || !wpa_s->dbus_new_path) + return; + + msg = dbus_message_new_signal(wpa_s->dbus_new_path, + WPAS_DBUS_NEW_IFACE_MESH, + "MeshPeerDisconnected"); + if (!msg) + return; + + dbus_message_iter_init_append(msg, &iter); + if (!wpa_dbus_dict_open_write(&iter, &dict_iter) || + !wpa_dbus_dict_append_byte_array(&dict_iter, "PeerAddress", + (const char *) peer_addr, + ETH_ALEN) || + !wpa_dbus_dict_append_int32(&dict_iter, "DisconnectReason", + reason) || + !wpa_dbus_dict_close_write(&iter, &dict_iter)) + wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); + else + dbus_connection_send(iface->con, msg, NULL); + dbus_message_unref(msg); +} + +#endif /* CONFIG_MESH */ + + void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s, int depth, const char *subject, const char *altsubject[], @@ -1256,9 +1394,12 @@ static void peer_groups_changed(struct wpa_supplicant *wpa_s) * @wpa_s: %wpa_supplicant network interface data * @client: this device is P2P client * @persistent: 0 - non persistent group, 1 - persistent group + * @ip: When group role is client, it contains local IP address, netmask, and + * GO's IP address, if assigned; otherwise, NULL */ void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, - int client, int persistent) + int client, int persistent, + const u8 *ip) { DBusMessage *msg; DBusMessageIter iter, dict_iter; @@ -1300,6 +1441,13 @@ void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, !wpa_dbus_dict_append_bool(&dict_iter, "persistent", persistent) || !wpa_dbus_dict_append_object_path(&dict_iter, "group_object", wpa_s->dbus_groupobj_path) || + (ip && + (!wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddr", + (char *) ip, 4) || + !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrMask", + (char *) ip + 4, 4) || + !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrGo", + (char *) ip + 8, 4))) || !wpa_dbus_dict_close_write(&iter, &dict_iter)) { wpa_printf(MSG_ERROR, "dbus: Failed to construct signal"); } else { @@ -1879,6 +2027,9 @@ void wpas_dbus_signal_p2p_group_formation_failure(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, "GroupFormationFailure"); @@ -1920,6 +2071,9 @@ void wpas_dbus_signal_p2p_invitation_received(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, "InvitationReceived"); @@ -3071,6 +3225,20 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { END_ARGS } }, + { "TDLSChannelSwitch", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_tdls_channel_switch, + { + { "args", "a{sv}", ARG_IN }, + END_ARGS + } + }, + { "TDLSCancelChannelSwitch", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_tdls_cancel_channel_switch, + { + { "peer_address", "s", ARG_IN }, + END_ARGS + } + }, #endif /* CONFIG_TDLS */ { "VendorElemAdd", WPAS_DBUS_NEW_IFACE_INTERFACE, (WPADBusMethodHandler) wpas_dbus_handler_vendor_elem_add, @@ -3104,6 +3272,12 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = { } }, #endif /* CONFIG_NO_CONFIG_WRITE */ + { "AbortScan", WPAS_DBUS_NEW_IFACE_INTERFACE, + (WPADBusMethodHandler) wpas_dbus_handler_abort_scan, + { + END_ARGS + } + }, { NULL, NULL, NULL, { END_ARGS } } }; @@ -3224,6 +3398,42 @@ static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = { wpas_dbus_setter_config_methods, NULL }, + { + "DeviceName", WPAS_DBUS_NEW_IFACE_WPS, "s", + wpas_dbus_getter_wps_device_name, + wpas_dbus_setter_wps_device_name, + NULL + }, + { + "Manufacturer", WPAS_DBUS_NEW_IFACE_WPS, "s", + wpas_dbus_getter_wps_manufacturer, + wpas_dbus_setter_wps_manufacturer, + NULL + }, + { + "ModelName", WPAS_DBUS_NEW_IFACE_WPS, "s", + wpas_dbus_getter_wps_device_model_name, + wpas_dbus_setter_wps_device_model_name, + NULL + }, + { + "ModelNumber", WPAS_DBUS_NEW_IFACE_WPS, "s", + wpas_dbus_getter_wps_device_model_number, + wpas_dbus_setter_wps_device_model_number, + NULL + }, + { + "SerialNumber", WPAS_DBUS_NEW_IFACE_WPS, "s", + wpas_dbus_getter_wps_device_serial_number, + wpas_dbus_setter_wps_device_serial_number, + NULL + }, + { + "DeviceType", WPAS_DBUS_NEW_IFACE_WPS, "ay", + wpas_dbus_getter_wps_device_device_type, + wpas_dbus_setter_wps_device_device_type, + NULL + }, #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P { "P2PDeviceConfig", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "a{sv}", @@ -3267,6 +3477,18 @@ static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = { NULL, NULL }, +#ifdef CONFIG_MESH + { "MeshPeers", WPAS_DBUS_NEW_IFACE_MESH, "aay", + wpas_dbus_getter_mesh_peers, + NULL, + NULL + }, + { "MeshGroup", WPAS_DBUS_NEW_IFACE_MESH, "ay", + wpas_dbus_getter_mesh_group, + NULL, + NULL + }, +#endif /* CONFIG_MESH */ { NULL, NULL, NULL, NULL, NULL, NULL } }; @@ -3544,6 +3766,32 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = { END_ARGS } }, +#ifdef CONFIG_MESH + { "MeshGroupStarted", WPAS_DBUS_NEW_IFACE_MESH, + { + { "args", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "MeshGroupRemoved", WPAS_DBUS_NEW_IFACE_MESH, + { + { "args", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "MeshPeerConnected", WPAS_DBUS_NEW_IFACE_MESH, + { + { "args", "a{sv}", ARG_OUT }, + END_ARGS + } + }, + { "MeshPeerDisconnected", WPAS_DBUS_NEW_IFACE_MESH, + { + { "args", "a{sv}", ARG_OUT }, + END_ARGS + } + }, +#endif /* CONFIG_MESH */ { NULL, NULL, { END_ARGS } } }; @@ -4012,7 +4260,13 @@ void wpas_dbus_signal_p2p_find_stopped(struct wpa_supplicant *wpa_s) iface = wpa_s->global->dbus; /* Do nothing if the control interface is not turned on */ - if (iface == NULL || !wpa_s->dbus_new_path) + if (iface == NULL) + return; + + if (wpa_s->p2p_mgmt) + wpa_s = wpa_s->parent; + + if (!wpa_s->dbus_new_path) return; msg = dbus_message_new_signal(wpa_s->dbus_new_path, diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new.h index d64fceef718c..40ae133b225e 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new.h @@ -64,6 +64,8 @@ enum wpas_dbus_bss_prop { #define WPAS_DBUS_NEW_IFACE_P2PDEVICE \ WPAS_DBUS_NEW_IFACE_INTERFACE ".P2PDevice" +#define WPAS_DBUS_NEW_IFACE_MESH WPAS_DBUS_NEW_IFACE_INTERFACE ".Mesh" + /* * Groups correspond to P2P groups where this device is a GO (owner) */ @@ -190,7 +192,8 @@ void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, const u8 *src, u16 dev_passwd_id, u8 go_intent); void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, - int client, int persistent); + int client, int persistent, + const u8 *ip); void wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s, const char *reason); void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s, @@ -237,6 +240,15 @@ void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *dev_addr, const u8 *bssid, int id, int op_freq); +void wpas_dbus_signal_mesh_group_started(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +void wpas_dbus_signal_mesh_group_removed(struct wpa_supplicant *wpa_s, + const u8 *meshid, u8 meshid_len, + int reason); +void wpas_dbus_signal_mesh_peer_connected(struct wpa_supplicant *wpa_s, + const u8 *peer_addr); +void wpas_dbus_signal_mesh_peer_disconnected(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, int reason); #else /* CONFIG_CTRL_IFACE_DBUS_NEW */ @@ -400,7 +412,8 @@ static inline void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s, static inline void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s, - int client, int persistent) + int client, int persistent, + const u8 *ip) { } @@ -551,6 +564,31 @@ void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s, { } +static inline +void wpas_dbus_signal_mesh_group_started(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ +} + +static inline +void wpas_dbus_signal_mesh_group_removed(struct wpa_supplicant *wpa_s, + const u8 *meshid, u8 meshid_len, + int reason) +{ +} + +static inline +void wpas_dbus_signal_mesh_peer_connected(struct wpa_supplicant *wpa_s, + const u8 *peer_addr) +{ +} + +static inline +void wpas_dbus_signal_mesh_peer_disconnected(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, int reason) +{ +} + #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */ #endif /* CTRL_IFACE_DBUS_H_NEW */ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c index e11dd36ca23c..94773b329133 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.c @@ -28,6 +28,10 @@ #include "dbus_dict_helpers.h" #include "dbus_common_i.h" #include "drivers/driver.h" +#ifdef CONFIG_MESH +#include "ap/hostapd.h" +#include "ap/sta_info.h" +#endif /* CONFIG_MESH */ static const char * const debug_strings[] = { "excessive", "msgdump", "debug", "info", "warning", "error", NULL @@ -517,6 +521,27 @@ dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter, /** + * wpas_dbus_string_property_getter - Get string type property + * @iter: Message iter to use when appending arguments + * @val: Pointer to place holding property value, can be %NULL + * @error: On failure an error describing the failure + * Returns: TRUE if the request was successful, FALSE if it failed + * + * Generic getter for string type properties. %NULL is converted to an empty + * string. + */ +dbus_bool_t wpas_dbus_string_property_getter(DBusMessageIter *iter, + const void *val, + DBusError *error) +{ + if (!val) + val = ""; + return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, + &val, error); +} + + +/** * wpas_dbus_handler_create_interface - Request registration of a network iface * @message: Pointer to incoming dbus message * @global: %wpa_supplicant global data structure @@ -955,8 +980,21 @@ dbus_bool_t wpas_dbus_getter_global_capabilities( const struct wpa_dbus_property_desc *property_desc, DBusMessageIter *iter, DBusError *error, void *user_data) { - const char *capabilities[5] = { NULL, NULL, NULL, NULL, NULL }; + const char *capabilities[10] = { NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL }; size_t num_items = 0; +#ifdef CONFIG_FILS + struct wpa_global *global = user_data; + struct wpa_supplicant *wpa_s; + int fils_supported = 0, fils_sk_pfs_supported = 0; + + for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) { + if (wpa_is_fils_supported(wpa_s)) + fils_supported = 1; + if (wpa_is_fils_sk_pfs_supported(wpa_s)) + fils_sk_pfs_supported = 1; + } +#endif /* CONFIG_FILS */ #ifdef CONFIG_AP capabilities[num_items++] = "ap"; @@ -970,6 +1008,24 @@ dbus_bool_t wpas_dbus_getter_global_capabilities( #ifdef CONFIG_INTERWORKING capabilities[num_items++] = "interworking"; #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_IEEE80211W + capabilities[num_items++] = "pmf"; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_MESH + capabilities[num_items++] = "mesh"; +#endif /* CONFIG_MESH */ +#ifdef CONFIG_FILS + if (fils_supported) + capabilities[num_items++] = "fils"; + if (fils_sk_pfs_supported) + capabilities[num_items++] = "fils_sk_pfs"; +#endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R + capabilities[num_items++] = "ft"; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_SHA384 + capabilities[num_items++] = "sha384"; +#endif /* CONFIG_SHA384 */ return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_STRING, @@ -1052,12 +1108,11 @@ static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var, } if (len != 0) { - ssid = os_malloc(len); + ssid = os_memdup(val, len); if (ssid == NULL) { *reply = wpas_dbus_error_no_memory(message); return -1; } - os_memcpy(ssid, val, len); } else { /* Allow zero-length SSIDs */ ssid = NULL; @@ -1396,6 +1451,27 @@ out: } +/* + * wpas_dbus_handler_abort_scan - Request an ongoing scan to be aborted + * @message: Pointer to incoming dbus message + * @wpa_s: wpa_supplicant structure for a network interface + * Returns: Abort failed or no scan in progress DBus error message on failure + * or NULL otherwise. + * + * Handler function for "AbortScan" method call of network interface. + */ +DBusMessage * wpas_dbus_handler_abort_scan(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + if (wpas_abort_ongoing_scan(wpa_s) < 0) + return dbus_message_new_error( + message, WPAS_DBUS_ERROR_IFACE_SCAN_ERROR, + "Abort failed or no scan in progress"); + + return NULL; +} + + /** * wpas_dbus_handler_signal_poll - Request immediate signal properties * @message: Pointer to incoming dbus message @@ -1903,13 +1979,12 @@ DBusMessage * wpas_dbus_handler_add_blob(DBusMessage *message, goto err; } - blob->data = os_malloc(blob_len); + blob->data = os_memdup(blob_data, blob_len); 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; wpa_config_set_blob(wpa_s->conf, blob); @@ -2270,6 +2345,156 @@ DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message, return NULL; } +/* + * wpas_dbus_handler_tdls_channel_switch - Enable channel switching with 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 "TDLSChannelSwitch" method call of network interface. + */ +DBusMessage * +wpas_dbus_handler_tdls_channel_switch(DBusMessage *message, + struct wpa_supplicant *wpa_s) +{ + DBusMessageIter iter, iter_dict; + struct wpa_dbus_dict_entry entry; + u8 peer[ETH_ALEN]; + struct hostapd_freq_params freq_params; + u8 oper_class = 0; + int ret; + int is_peer_present = 0; + + if (!wpa_tdls_is_external_setup(wpa_s->wpa)) { + wpa_printf(MSG_INFO, + "tdls_chanswitch: Only supported with external setup"); + return wpas_dbus_error_unknown_error(message, "TDLS is not using external setup"); + } + + os_memset(&freq_params, 0, sizeof(freq_params)); + + dbus_message_iter_init(message, &iter); + + if (!wpa_dbus_dict_open_read(&iter, &iter_dict, NULL)) + return wpas_dbus_error_invalid_args(message, NULL); + + while (wpa_dbus_dict_has_dict_entry(&iter_dict)) { + if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) + return wpas_dbus_error_invalid_args(message, NULL); + + if (os_strcmp(entry.key, "PeerAddress") == 0 && + entry.type == DBUS_TYPE_STRING) { + if (hwaddr_aton(entry.str_value, peer)) { + wpa_printf(MSG_DEBUG, + "tdls_chanswitch: Invalid address '%s'", + entry.str_value); + wpa_dbus_dict_entry_clear(&entry); + return wpas_dbus_error_invalid_args(message, + NULL); + } + + is_peer_present = 1; + } else if (os_strcmp(entry.key, "OperClass") == 0 && + entry.type == DBUS_TYPE_BYTE) { + oper_class = entry.byte_value; + } else if (os_strcmp(entry.key, "Frequency") == 0 && + entry.type == DBUS_TYPE_UINT32) { + freq_params.freq = entry.uint32_value; + } else if (os_strcmp(entry.key, "SecChannelOffset") == 0 && + entry.type == DBUS_TYPE_UINT32) { + freq_params.sec_channel_offset = entry.uint32_value; + } else if (os_strcmp(entry.key, "CenterFrequency1") == 0 && + entry.type == DBUS_TYPE_UINT32) { + freq_params.center_freq1 = entry.uint32_value; + } else if (os_strcmp(entry.key, "CenterFrequency2") == 0 && + entry.type == DBUS_TYPE_UINT32) { + freq_params.center_freq2 = entry.uint32_value; + } else if (os_strcmp(entry.key, "Bandwidth") == 0 && + entry.type == DBUS_TYPE_UINT32) { + freq_params.bandwidth = entry.uint32_value; + } else if (os_strcmp(entry.key, "HT") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + freq_params.ht_enabled = entry.bool_value; + } else if (os_strcmp(entry.key, "VHT") == 0 && + entry.type == DBUS_TYPE_BOOLEAN) { + freq_params.vht_enabled = entry.bool_value; + } else { + wpa_dbus_dict_entry_clear(&entry); + return wpas_dbus_error_invalid_args(message, NULL); + } + + wpa_dbus_dict_entry_clear(&entry); + } + + if (oper_class == 0) { + wpa_printf(MSG_INFO, + "tdls_chanswitch: Invalid op class provided"); + return wpas_dbus_error_invalid_args( + message, "Invalid op class provided"); + } + + if (freq_params.freq == 0) { + wpa_printf(MSG_INFO, + "tdls_chanswitch: Invalid freq provided"); + return wpas_dbus_error_invalid_args(message, + "Invalid freq provided"); + } + + if (is_peer_present == 0) { + wpa_printf(MSG_DEBUG, + "tdls_chanswitch: peer address not provided"); + return wpas_dbus_error_invalid_args( + message, "peer address not provided"); + } + + wpa_printf(MSG_DEBUG, "dbus: 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" : ""); + + ret = wpa_tdls_enable_chan_switch(wpa_s->wpa, peer, oper_class, + &freq_params); + if (ret) + return wpas_dbus_error_unknown_error( + message, "error processing TDLS channel switch"); + + return NULL; +} + +/* + * wpas_dbus_handler_tdls_cancel_channel_switch - Disable channel switching with 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 "TDLSCancelChannelSwitch" method call of network + * interface. + */ +DBusMessage * +wpas_dbus_handler_tdls_cancel_channel_switch(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_CANCEL_CHAN_SWITCH " MACSTR, + MAC2STR(peer)); + + ret = wpa_tdls_disable_chan_switch(wpa_s->wpa, peer); + if (ret) + return wpas_dbus_error_unknown_error( + message, "error canceling TDLS channel switch"); + + return NULL; +} + #endif /* CONFIG_TDLS */ @@ -2468,6 +2693,28 @@ dbus_bool_t wpas_dbus_getter_capabilities( goto nomem; } + if (!wpa_dbus_dict_begin_string_array(&iter_dict, "GroupMgmt", + &iter_dict_entry, + &iter_dict_val, + &iter_array) || + (res == 0 && (capa.enc & WPA_DRIVER_CAPA_ENC_BIP) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "aes-128-cmac")) || + (res == 0 && (capa.enc & WPA_DRIVER_CAPA_ENC_BIP_GMAC_128) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "bip-gmac-128")) || + (res == 0 && (capa.enc & WPA_DRIVER_CAPA_ENC_BIP_GMAC_256) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "bip-gmac-256")) || + (res == 0 && (capa.enc & WPA_DRIVER_CAPA_ENC_BIP_CMAC_256) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "bip-cmac-256")) || + !wpa_dbus_dict_end_string_array(&iter_dict, + &iter_dict_entry, + &iter_dict_val, + &iter_array)) + goto nomem; + /***** key management */ if (res < 0) { const char *args[] = { @@ -2627,6 +2874,11 @@ dbus_bool_t wpas_dbus_getter_capabilities( !wpa_s->conf->p2p_disabled && !wpa_dbus_dict_string_array_add_element( &iter_array, "p2p")) || +#ifdef CONFIG_MESH + (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_MESH) && + !wpa_dbus_dict_string_array_add_element( + &iter_array, "mesh")) || +#endif /* CONFIG_MESH */ !wpa_dbus_dict_end_string_array(&iter_dict, &iter_dict_entry, &iter_dict_val, @@ -3086,10 +3338,8 @@ dbus_bool_t wpas_dbus_getter_ifname( DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; - const char *ifname = wpa_s->ifname; - return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, - &ifname, error); + return wpas_dbus_string_property_getter(iter, wpa_s->ifname, error); } @@ -3107,7 +3357,6 @@ dbus_bool_t wpas_dbus_getter_driver( DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; - const char *driver; if (wpa_s->driver == NULL || wpa_s->driver->name == NULL) { wpa_printf(MSG_DEBUG, "%s[dbus]: wpa_s has no driver set", @@ -3117,9 +3366,8 @@ dbus_bool_t wpas_dbus_getter_driver( return FALSE; } - driver = wpa_s->driver->name; - return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, - &driver, error); + return wpas_dbus_string_property_getter(iter, wpa_s->driver->name, + error); } @@ -3232,10 +3480,9 @@ dbus_bool_t wpas_dbus_getter_bridge_ifname( DBusMessageIter *iter, DBusError *error, void *user_data) { 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); + return wpas_dbus_string_property_getter(iter, wpa_s->bridge_ifname, + error); } @@ -3253,13 +3500,8 @@ dbus_bool_t wpas_dbus_getter_config_file( DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; - char *confname = ""; - if (wpa_s->confname) - confname = wpa_s->confname; - - return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, - &confname, error); + return wpas_dbus_string_property_getter(iter, wpa_s->confname, error); } @@ -3399,14 +3641,10 @@ 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); + return wpas_dbus_string_property_getter(iter, + wpa_s->conf->pkcs11_engine_path, + error); } @@ -3424,14 +3662,10 @@ 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); + return wpas_dbus_string_property_getter(iter, + wpa_s->conf->pkcs11_module_path, + error); } @@ -3683,6 +3917,7 @@ dbus_bool_t wpas_dbus_getter_bss_mode( struct bss_handler_args *args = user_data; struct wpa_bss *res; const char *mode; + const u8 *mesh; res = get_bss_helper(args, error, __func__); if (!res) @@ -3696,9 +3931,15 @@ dbus_bool_t wpas_dbus_getter_bss_mode( case IEEE80211_CAP_DMG_AP: mode = "infrastructure"; break; + default: + mode = ""; + break; } } else { - if (res->caps & IEEE80211_CAP_IBSS) + mesh = wpa_bss_get_ie(res, WLAN_EID_MESH_ID); + if (mesh) + mode = "mesh"; + else if (res->caps & IEEE80211_CAP_IBSS) mode = "ad-hoc"; else mode = "infrastructure"; @@ -3826,7 +4067,7 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop( DBusMessageIter iter_dict, variant_iter; const char *group; const char *pairwise[5]; /* max 5 pairwise ciphers is supported */ - const char *key_mgmt[9]; /* max 9 key managements may be supported */ + const char *key_mgmt[13]; /* max 13 key managements may be supported */ int n; if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, @@ -3858,6 +4099,16 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop( if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) key_mgmt[n++] = "wpa-eap-suite-b-192"; #endif /* CONFIG_SUITEB192 */ +#ifdef CONFIG_FILS + if (ie_data->key_mgmt & WPA_KEY_MGMT_FILS_SHA256) + key_mgmt[n++] = "wpa-fils-sha256"; + if (ie_data->key_mgmt & WPA_KEY_MGMT_FILS_SHA384) + key_mgmt[n++] = "wpa-fils-sha384"; + if (ie_data->key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) + key_mgmt[n++] = "wpa-ft-fils-sha256"; + if (ie_data->key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) + key_mgmt[n++] = "wpa-ft-fils-sha384"; +#endif /* CONFIG_FILS */ if (ie_data->key_mgmt & WPA_KEY_MGMT_NONE) key_mgmt[n++] = "wpa-none"; @@ -4534,3 +4785,100 @@ DBusMessage * wpas_dbus_handler_vendor_elem_remove(DBusMessage *message, return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS, "Not found"); } + + +#ifdef CONFIG_MESH + +/** + * wpas_dbus_getter_mesh_peers - Get connected mesh peers + * @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 "MeshPeers" property. + */ +dbus_bool_t wpas_dbus_getter_mesh_peers( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + struct hostapd_data *hapd; + struct sta_info *sta; + DBusMessageIter variant_iter, array_iter; + int i; + DBusMessageIter inner_array_iter; + + if (!wpa_s->ifmsh) + return FALSE; + hapd = wpa_s->ifmsh->bss[0]; + + if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, + DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_BYTE_AS_STRING, + &variant_iter) || + !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_ARRAY_AS_STRING + DBUS_TYPE_BYTE_AS_STRING, + &array_iter)) + return FALSE; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (!dbus_message_iter_open_container( + &array_iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, + &inner_array_iter)) + return FALSE; + + for (i = 0; i < ETH_ALEN; i++) { + if (!dbus_message_iter_append_basic(&inner_array_iter, + DBUS_TYPE_BYTE, + &(sta->addr[i]))) + return FALSE; + } + + if (!dbus_message_iter_close_container( + &array_iter, &inner_array_iter)) + return FALSE; + } + + if (!dbus_message_iter_close_container(&variant_iter, &array_iter) || + !dbus_message_iter_close_container(iter, &variant_iter)) + return FALSE; + + return TRUE; +} + + +/** + * wpas_dbus_getter_mesh_group - Get mesh group + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "MeshGroup" property. + */ +dbus_bool_t wpas_dbus_getter_mesh_group( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + struct wpa_ssid *ssid = wpa_s->current_ssid; + + if (!wpa_s->ifmsh || !ssid) + return FALSE; + + if (!wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, + (char *) ssid->ssid, + ssid->ssid_len, error)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: error constructing reply", __func__); + return FALSE; + } + + return TRUE; +} + +#endif /* CONFIG_MESH */ diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h index 1d6235d6f3e4..6f952cc39091 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers.h @@ -43,6 +43,10 @@ dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter, size_t array_len, DBusError *error); +dbus_bool_t wpas_dbus_string_property_getter(DBusMessageIter *iter, + const void *val, + DBusError *error); + DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message, struct wpa_global *global); @@ -70,6 +74,9 @@ DECLARE_ACCESSOR(wpas_dbus_setter_iface_global); DBusMessage * wpas_dbus_handler_scan(DBusMessage *message, struct wpa_supplicant *wpa_s); +DBusMessage * wpas_dbus_handler_abort_scan(DBusMessage *message, + struct wpa_supplicant *wpa_s); + DBusMessage * wpas_dbus_handler_signal_poll(DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -186,6 +193,21 @@ DECLARE_ACCESSOR(wpas_dbus_getter_process_credentials); DECLARE_ACCESSOR(wpas_dbus_setter_process_credentials); DECLARE_ACCESSOR(wpas_dbus_getter_config_methods); DECLARE_ACCESSOR(wpas_dbus_setter_config_methods); +DECLARE_ACCESSOR(wpas_dbus_getter_wps_device_name); +DECLARE_ACCESSOR(wpas_dbus_setter_wps_device_name); +DECLARE_ACCESSOR(wpas_dbus_getter_wps_manufacturer); +DECLARE_ACCESSOR(wpas_dbus_setter_wps_manufacturer); +DECLARE_ACCESSOR(wpas_dbus_getter_wps_device_model_name); +DECLARE_ACCESSOR(wpas_dbus_setter_wps_device_model_name); +DECLARE_ACCESSOR(wpas_dbus_getter_wps_device_model_number); +DECLARE_ACCESSOR(wpas_dbus_setter_wps_device_model_number); +DECLARE_ACCESSOR(wpas_dbus_getter_wps_device_serial_number); +DECLARE_ACCESSOR(wpas_dbus_setter_wps_device_serial_number); +DECLARE_ACCESSOR(wpas_dbus_getter_wps_device_device_type); +DECLARE_ACCESSOR(wpas_dbus_setter_wps_device_device_type); + +DECLARE_ACCESSOR(wpas_dbus_getter_mesh_peers); +DECLARE_ACCESSOR(wpas_dbus_getter_mesh_group); DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message, struct wpa_supplicant *wpa_s); @@ -195,6 +217,12 @@ 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_handler_tdls_channel_switch(DBusMessage *message, + struct wpa_supplicant *wpa_s); +DBusMessage * +wpas_dbus_handler_tdls_cancel_channel_switch(DBusMessage *message, + struct wpa_supplicant *wpa_s); DBusMessage * wpas_dbus_handler_vendor_elem_add(DBusMessage *message, struct wpa_supplicant *wpa_s); diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c index 73b9e20c20b0..9305b9a4f37d 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_p2p.c @@ -28,6 +28,18 @@ #include "../p2p_supplicant.h" #include "../wifi_display.h" + +static int wpas_dbus_validate_dbus_ipaddr(struct wpa_dbus_dict_entry entry) +{ + if (entry.type != DBUS_TYPE_ARRAY || + entry.array_type != DBUS_TYPE_BYTE || + entry.array_len != 4) + return 0; + + return 1; +} + + /** * Parses out the mac address from the peer object path. * @peer_path - object path of the form @@ -78,6 +90,7 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message, int num_req_dev_types = 0; unsigned int i; u8 *req_dev_types = NULL; + unsigned int freq = 0; dbus_message_iter_init(message, &iter); entry.key = NULL; @@ -122,6 +135,10 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message, type = P2P_FIND_PROGRESSIVE; else goto error_clear; + } else if (os_strcmp(entry.key, "freq") == 0 && + (entry.type == DBUS_TYPE_INT32 || + entry.type == DBUS_TYPE_UINT32)) { + freq = entry.uint32_value; } else goto error_clear; wpa_dbus_dict_entry_clear(&entry); @@ -129,8 +146,11 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message, wpa_s = wpa_s->global->p2p_init_wpa_s; - wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types, - NULL, 0, 0, NULL, 0); + if (wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, + req_dev_types, NULL, 0, 0, NULL, freq)) + reply = wpas_dbus_error_unknown_error( + message, "Could not start P2P find"); + os_free(req_dev_types); return reply; @@ -867,6 +887,35 @@ dbus_bool_t wpas_dbus_getter_p2p_device_config( goto err_no_mem; } + /* GO IP address */ + if (WPA_GET_BE32(wpa_s->conf->ip_addr_go) && + !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrGo", + (char *) wpa_s->conf->ip_addr_go, + 4)) + goto err_no_mem; + + /* IP address mask */ + if (WPA_GET_BE32(wpa_s->conf->ip_addr_mask) && + !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrMask", + (char *) wpa_s->conf->ip_addr_mask, + 4)) + goto err_no_mem; + + /* IP address start */ + if (WPA_GET_BE32(wpa_s->conf->ip_addr_start) && + !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrStart", + (char *) + wpa_s->conf->ip_addr_start, + 4)) + goto err_no_mem; + + /* IP address end */ + if (WPA_GET_BE32(wpa_s->conf->ip_addr_end) && + !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrEnd", + (char *) wpa_s->conf->ip_addr_end, + 4)) + goto err_no_mem; + /* Vendor Extensions */ for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { if (wpa_s->conf->wps_vendor_ext[i] == NULL) @@ -1051,6 +1100,26 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config( 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, "IpAddrGo") == 0) { + if (!wpas_dbus_validate_dbus_ipaddr(entry)) + goto error; + os_memcpy(wpa_s->conf->ip_addr_go, + entry.bytearray_value, 4); + } else if (os_strcmp(entry.key, "IpAddrMask") == 0) { + if (!wpas_dbus_validate_dbus_ipaddr(entry)) + goto error; + os_memcpy(wpa_s->conf->ip_addr_mask, + entry.bytearray_value, 4); + } else if (os_strcmp(entry.key, "IpAddrStart") == 0) { + if (!wpas_dbus_validate_dbus_ipaddr(entry)) + goto error; + os_memcpy(wpa_s->conf->ip_addr_start, + entry.bytearray_value, 4); + } else if (os_strcmp(entry.key, "IpAddrEnd") == 0) { + if (!wpas_dbus_validate_dbus_ipaddr(entry)) + goto error; + os_memcpy(wpa_s->conf->ip_addr_end, + entry.bytearray_value, 4); } else if (os_strcmp(entry.key, "GroupIdle") == 0 && entry.type == DBUS_TYPE_UINT32) wpa_s->conf->p2p_group_idle = entry.uint32_value; @@ -2286,19 +2355,12 @@ dbus_bool_t wpas_dbus_getter_p2p_group_passphrase( DBusMessageIter *iter, DBusError *error, void *user_data) { struct wpa_supplicant *wpa_s = user_data; - char *p_pass; struct wpa_ssid *ssid = wpa_s->current_ssid; if (ssid == NULL) return FALSE; - p_pass = ssid->passphrase; - if (!p_pass) - p_pass = ""; - - return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, - &p_pass, error); - + return wpas_dbus_string_property_getter(iter, ssid->passphrase, error); } diff --git a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c index f16e2290c7ed..f762b3f2ef5c 100644 --- a/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c +++ b/contrib/wpa/wpa_supplicant/dbus/dbus_new_handlers_wps.c @@ -412,12 +412,10 @@ 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); + return wpas_dbus_string_property_getter(iter, + wpa_s->conf->config_methods, + error); } @@ -454,3 +452,349 @@ dbus_bool_t wpas_dbus_setter_config_methods( return TRUE; } + + +/** + * wpas_dbus_getter_wps_device_name - Get current WPS device name + * @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 "DeviceName" property. + */ +dbus_bool_t wpas_dbus_getter_wps_device_name( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + + return wpas_dbus_string_property_getter(iter, wpa_s->conf->device_name, + error); +} + + +/** + * wpas_dbus_setter_wps_device_name - Set current WPS device name + * @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 "DeviceName" property. + */ +dbus_bool_t wpas_dbus_setter_wps_device_name( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + char *methods, *devname; + + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING, + &methods)) + return FALSE; + + if (os_strlen(methods) > WPS_DEV_NAME_MAX_LEN) + return FALSE; + + devname = os_strdup(methods); + if (!devname) + return FALSE; + + os_free(wpa_s->conf->device_name); + wpa_s->conf->device_name = devname; + wpa_s->conf->changed_parameters |= CFG_CHANGED_DEVICE_NAME; + wpa_supplicant_update_config(wpa_s); + + return TRUE; +} + + +/** + * wpas_dbus_getter_wps_manufacturer - Get current manufacturer name + * @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 "Manufacturer" property. + */ +dbus_bool_t wpas_dbus_getter_wps_manufacturer( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + + return wpas_dbus_string_property_getter(iter, wpa_s->conf->manufacturer, + error); +} + + +/** + * wpas_dbus_setter_wps_manufacturer - Set current manufacturer name + * @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 "Manufacturer" property. + */ +dbus_bool_t wpas_dbus_setter_wps_manufacturer( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + char *methods, *manufacturer; + + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING, + &methods)) + return FALSE; + + if (os_strlen(methods) > WPS_MANUFACTURER_MAX_LEN) + return FALSE; + + manufacturer = os_strdup(methods); + if (!manufacturer) + return FALSE; + + os_free(wpa_s->conf->manufacturer); + wpa_s->conf->manufacturer = manufacturer; + wpa_s->conf->changed_parameters |= CFG_CHANGED_WPS_STRING; + wpa_supplicant_update_config(wpa_s); + + return TRUE; +} + + +/** + * wpas_dbus_getter_wps_device_model_name - Get current device model name + * @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 "ModelName" property. + */ +dbus_bool_t wpas_dbus_getter_wps_device_model_name( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + + return wpas_dbus_string_property_getter(iter, wpa_s->conf->model_name, + error); +} + + +/** + * wpas_dbus_setter_wps_device_model_name - Set current device model name + * @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 "ModelName" property. + */ +dbus_bool_t wpas_dbus_setter_wps_device_model_name( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + char *methods, *model_name; + + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING, + &methods)) + return FALSE; + + if (os_strlen(methods) > WPS_MODEL_NAME_MAX_LEN) + return FALSE; + + model_name = os_strdup(methods); + if (!model_name) + return FALSE; + os_free(wpa_s->conf->model_name); + wpa_s->conf->model_name = model_name; + wpa_s->conf->changed_parameters |= CFG_CHANGED_WPS_STRING; + wpa_supplicant_update_config(wpa_s); + + return TRUE; +} + + +/** + * wpas_dbus_getter_wps_device_model_number - Get current device model number + * @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 "ModelNumber" property. + */ +dbus_bool_t wpas_dbus_getter_wps_device_model_number( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + + return wpas_dbus_string_property_getter(iter, wpa_s->conf->model_number, + error); +} + + +/** + * wpas_dbus_setter_wps_device_model_number - Set current device model number + * @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 "ModelNumber" property. + */ +dbus_bool_t wpas_dbus_setter_wps_device_model_number( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + char *methods, *model_number; + + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING, + &methods)) + return FALSE; + + if (os_strlen(methods) > WPS_MODEL_NUMBER_MAX_LEN) + return FALSE; + + model_number = os_strdup(methods); + if (!model_number) + return FALSE; + + os_free(wpa_s->conf->model_number); + wpa_s->conf->model_number = model_number; + wpa_s->conf->changed_parameters |= CFG_CHANGED_WPS_STRING; + wpa_supplicant_update_config(wpa_s); + + return TRUE; +} + + +/** + * wpas_dbus_getter_wps_device_serial_number - Get current device serial number + * @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 "SerialNumber" property. + */ +dbus_bool_t wpas_dbus_getter_wps_device_serial_number( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + + return wpas_dbus_string_property_getter(iter, + wpa_s->conf->serial_number, + error); +} + + +/** + * wpas_dbus_setter_wps_device_serial_number - Set current device serial number + * @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 "SerialNumber" property. + */ +dbus_bool_t wpas_dbus_setter_wps_device_serial_number( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + char *methods, *serial_number; + + if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING, + &methods)) + return FALSE; + + if (os_strlen(methods) > WPS_SERIAL_NUMBER_MAX_LEN) + return FALSE; + + serial_number = os_strdup(methods); + if (!serial_number) + return FALSE; + os_free(wpa_s->conf->serial_number); + wpa_s->conf->serial_number = serial_number; + wpa_s->conf->changed_parameters |= CFG_CHANGED_WPS_STRING; + wpa_supplicant_update_config(wpa_s); + + return TRUE; +} + + +/** + * wpas_dbus_getter_wps_device_device_type - Get current device type + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Getter for "DeviceType" property. + */ +dbus_bool_t wpas_dbus_getter_wps_device_device_type( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + + if (!wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE, + (char *) + wpa_s->conf->device_type, + WPS_DEV_TYPE_LEN, error)) { + dbus_set_error(error, DBUS_ERROR_FAILED, + "%s: error constructing reply", __func__); + return FALSE; + } + + return TRUE; +} + + +/** + * wpas_dbus_setter_wps_device_device_type - Set current device type + * @iter: Pointer to incoming dbus message iter + * @error: Location to store error on failure + * @user_data: Function specific data + * Returns: TRUE on success, FALSE on failure + * + * Setter for "DeviceType" property. + */ +dbus_bool_t wpas_dbus_setter_wps_device_device_type( + const struct wpa_dbus_property_desc *property_desc, + DBusMessageIter *iter, DBusError *error, void *user_data) +{ + struct wpa_supplicant *wpa_s = user_data; + u8 *dev_type; + int dev_len; + DBusMessageIter variant, array_iter; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) + return FALSE; + + dbus_message_iter_recurse(iter, &variant); + if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY) + return FALSE; + + dbus_message_iter_recurse(&variant, &array_iter); + dbus_message_iter_get_fixed_array(&array_iter, &dev_type, &dev_len); + + if (dev_len != WPS_DEV_TYPE_LEN) + return FALSE; + + os_memcpy(wpa_s->conf->device_type, dev_type, WPS_DEV_TYPE_LEN); + wpa_s->conf->changed_parameters |= CFG_CHANGED_DEVICE_TYPE; + wpa_supplicant_update_config(wpa_s); + + return TRUE; +} diff --git a/contrib/wpa/wpa_supplicant/defconfig b/contrib/wpa/wpa_supplicant/defconfig index 1d05198f849a..af281e56d2e1 100644 --- a/contrib/wpa/wpa_supplicant/defconfig +++ b/contrib/wpa/wpa_supplicant/defconfig @@ -1,9 +1,9 @@ # Example wpa_supplicant build time configuration # # This file lists the configuration options that are used when building the -# hostapd binary. All lines starting with # are ignored. Configuration option -# lines must be commented out complete, if they are not to be included, i.e., -# just setting VARIABLE=n is not disabling that variable. +# wpa_supplicant binary. All lines starting with # are ignored. Configuration +# option lines must be commented out complete, if they are not to be included, +# i.e., just setting VARIABLE=n is not disabling that variable. # # This file is included in Makefile, so variables like CFLAGS and LIBS can also # be modified from here. In most cases, these lines should use += in order not @@ -44,7 +44,7 @@ CONFIG_DRIVER_NL80211=y #CONFIG_LIBNL20=y # Use libnl 3.2 libraries (if this is selected, CONFIG_LIBNL20 is ignored) -#CONFIG_LIBNL32=y +CONFIG_LIBNL32=y # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) @@ -73,6 +73,12 @@ CONFIG_DRIVER_NL80211=y # Driver interface for wired Ethernet drivers CONFIG_DRIVER_WIRED=y +# Driver interface for MACsec capable Qualcomm Atheros drivers +#CONFIG_DRIVER_MACSEC_QCA=y + +# Driver interface for Linux MACsec drivers +#CONFIG_DRIVER_MACSEC_LINUX=y + # Driver interface for the Broadcom RoboSwitch family #CONFIG_DRIVER_ROBOSWITCH=y @@ -83,8 +89,8 @@ CONFIG_DRIVER_WIRED=y #LIBS += -lsocket -ldlpi -lnsl #LIBS_c += -lsocket -# Enable IEEE 802.1X Supplicant (automatically included if any EAP method is -# included) +# Enable IEEE 802.1X Supplicant (automatically included if any EAP method or +# MACsec is included) CONFIG_IEEE8021X_EAPOL=y # EAP-MD5 @@ -166,6 +172,9 @@ CONFIG_EAP_LEAP=y # EAP-EKE #CONFIG_EAP_EKE=y +# MACsec +#CONFIG_MACSEC=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 @@ -288,9 +297,6 @@ CONFIG_BACKEND=file # bridge interfaces (commit 'bridge: respect RFC2863 operational state')'). #CONFIG_NO_LINUX_PACKET_SOCKET_WAR=y -# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) -CONFIG_PEERKEY=y - # IEEE 802.11w (management frame protection), also known as PMF # Driver support is also needed for IEEE 802.11w. #CONFIG_IEEE80211W=y @@ -299,6 +305,7 @@ CONFIG_PEERKEY=y # openssl = OpenSSL (default) # gnutls = GnuTLS # internal = Internal TLSv1 implementation (experimental) +# linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental) # none = Empty template #CONFIG_TLS=openssl @@ -316,6 +323,10 @@ CONFIG_PEERKEY=y # will be used) #CONFIG_TLSV12=y +# Select which ciphers to use by default with OpenSSL if the user does not +# specify them. +#CONFIG_TLS_DEFAULT_CIPHERS="DEFAULT:!EXP:!LOW" + # If CONFIG_TLS=internal is used, additional library and include paths are # needed for LibTomMath. Alternatively, an integrated, minimal version of # LibTomMath can be used. See beginning of libtommath.c for details on benefits @@ -370,7 +381,7 @@ CONFIG_PEERKEY=y # amount of memory/flash. #CONFIG_DYNAMIC_EAP_METHODS=y -# IEEE Std 802.11r-2008 (Fast BSS Transition) +# IEEE Std 802.11r-2008 (Fast BSS Transition) for station mode #CONFIG_IEEE80211R=y # Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt) @@ -548,3 +559,37 @@ CONFIG_PEERKEY=y # Support Multi Band Operation #CONFIG_MBO=y + +# Fast Initial Link Setup (FILS) (IEEE 802.11ai) +# Note: This is an experimental and not yet complete implementation. This +# should not be enabled for production use. +#CONFIG_FILS=y +# FILS shared key authentication with PFS +#CONFIG_FILS_SK_PFS=y + +# Support RSN on IBSS networks +# This is needed to be able to use mode=1 network profile with proto=RSN and +# key_mgmt=WPA-PSK (i.e., full key management instead of WPA-None). +#CONFIG_IBSS_RSN=y + +# External PMKSA cache control +# This can be used to enable control interface commands that allow the current +# PMKSA cache entries to be fetched and new entries to be added. +#CONFIG_PMKSA_CACHE_EXTERNAL=y + +# Mesh Networking (IEEE 802.11s) +#CONFIG_MESH=y + +# Background scanning modules +# These can be used to request wpa_supplicant to perform background scanning +# operations for roaming within an ESS (same SSID). See the bgscan parameter in +# the wpa_supplicant.conf file for more details. +# Periodic background scans based on signal strength +#CONFIG_BGSCAN_SIMPLE=y +# Learn channels used by the network and try to avoid bgscans on other +# channels (experimental) +#CONFIG_BGSCAN_LEARN=y + +# Opportunistic Wireless Encryption (OWE) +# Experimental implementation of draft-harkins-owe-07.txt +#CONFIG_OWE=y diff --git a/contrib/wpa/wpa_supplicant/dpp_supplicant.c b/contrib/wpa/wpa_supplicant/dpp_supplicant.c new file mode 100644 index 000000000000..7bc46610a971 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/dpp_supplicant.c @@ -0,0 +1,2613 @@ +/* + * wpa_supplicant - DPP + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation + * + * 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/dpp.h" +#include "common/gas.h" +#include "common/gas_server.h" +#include "rsn_supp/wpa.h" +#include "rsn_supp/pmksa_cache.h" +#include "wpa_supplicant_i.h" +#include "config.h" +#include "driver_i.h" +#include "offchannel.h" +#include "gas_query.h" +#include "bss.h" +#include "scan.h" +#include "notify.h" +#include "dpp_supplicant.h" + + +static int wpas_dpp_listen_start(struct wpa_supplicant *wpa_s, + unsigned int freq); +static void wpas_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx); +static void wpas_dpp_auth_success(struct wpa_supplicant *wpa_s, int initiator); +static void wpas_dpp_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); +static void wpas_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx); +static int wpas_dpp_auth_init_next(struct wpa_supplicant *wpa_s); +static void +wpas_dpp_tx_pkex_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); + +static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + +/* Use a hardcoded Transaction ID 1 in Peer Discovery frames since there is only + * a single transaction in progress at any point in time. */ +static const u8 TRANSACTION_ID = 1; + + +static struct dpp_configurator * +dpp_configurator_get_id(struct wpa_supplicant *wpa_s, unsigned int id) +{ + struct dpp_configurator *conf; + + dl_list_for_each(conf, &wpa_s->dpp_configurator, + struct dpp_configurator, list) { + if (conf->id == id) + return conf; + } + return NULL; +} + + +static unsigned int wpas_dpp_next_id(struct wpa_supplicant *wpa_s) +{ + struct dpp_bootstrap_info *bi; + unsigned int max_id = 0; + + dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info, + list) { + if (bi->id > max_id) + max_id = bi->id; + } + return max_id + 1; +} + + +/** + * wpas_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code + * @wpa_s: Pointer to wpa_supplicant data + * @cmd: DPP URI read from a QR Code + * Returns: Identifier of the stored info or -1 on failure + */ +int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd) +{ + struct dpp_bootstrap_info *bi; + struct dpp_authentication *auth = wpa_s->dpp_auth; + + bi = dpp_parse_qr_code(cmd); + if (!bi) + return -1; + + bi->id = wpas_dpp_next_id(wpa_s); + dl_list_add(&wpa_s->dpp_bootstrap, &bi->list); + + if (auth && auth->response_pending && + dpp_notify_new_qr_code(auth, bi) == 1) { + wpa_printf(MSG_DEBUG, + "DPP: Sending out pending authentication response"); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", + MAC2STR(auth->peer_mac_addr), auth->curr_freq, + DPP_PA_AUTHENTICATION_RESP); + offchannel_send_action(wpa_s, auth->curr_freq, + auth->peer_mac_addr, wpa_s->own_addr, + broadcast, + wpabuf_head(auth->resp_msg), + wpabuf_len(auth->resp_msg), + 500, wpas_dpp_tx_status, 0); + } + + return bi->id; +} + + +static char * get_param(const char *cmd, const char *param) +{ + const char *pos, *end; + char *val; + size_t len; + + pos = os_strstr(cmd, param); + if (!pos) + return NULL; + + pos += os_strlen(param); + end = os_strchr(pos, ' '); + if (end) + len = end - pos; + else + len = os_strlen(pos); + val = os_malloc(len + 1); + if (!val) + return NULL; + os_memcpy(val, pos, len); + val[len] = '\0'; + return val; +} + + +int wpas_dpp_bootstrap_gen(struct wpa_supplicant *wpa_s, const char *cmd) +{ + char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL; + char *key = NULL; + u8 *privkey = NULL; + size_t privkey_len = 0; + size_t len; + int ret = -1; + struct dpp_bootstrap_info *bi; + + bi = os_zalloc(sizeof(*bi)); + if (!bi) + goto fail; + + if (os_strstr(cmd, "type=qrcode")) + bi->type = DPP_BOOTSTRAP_QR_CODE; + else if (os_strstr(cmd, "type=pkex")) + bi->type = DPP_BOOTSTRAP_PKEX; + else + goto fail; + + chan = get_param(cmd, " chan="); + mac = get_param(cmd, " mac="); + info = get_param(cmd, " info="); + curve = get_param(cmd, " curve="); + key = get_param(cmd, " key="); + + if (key) { + privkey_len = os_strlen(key) / 2; + privkey = os_malloc(privkey_len); + if (!privkey || + hexstr2bin(key, privkey, privkey_len) < 0) + goto fail; + } + + pk = dpp_keygen(bi, curve, privkey, privkey_len); + if (!pk) + goto fail; + + len = 4; /* "DPP:" */ + if (chan) { + if (dpp_parse_uri_chan_list(bi, chan) < 0) + goto fail; + len += 3 + os_strlen(chan); /* C:...; */ + } + if (mac) { + if (dpp_parse_uri_mac(bi, mac) < 0) + goto fail; + len += 3 + os_strlen(mac); /* M:...; */ + } + if (info) { + if (dpp_parse_uri_info(bi, info) < 0) + goto fail; + len += 3 + os_strlen(info); /* I:...; */ + } + len += 4 + os_strlen(pk); + bi->uri = os_malloc(len + 1); + if (!bi->uri) + goto fail; + os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;", + chan ? "C:" : "", chan ? chan : "", chan ? ";" : "", + mac ? "M:" : "", mac ? mac : "", mac ? ";" : "", + info ? "I:" : "", info ? info : "", info ? ";" : "", + pk); + bi->id = wpas_dpp_next_id(wpa_s); + dl_list_add(&wpa_s->dpp_bootstrap, &bi->list); + ret = bi->id; + bi = NULL; +fail: + os_free(curve); + os_free(pk); + os_free(chan); + os_free(mac); + os_free(info); + str_clear_free(key); + bin_clear_free(privkey, privkey_len); + dpp_bootstrap_info_free(bi); + return ret; +} + + +static struct dpp_bootstrap_info * +dpp_bootstrap_get_id(struct wpa_supplicant *wpa_s, unsigned int id) +{ + struct dpp_bootstrap_info *bi; + + dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info, + list) { + if (bi->id == id) + return bi; + } + return NULL; +} + + +static int dpp_bootstrap_del(struct wpa_supplicant *wpa_s, unsigned int id) +{ + struct dpp_bootstrap_info *bi, *tmp; + int found = 0; + + dl_list_for_each_safe(bi, tmp, &wpa_s->dpp_bootstrap, + struct dpp_bootstrap_info, list) { + if (id && bi->id != id) + continue; + found = 1; + dl_list_del(&bi->list); + dpp_bootstrap_info_free(bi); + } + + if (id == 0) + return 0; /* flush succeeds regardless of entries found */ + return found ? 0 : -1; +} + + +int wpas_dpp_bootstrap_remove(struct wpa_supplicant *wpa_s, const char *id) +{ + unsigned int id_val; + + if (os_strcmp(id, "*") == 0) { + id_val = 0; + } else { + id_val = atoi(id); + if (id_val == 0) + return -1; + } + + return dpp_bootstrap_del(wpa_s, id_val); +} + + +const char * wpas_dpp_bootstrap_get_uri(struct wpa_supplicant *wpa_s, + unsigned int id) +{ + struct dpp_bootstrap_info *bi; + + bi = dpp_bootstrap_get_id(wpa_s, id); + if (!bi) + return NULL; + return bi->uri; +} + + +int wpas_dpp_bootstrap_info(struct wpa_supplicant *wpa_s, int id, + char *reply, int reply_size) +{ + struct dpp_bootstrap_info *bi; + + bi = dpp_bootstrap_get_id(wpa_s, id); + if (!bi) + return -1; + return os_snprintf(reply, reply_size, "type=%s\n" + "mac_addr=" MACSTR "\n" + "info=%s\n" + "num_freq=%u\n" + "curve=%s\n", + dpp_bootstrap_type_txt(bi->type), + MAC2STR(bi->mac_addr), + bi->info ? bi->info : "", + bi->num_freq, + bi->curve->name); +} + + +static void wpas_dpp_auth_resp_retry_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct dpp_authentication *auth = wpa_s->dpp_auth; + + if (!auth || !auth->resp_msg) + return; + + wpa_printf(MSG_DEBUG, + "DPP: Retry Authentication Response after timeout"); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", + MAC2STR(auth->peer_mac_addr), auth->curr_freq, + DPP_PA_AUTHENTICATION_RESP); + offchannel_send_action(wpa_s, auth->curr_freq, auth->peer_mac_addr, + wpa_s->own_addr, broadcast, + wpabuf_head(auth->resp_msg), + wpabuf_len(auth->resp_msg), + 500, wpas_dpp_tx_status, 0); +} + + +static void wpas_dpp_auth_resp_retry(struct wpa_supplicant *wpa_s) +{ + struct dpp_authentication *auth = wpa_s->dpp_auth; + unsigned int wait_time, max_tries; + + if (!auth || !auth->resp_msg) + return; + + if (wpa_s->dpp_resp_max_tries) + max_tries = wpa_s->dpp_resp_max_tries; + else + max_tries = 5; + auth->auth_resp_tries++; + if (auth->auth_resp_tries >= max_tries) { + wpa_printf(MSG_INFO, "DPP: No confirm received from initiator - stopping exchange"); + offchannel_send_action_done(wpa_s); + dpp_auth_deinit(wpa_s->dpp_auth); + wpa_s->dpp_auth = NULL; + return; + } + + if (wpa_s->dpp_resp_retry_time) + wait_time = wpa_s->dpp_resp_retry_time; + else + wait_time = 1000; + wpa_printf(MSG_DEBUG, + "DPP: Schedule retransmission of Authentication Response frame in %u ms", + wait_time); + eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s, NULL); + eloop_register_timeout(wait_time / 1000, + (wait_time % 1000) * 1000, + wpas_dpp_auth_resp_retry_timeout, wpa_s, NULL); +} + + +static void wpas_dpp_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) +{ + const char *res_txt; + struct dpp_authentication *auth = wpa_s->dpp_auth; + + res_txt = result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" : + (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" : + "FAILED"); + wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR + " result=%s", freq, MAC2STR(dst), res_txt); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR + " freq=%u result=%s", MAC2STR(dst), freq, res_txt); + + if (!wpa_s->dpp_auth) { + wpa_printf(MSG_DEBUG, + "DPP: Ignore TX status since there is no ongoing authentication exchange"); + return; + } + + if (wpa_s->dpp_auth->remove_on_tx_status) { + wpa_printf(MSG_DEBUG, + "DPP: Terminate authentication exchange due to an earlier error"); + eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s, + NULL); + offchannel_send_action_done(wpa_s); + dpp_auth_deinit(wpa_s->dpp_auth); + wpa_s->dpp_auth = NULL; + return; + } + + if (wpa_s->dpp_auth_ok_on_ack) + wpas_dpp_auth_success(wpa_s, 1); + + if (!is_broadcast_ether_addr(dst) && + result != OFFCHANNEL_SEND_ACTION_SUCCESS) { + wpa_printf(MSG_DEBUG, + "DPP: Unicast DPP Action frame was not ACKed"); + if (auth->waiting_auth_resp) { + /* In case of DPP Authentication Request frame, move to + * the next channel immediately. */ + offchannel_send_action_done(wpa_s); + wpas_dpp_auth_init_next(wpa_s); + return; + } + if (auth->waiting_auth_conf) { + wpas_dpp_auth_resp_retry(wpa_s); + return; + } + } + + if (!is_broadcast_ether_addr(dst) && auth->waiting_auth_resp && + result == OFFCHANNEL_SEND_ACTION_SUCCESS) { + /* Allow timeout handling to stop iteration if no response is + * received from a peer that has ACKed a request. */ + auth->auth_req_ack = 1; + } + + if (!wpa_s->dpp_auth_ok_on_ack && wpa_s->dpp_auth->neg_freq > 0 && + wpa_s->dpp_auth->curr_freq != wpa_s->dpp_auth->neg_freq) { + wpa_printf(MSG_DEBUG, + "DPP: Move from curr_freq %u MHz to neg_freq %u MHz for response", + wpa_s->dpp_auth->curr_freq, + wpa_s->dpp_auth->neg_freq); + offchannel_send_action_done(wpa_s); + wpas_dpp_listen_start(wpa_s, wpa_s->dpp_auth->neg_freq); + } + + if (wpa_s->dpp_auth_ok_on_ack) + wpa_s->dpp_auth_ok_on_ack = 0; +} + + +static void wpas_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct dpp_authentication *auth = wpa_s->dpp_auth; + unsigned int freq; + struct os_reltime now, diff; + unsigned int wait_time, diff_ms; + + if (!auth || !auth->waiting_auth_resp) + return; + + wait_time = wpa_s->dpp_resp_wait_time ? + wpa_s->dpp_resp_wait_time : 2000; + os_get_reltime(&now); + os_reltime_sub(&now, &wpa_s->dpp_last_init, &diff); + diff_ms = diff.sec * 1000 + diff.usec / 1000; + wpa_printf(MSG_DEBUG, + "DPP: Reply wait timeout - wait_time=%u diff_ms=%u", + wait_time, diff_ms); + + if (auth->auth_req_ack && diff_ms >= wait_time) { + /* Peer ACK'ed Authentication Request frame, but did not reply + * with Authentication Response frame within two seconds. */ + wpa_printf(MSG_INFO, + "DPP: No response received from responder - stopping initiation attempt"); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_INIT_FAILED); + offchannel_send_action_done(wpa_s); + wpas_dpp_listen_stop(wpa_s); + dpp_auth_deinit(auth); + wpa_s->dpp_auth = NULL; + return; + } + + if (diff_ms >= wait_time) { + /* Authentication Request frame was not ACK'ed and no reply + * was receiving within two seconds. */ + wpa_printf(MSG_DEBUG, + "DPP: Continue Initiator channel iteration"); + offchannel_send_action_done(wpa_s); + wpas_dpp_listen_stop(wpa_s); + wpas_dpp_auth_init_next(wpa_s); + return; + } + + /* Driver did not support 2000 ms long wait_time with TX command, so + * schedule listen operation to continue waiting for the response. + * + * DPP listen operations continue until stopped, so simply schedule a + * new call to this function at the point when the two second reply + * wait has expired. */ + wait_time -= diff_ms; + + freq = auth->curr_freq; + if (auth->neg_freq > 0) + freq = auth->neg_freq; + wpa_printf(MSG_DEBUG, + "DPP: Continue reply wait on channel %u MHz for %u ms", + freq, wait_time); + wpa_s->dpp_in_response_listen = 1; + wpas_dpp_listen_start(wpa_s, freq); + + eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000, + wpas_dpp_reply_wait_timeout, wpa_s, NULL); +} + + +static void wpas_dpp_set_testing_options(struct wpa_supplicant *wpa_s, + struct dpp_authentication *auth) +{ +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->dpp_config_obj_override) + auth->config_obj_override = + os_strdup(wpa_s->dpp_config_obj_override); + if (wpa_s->dpp_discovery_override) + auth->discovery_override = + os_strdup(wpa_s->dpp_discovery_override); + if (wpa_s->dpp_groups_override) + auth->groups_override = + os_strdup(wpa_s->dpp_groups_override); + auth->ignore_netaccesskey_mismatch = + wpa_s->dpp_ignore_netaccesskey_mismatch; +#endif /* CONFIG_TESTING_OPTIONS */ +} + + +static int wpas_dpp_set_configurator(struct wpa_supplicant *wpa_s, + struct dpp_authentication *auth, + const char *cmd) +{ + const char *pos, *end; + struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL; + struct dpp_configurator *conf = NULL; + u8 ssid[32] = { "test" }; + size_t ssid_len = 4; + char pass[64] = { }; + size_t pass_len = 0; + u8 psk[PMK_LEN]; + int psk_set = 0; + char *group_id = NULL; + + if (!cmd) + return 0; + + wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd); + pos = os_strstr(cmd, " ssid="); + if (pos) { + pos += 6; + end = os_strchr(pos, ' '); + ssid_len = end ? (size_t) (end - pos) : os_strlen(pos); + ssid_len /= 2; + if (ssid_len > sizeof(ssid) || + hexstr2bin(pos, ssid, ssid_len) < 0) + goto fail; + } + + pos = os_strstr(cmd, " pass="); + if (pos) { + pos += 6; + end = os_strchr(pos, ' '); + pass_len = end ? (size_t) (end - pos) : os_strlen(pos); + pass_len /= 2; + if (pass_len > sizeof(pass) - 1 || pass_len < 8 || + hexstr2bin(pos, (u8 *) pass, pass_len) < 0) + goto fail; + } + + pos = os_strstr(cmd, " psk="); + if (pos) { + pos += 5; + if (hexstr2bin(pos, psk, PMK_LEN) < 0) + goto fail; + psk_set = 1; + } + + pos = os_strstr(cmd, " group_id="); + if (pos) { + size_t group_id_len; + + pos += 10; + end = os_strchr(pos, ' '); + group_id_len = end ? (size_t) (end - pos) : os_strlen(pos); + group_id = os_malloc(group_id_len + 1); + if (!group_id) + goto fail; + os_memcpy(group_id, pos, group_id_len); + group_id[group_id_len] = '\0'; + } + + if (os_strstr(cmd, " conf=sta-")) { + conf_sta = os_zalloc(sizeof(struct dpp_configuration)); + if (!conf_sta) + goto fail; + os_memcpy(conf_sta->ssid, ssid, ssid_len); + conf_sta->ssid_len = ssid_len; + if (os_strstr(cmd, " conf=sta-psk") || + os_strstr(cmd, " conf=sta-sae") || + os_strstr(cmd, " conf=sta-psk-sae")) { + if (os_strstr(cmd, " conf=sta-psk-sae")) + conf_sta->akm = DPP_AKM_PSK_SAE; + else if (os_strstr(cmd, " conf=sta-sae")) + conf_sta->akm = DPP_AKM_SAE; + else + conf_sta->akm = DPP_AKM_PSK; + if (psk_set) { + os_memcpy(conf_sta->psk, psk, PMK_LEN); + } else if (pass_len > 0) { + conf_sta->passphrase = os_strdup(pass); + if (!conf_sta->passphrase) + goto fail; + } else { + goto fail; + } + } else if (os_strstr(cmd, " conf=sta-dpp")) { + conf_sta->akm = DPP_AKM_DPP; + } else { + goto fail; + } + if (os_strstr(cmd, " group_id=")) { + conf_sta->group_id = group_id; + group_id = NULL; + } + } + + if (os_strstr(cmd, " conf=ap-")) { + conf_ap = os_zalloc(sizeof(struct dpp_configuration)); + if (!conf_ap) + goto fail; + os_memcpy(conf_ap->ssid, ssid, ssid_len); + conf_ap->ssid_len = ssid_len; + if (os_strstr(cmd, " conf=ap-psk") || + os_strstr(cmd, " conf=ap-sae") || + os_strstr(cmd, " conf=ap-psk-sae")) { + if (os_strstr(cmd, " conf=ap-psk-sae")) + conf_ap->akm = DPP_AKM_PSK_SAE; + else if (os_strstr(cmd, " conf=ap-sae")) + conf_ap->akm = DPP_AKM_SAE; + else + conf_ap->akm = DPP_AKM_PSK; + if (psk_set) { + os_memcpy(conf_ap->psk, psk, PMK_LEN); + } else { + conf_ap->passphrase = os_strdup(pass); + if (!conf_ap->passphrase) + goto fail; + } + } else if (os_strstr(cmd, " conf=ap-dpp")) { + conf_ap->akm = DPP_AKM_DPP; + } else { + goto fail; + } + if (os_strstr(cmd, " group_id=")) { + conf_ap->group_id = group_id; + group_id = NULL; + } + } + + pos = os_strstr(cmd, " expiry="); + if (pos) { + long int val; + + pos += 8; + val = strtol(pos, NULL, 0); + if (val <= 0) + goto fail; + if (conf_sta) + conf_sta->netaccesskey_expiry = val; + if (conf_ap) + conf_ap->netaccesskey_expiry = val; + } + + pos = os_strstr(cmd, " configurator="); + if (pos) { + pos += 14; + conf = dpp_configurator_get_id(wpa_s, atoi(pos)); + if (!conf) { + wpa_printf(MSG_INFO, + "DPP: Could not find the specified configurator"); + goto fail; + } + } + auth->conf_sta = conf_sta; + auth->conf_ap = conf_ap; + auth->conf = conf; + os_free(group_id); + return 0; + +fail: + wpa_msg(wpa_s, MSG_INFO, "DPP: Failed to set configurator parameters"); + dpp_configuration_free(conf_sta); + dpp_configuration_free(conf_ap); + os_free(group_id); + return -1; +} + + +static void wpas_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (!wpa_s->dpp_auth) + return; + wpa_printf(MSG_DEBUG, "DPP: Retry initiation after timeout"); + wpas_dpp_auth_init_next(wpa_s); +} + + +static int wpas_dpp_auth_init_next(struct wpa_supplicant *wpa_s) +{ + struct dpp_authentication *auth = wpa_s->dpp_auth; + const u8 *dst; + unsigned int wait_time, max_wait_time, freq, max_tries, used; + struct os_reltime now, diff; + + wpa_s->dpp_in_response_listen = 0; + if (!auth) + return -1; + + if (auth->freq_idx == 0) + os_get_reltime(&wpa_s->dpp_init_iter_start); + + if (auth->freq_idx >= auth->num_freq) { + auth->num_freq_iters++; + if (wpa_s->dpp_init_max_tries) + max_tries = wpa_s->dpp_init_max_tries; + else + max_tries = 5; + if (auth->num_freq_iters >= max_tries || auth->auth_req_ack) { + wpa_printf(MSG_INFO, + "DPP: No response received from responder - stopping initiation attempt"); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_INIT_FAILED); + eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, + wpa_s, NULL); + offchannel_send_action_done(wpa_s); + dpp_auth_deinit(wpa_s->dpp_auth); + wpa_s->dpp_auth = NULL; + return -1; + } + auth->freq_idx = 0; + eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL); + if (wpa_s->dpp_init_retry_time) + wait_time = wpa_s->dpp_init_retry_time; + else + wait_time = 10000; + os_get_reltime(&now); + os_reltime_sub(&now, &wpa_s->dpp_init_iter_start, &diff); + used = diff.sec * 1000 + diff.usec / 1000; + if (used > wait_time) + wait_time = 0; + else + wait_time -= used; + wpa_printf(MSG_DEBUG, "DPP: Next init attempt in %u ms", + wait_time); + eloop_register_timeout(wait_time / 1000, + (wait_time % 1000) * 1000, + wpas_dpp_init_timeout, wpa_s, + NULL); + return 0; + } + freq = auth->freq[auth->freq_idx++]; + auth->curr_freq = freq; + + if (is_zero_ether_addr(auth->peer_bi->mac_addr)) + dst = broadcast; + else + dst = auth->peer_bi->mac_addr; + wpa_s->dpp_auth_ok_on_ack = 0; + eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); + wait_time = wpa_s->max_remain_on_chan; + max_wait_time = wpa_s->dpp_resp_wait_time ? + wpa_s->dpp_resp_wait_time : 2000; + if (wait_time > max_wait_time) + wait_time = max_wait_time; + wait_time += 10; /* give the driver some extra time to complete */ + eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000, + wpas_dpp_reply_wait_timeout, + wpa_s, NULL); + wait_time -= 10; + if (auth->neg_freq > 0 && freq != auth->neg_freq) { + wpa_printf(MSG_DEBUG, + "DPP: Initiate on %u MHz and move to neg_freq %u MHz for response", + freq, auth->neg_freq); + } + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", + MAC2STR(dst), freq, DPP_PA_AUTHENTICATION_REQ); + auth->auth_req_ack = 0; + os_get_reltime(&wpa_s->dpp_last_init); + return offchannel_send_action(wpa_s, freq, dst, + wpa_s->own_addr, broadcast, + wpabuf_head(auth->req_msg), + wpabuf_len(auth->req_msg), + wait_time, wpas_dpp_tx_status, 0); +} + + +int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd) +{ + const char *pos; + struct dpp_bootstrap_info *peer_bi, *own_bi = NULL; + u8 allowed_roles = DPP_CAPAB_CONFIGURATOR; + unsigned int neg_freq = 0; + + wpa_s->dpp_gas_client = 0; + + pos = os_strstr(cmd, " peer="); + if (!pos) + return -1; + pos += 6; + peer_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos)); + if (!peer_bi) { + wpa_printf(MSG_INFO, + "DPP: Could not find bootstrapping info for the identified peer"); + return -1; + } + + pos = os_strstr(cmd, " own="); + if (pos) { + pos += 5; + own_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos)); + if (!own_bi) { + wpa_printf(MSG_INFO, + "DPP: Could not find bootstrapping info for the identified local entry"); + return -1; + } + + if (peer_bi->curve != own_bi->curve) { + wpa_printf(MSG_INFO, + "DPP: Mismatching curves in bootstrapping info (peer=%s own=%s)", + peer_bi->curve->name, own_bi->curve->name); + return -1; + } + } + + pos = os_strstr(cmd, " role="); + if (pos) { + pos += 6; + if (os_strncmp(pos, "configurator", 12) == 0) + allowed_roles = DPP_CAPAB_CONFIGURATOR; + else if (os_strncmp(pos, "enrollee", 8) == 0) + allowed_roles = DPP_CAPAB_ENROLLEE; + else if (os_strncmp(pos, "either", 6) == 0) + allowed_roles = DPP_CAPAB_CONFIGURATOR | + DPP_CAPAB_ENROLLEE; + else + goto fail; + } + + pos = os_strstr(cmd, " netrole="); + if (pos) { + pos += 9; + wpa_s->dpp_netrole_ap = os_strncmp(pos, "ap", 2) == 0; + } + + pos = os_strstr(cmd, " neg_freq="); + if (pos) + neg_freq = atoi(pos + 10); + + if (wpa_s->dpp_auth) { + eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s, + NULL); + offchannel_send_action_done(wpa_s); + dpp_auth_deinit(wpa_s->dpp_auth); + } + wpa_s->dpp_auth = dpp_auth_init(wpa_s, peer_bi, own_bi, allowed_roles, + neg_freq, + wpa_s->hw.modes, wpa_s->hw.num_modes); + if (!wpa_s->dpp_auth) + goto fail; + wpas_dpp_set_testing_options(wpa_s, wpa_s->dpp_auth); + if (wpas_dpp_set_configurator(wpa_s, wpa_s->dpp_auth, cmd) < 0) { + dpp_auth_deinit(wpa_s->dpp_auth); + wpa_s->dpp_auth = NULL; + goto fail; + } + + wpa_s->dpp_auth->neg_freq = neg_freq; + + if (!is_zero_ether_addr(peer_bi->mac_addr)) + os_memcpy(wpa_s->dpp_auth->peer_mac_addr, peer_bi->mac_addr, + ETH_ALEN); + + return wpas_dpp_auth_init_next(wpa_s); +fail: + return -1; +} + + +struct wpas_dpp_listen_work { + unsigned int freq; + unsigned int duration; + struct wpabuf *probe_resp_ie; +}; + + +static void wpas_dpp_listen_work_free(struct wpas_dpp_listen_work *lwork) +{ + if (!lwork) + return; + os_free(lwork); +} + + +static void wpas_dpp_listen_work_done(struct wpa_supplicant *wpa_s) +{ + struct wpas_dpp_listen_work *lwork; + + if (!wpa_s->dpp_listen_work) + return; + + lwork = wpa_s->dpp_listen_work->ctx; + wpas_dpp_listen_work_free(lwork); + radio_work_done(wpa_s->dpp_listen_work); + wpa_s->dpp_listen_work = NULL; +} + + +static void dpp_start_listen_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_supplicant *wpa_s = work->wpa_s; + struct wpas_dpp_listen_work *lwork = work->ctx; + + if (deinit) { + if (work->started) { + wpa_s->dpp_listen_work = NULL; + wpas_dpp_listen_stop(wpa_s); + } + wpas_dpp_listen_work_free(lwork); + return; + } + + wpa_s->dpp_listen_work = work; + + wpa_s->dpp_pending_listen_freq = lwork->freq; + + if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, + wpa_s->max_remain_on_chan) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to request the driver to remain on channel (%u MHz) for listen", + lwork->freq); + wpas_dpp_listen_work_done(wpa_s); + wpa_s->dpp_pending_listen_freq = 0; + return; + } + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = lwork->freq; +} + + +static int wpas_dpp_listen_start(struct wpa_supplicant *wpa_s, + unsigned int freq) +{ + struct wpas_dpp_listen_work *lwork; + + if (wpa_s->dpp_listen_work) { + wpa_printf(MSG_DEBUG, + "DPP: Reject start_listen since dpp_listen_work already exists"); + return -1; + } + + if (wpa_s->dpp_listen_freq) + wpas_dpp_listen_stop(wpa_s); + wpa_s->dpp_listen_freq = freq; + + lwork = os_zalloc(sizeof(*lwork)); + if (!lwork) + return -1; + lwork->freq = freq; + + if (radio_add_work(wpa_s, freq, "dpp-listen", 0, dpp_start_listen_cb, + lwork) < 0) { + wpas_dpp_listen_work_free(lwork); + return -1; + } + + return 0; +} + + +int wpas_dpp_listen(struct wpa_supplicant *wpa_s, const char *cmd) +{ + int freq; + + freq = atoi(cmd); + if (freq <= 0) + return -1; + + if (os_strstr(cmd, " role=configurator")) + wpa_s->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR; + else if (os_strstr(cmd, " role=enrollee")) + wpa_s->dpp_allowed_roles = DPP_CAPAB_ENROLLEE; + else + wpa_s->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | + DPP_CAPAB_ENROLLEE; + wpa_s->dpp_qr_mutual = os_strstr(cmd, " qr=mutual") != NULL; + wpa_s->dpp_netrole_ap = os_strstr(cmd, " netrole=ap") != NULL; + if (wpa_s->dpp_listen_freq == (unsigned int) freq) { + wpa_printf(MSG_DEBUG, "DPP: Already listening on %u MHz", + freq); + return 0; + } + + return wpas_dpp_listen_start(wpa_s, freq); +} + + +void wpas_dpp_listen_stop(struct wpa_supplicant *wpa_s) +{ + wpa_s->dpp_in_response_listen = 0; + if (!wpa_s->dpp_listen_freq) + return; + + wpa_printf(MSG_DEBUG, "DPP: Stop listen on %u MHz", + wpa_s->dpp_listen_freq); + wpa_drv_cancel_remain_on_channel(wpa_s); + wpa_s->dpp_listen_freq = 0; + wpas_dpp_listen_work_done(wpa_s); +} + + +void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq) +{ + wpas_dpp_listen_work_done(wpa_s); + + if (wpa_s->dpp_auth && wpa_s->dpp_in_response_listen) { + unsigned int new_freq; + + /* Continue listen with a new remain-on-channel */ + if (wpa_s->dpp_auth->neg_freq > 0) + new_freq = wpa_s->dpp_auth->neg_freq; + else + new_freq = wpa_s->dpp_auth->curr_freq; + wpa_printf(MSG_DEBUG, + "DPP: Continue wait on %u MHz for the ongoing DPP provisioning session", + new_freq); + wpas_dpp_listen_start(wpa_s, new_freq); + return; + } + + if (wpa_s->dpp_listen_freq) { + /* Continue listen with a new remain-on-channel */ + wpas_dpp_listen_start(wpa_s, wpa_s->dpp_listen_freq); + } +} + + +static void wpas_dpp_rx_auth_req(struct wpa_supplicant *wpa_s, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len, + unsigned int freq) +{ + const u8 *r_bootstrap, *i_bootstrap; + u16 r_bootstrap_len, i_bootstrap_len; + struct dpp_bootstrap_info *bi, *own_bi = NULL, *peer_bi = NULL; + + wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR, + MAC2STR(src)); + + r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH, + &r_bootstrap_len); + if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL + "Missing or invalid required Responder Bootstrapping Key Hash attribute"); + return; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash", + r_bootstrap, r_bootstrap_len); + + i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH, + &i_bootstrap_len); + if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) { + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL + "Missing or invalid required Initiator Bootstrapping Key Hash attribute"); + return; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash", + i_bootstrap, i_bootstrap_len); + + /* Try to find own and peer bootstrapping key matches based on the + * received hash values */ + dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info, + list) { + if (!own_bi && bi->own && + os_memcmp(bi->pubkey_hash, r_bootstrap, + SHA256_MAC_LEN) == 0) { + wpa_printf(MSG_DEBUG, + "DPP: Found matching own bootstrapping information"); + own_bi = bi; + } + + if (!peer_bi && !bi->own && + os_memcmp(bi->pubkey_hash, i_bootstrap, + SHA256_MAC_LEN) == 0) { + wpa_printf(MSG_DEBUG, + "DPP: Found matching peer bootstrapping information"); + peer_bi = bi; + } + + if (own_bi && peer_bi) + break; + } + + if (!own_bi) { + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL + "No matching own bootstrapping key found - ignore message"); + return; + } + + if (wpa_s->dpp_auth) { + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL + "Already in DPP authentication exchange - ignore new one"); + return; + } + + wpa_s->dpp_gas_client = 0; + wpa_s->dpp_auth_ok_on_ack = 0; + wpa_s->dpp_auth = dpp_auth_req_rx(wpa_s, wpa_s->dpp_allowed_roles, + wpa_s->dpp_qr_mutual, + peer_bi, own_bi, freq, hdr, buf, len); + if (!wpa_s->dpp_auth) { + wpa_printf(MSG_DEBUG, "DPP: No response generated"); + return; + } + wpas_dpp_set_testing_options(wpa_s, wpa_s->dpp_auth); + if (wpas_dpp_set_configurator(wpa_s, wpa_s->dpp_auth, + wpa_s->dpp_configurator_params) < 0) { + dpp_auth_deinit(wpa_s->dpp_auth); + wpa_s->dpp_auth = NULL; + return; + } + os_memcpy(wpa_s->dpp_auth->peer_mac_addr, src, ETH_ALEN); + + if (wpa_s->dpp_listen_freq && + wpa_s->dpp_listen_freq != wpa_s->dpp_auth->curr_freq) { + wpa_printf(MSG_DEBUG, + "DPP: Stop listen on %u MHz to allow response on the request %u MHz", + wpa_s->dpp_listen_freq, wpa_s->dpp_auth->curr_freq); + wpas_dpp_listen_stop(wpa_s); + } + + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", + MAC2STR(src), wpa_s->dpp_auth->curr_freq, + DPP_PA_AUTHENTICATION_RESP); + offchannel_send_action(wpa_s, wpa_s->dpp_auth->curr_freq, + src, wpa_s->own_addr, broadcast, + wpabuf_head(wpa_s->dpp_auth->resp_msg), + wpabuf_len(wpa_s->dpp_auth->resp_msg), + 500, wpas_dpp_tx_status, 0); +} + + +static void wpas_dpp_start_gas_server(struct wpa_supplicant *wpa_s) +{ + /* TODO: stop wait and start ROC */ +} + + +static struct wpa_ssid * wpas_dpp_add_network(struct wpa_supplicant *wpa_s, + struct dpp_authentication *auth) +{ + struct wpa_ssid *ssid; + + ssid = wpa_config_add_network(wpa_s->conf); + if (!ssid) + return NULL; + wpas_notify_network_added(wpa_s, ssid); + wpa_config_set_network_defaults(ssid); + ssid->disabled = 1; + + ssid->ssid = os_malloc(auth->ssid_len); + if (!ssid->ssid) + goto fail; + os_memcpy(ssid->ssid, auth->ssid, auth->ssid_len); + ssid->ssid_len = auth->ssid_len; + + if (auth->connector) { + ssid->key_mgmt = WPA_KEY_MGMT_DPP; + ssid->ieee80211w = MGMT_FRAME_PROTECTION_REQUIRED; + ssid->dpp_connector = os_strdup(auth->connector); + if (!ssid->dpp_connector) + goto fail; + } + + if (auth->c_sign_key) { + ssid->dpp_csign = os_malloc(wpabuf_len(auth->c_sign_key)); + if (!ssid->dpp_csign) + goto fail; + os_memcpy(ssid->dpp_csign, wpabuf_head(auth->c_sign_key), + wpabuf_len(auth->c_sign_key)); + ssid->dpp_csign_len = wpabuf_len(auth->c_sign_key); + } + + if (auth->net_access_key) { + ssid->dpp_netaccesskey = + os_malloc(wpabuf_len(auth->net_access_key)); + if (!ssid->dpp_netaccesskey) + goto fail; + os_memcpy(ssid->dpp_netaccesskey, + wpabuf_head(auth->net_access_key), + wpabuf_len(auth->net_access_key)); + ssid->dpp_netaccesskey_len = wpabuf_len(auth->net_access_key); + ssid->dpp_netaccesskey_expiry = auth->net_access_key_expiry; + } + + if (!auth->connector) { + ssid->key_mgmt = 0; + if (auth->akm == DPP_AKM_PSK || auth->akm == DPP_AKM_PSK_SAE) + ssid->key_mgmt |= WPA_KEY_MGMT_PSK | + WPA_KEY_MGMT_PSK_SHA256 | WPA_KEY_MGMT_FT_PSK; + if (auth->akm == DPP_AKM_SAE || auth->akm == DPP_AKM_PSK_SAE) + ssid->key_mgmt |= WPA_KEY_MGMT_SAE | + WPA_KEY_MGMT_FT_SAE; + ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL; + if (auth->passphrase[0]) { + if (wpa_config_set_quoted(ssid, "psk", + auth->passphrase) < 0) + goto fail; + wpa_config_update_psk(ssid); + ssid->export_keys = 1; + } else { + ssid->psk_set = auth->psk_set; + os_memcpy(ssid->psk, auth->psk, PMK_LEN); + } + } + + return ssid; +fail: + wpas_notify_network_removed(wpa_s, ssid); + wpa_config_remove_network(wpa_s->conf, ssid->id); + return NULL; +} + + +static void wpas_dpp_process_config(struct wpa_supplicant *wpa_s, + struct dpp_authentication *auth) +{ + struct wpa_ssid *ssid; + + if (wpa_s->conf->dpp_config_processing < 1) + return; + + ssid = wpas_dpp_add_network(wpa_s, auth); + if (!ssid) + return; + + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_NETWORK_ID "%d", ssid->id); + if (wpa_s->conf->dpp_config_processing < 2) + return; + + wpa_printf(MSG_DEBUG, "DPP: Trying to connect to the new network"); + ssid->disabled = 0; + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + wpa_s->scan_runs = 0; + wpa_s->normal_scans = 0; + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +static void wpas_dpp_handle_config_obj(struct wpa_supplicant *wpa_s, + struct dpp_authentication *auth) +{ + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_RECEIVED); + if (auth->ssid_len) + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONFOBJ_SSID "%s", + wpa_ssid_txt(auth->ssid, auth->ssid_len)); + if (auth->connector) { + /* TODO: Save the Connector and consider using a command + * to fetch the value instead of sending an event with + * it. The Connector could end up being larger than what + * most clients are ready to receive as an event + * message. */ + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONNECTOR "%s", + auth->connector); + } + if (auth->c_sign_key) { + char *hex; + size_t hexlen; + + hexlen = 2 * wpabuf_len(auth->c_sign_key) + 1; + hex = os_malloc(hexlen); + if (hex) { + wpa_snprintf_hex(hex, hexlen, + wpabuf_head(auth->c_sign_key), + wpabuf_len(auth->c_sign_key)); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_C_SIGN_KEY "%s", + hex); + os_free(hex); + } + } + if (auth->net_access_key) { + char *hex; + size_t hexlen; + + hexlen = 2 * wpabuf_len(auth->net_access_key) + 1; + hex = os_malloc(hexlen); + if (hex) { + wpa_snprintf_hex(hex, hexlen, + wpabuf_head(auth->net_access_key), + wpabuf_len(auth->net_access_key)); + if (auth->net_access_key_expiry) + wpa_msg(wpa_s, MSG_INFO, + DPP_EVENT_NET_ACCESS_KEY "%s %lu", hex, + (long unsigned) + auth->net_access_key_expiry); + else + wpa_msg(wpa_s, MSG_INFO, + DPP_EVENT_NET_ACCESS_KEY "%s", hex); + os_free(hex); + } + } + + wpas_dpp_process_config(wpa_s, auth); +} + + +static void wpas_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, + enum gas_query_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code) +{ + struct wpa_supplicant *wpa_s = ctx; + const u8 *pos; + struct dpp_authentication *auth = wpa_s->dpp_auth; + + wpa_s->dpp_gas_dialog_token = -1; + + if (!auth || !auth->auth_success) { + wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); + return; + } + if (result != GAS_QUERY_SUCCESS || + !resp || status_code != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "DPP: GAS query did not succeed"); + goto fail; + } + + wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response adv_proto", + adv_proto); + wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Response (GAS response)", + resp); + + if (wpabuf_len(adv_proto) != 10 || + !(pos = wpabuf_head(adv_proto)) || + pos[0] != WLAN_EID_ADV_PROTO || + pos[1] != 8 || + pos[3] != WLAN_EID_VENDOR_SPECIFIC || + pos[4] != 5 || + WPA_GET_BE24(&pos[5]) != OUI_WFA || + pos[8] != 0x1a || + pos[9] != 1) { + wpa_printf(MSG_DEBUG, + "DPP: Not a DPP Advertisement Protocol ID"); + goto fail; + } + + if (dpp_conf_resp_rx(auth, resp) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed"); + goto fail; + } + + wpas_dpp_handle_config_obj(wpa_s, auth); + dpp_auth_deinit(wpa_s->dpp_auth); + wpa_s->dpp_auth = NULL; + return; + +fail: + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED); + dpp_auth_deinit(wpa_s->dpp_auth); + wpa_s->dpp_auth = NULL; +} + + +static void wpas_dpp_start_gas_client(struct wpa_supplicant *wpa_s) +{ + struct dpp_authentication *auth = wpa_s->dpp_auth; + struct wpabuf *buf, *conf_req; + char json[100]; + int res; + + wpa_s->dpp_gas_client = 1; + os_snprintf(json, sizeof(json), + "{\"name\":\"Test\"," + "\"wi-fi_tech\":\"infra\"," + "\"netRole\":\"%s\"}", + wpa_s->dpp_netrole_ap ? "ap" : "sta"); +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_INVALID_CONFIG_ATTR_OBJ_CONF_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Config Attr"); + json[29] = 'k'; /* replace "infra" with "knfra" */ + } +#endif /* CONFIG_TESTING_OPTIONS */ + wpa_printf(MSG_DEBUG, "DPP: GAS Config Attributes: %s", json); + + offchannel_send_action_done(wpa_s); + wpas_dpp_listen_stop(wpa_s); + + conf_req = dpp_build_conf_req(auth, json); + if (!conf_req) { + wpa_printf(MSG_DEBUG, + "DPP: No configuration request data available"); + return; + } + + buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req)); + if (!buf) { + wpabuf_free(conf_req); + return; + } + + /* Advertisement Protocol IE */ + wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(buf, 8); /* Length */ + wpabuf_put_u8(buf, 0x7f); + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 5); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, DPP_OUI_TYPE); + wpabuf_put_u8(buf, 0x01); + + /* GAS Query */ + wpabuf_put_le16(buf, wpabuf_len(conf_req)); + wpabuf_put_buf(buf, conf_req); + wpabuf_free(conf_req); + + wpa_printf(MSG_DEBUG, "DPP: GAS request to " MACSTR " (freq %u MHz)", + MAC2STR(auth->peer_mac_addr), auth->curr_freq); + + res = gas_query_req(wpa_s->gas, auth->peer_mac_addr, auth->curr_freq, + 1, buf, wpas_dpp_gas_resp_cb, wpa_s); + if (res < 0) { + wpa_msg(wpa_s, MSG_DEBUG, "GAS: Failed to send Query Request"); + wpabuf_free(buf); + } else { + wpa_printf(MSG_DEBUG, + "DPP: GAS query started with dialog token %u", res); + wpa_s->dpp_gas_dialog_token = res; + } +} + + +static void wpas_dpp_auth_success(struct wpa_supplicant *wpa_s, int initiator) +{ + wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded"); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=%d", initiator); +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at Authentication Confirm"); + if (wpa_s->dpp_auth->configurator) { + /* Prevent GAS response */ + wpa_s->dpp_auth->auth_success = 0; + } + return; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (wpa_s->dpp_auth->configurator) + wpas_dpp_start_gas_server(wpa_s); + else + wpas_dpp_start_gas_client(wpa_s); +} + + +static void wpas_dpp_rx_auth_resp(struct wpa_supplicant *wpa_s, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len, + unsigned int freq) +{ + struct dpp_authentication *auth = wpa_s->dpp_auth; + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "DPP: Authentication Response from " MACSTR + " (freq %u MHz)", MAC2STR(src), freq); + + if (!auth) { + wpa_printf(MSG_DEBUG, + "DPP: No DPP Authentication in progress - drop"); + return; + } + + if (!is_zero_ether_addr(auth->peer_mac_addr) && + os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected " + MACSTR ") - drop", MAC2STR(auth->peer_mac_addr)); + return; + } + + eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); + + if (auth->curr_freq != freq && auth->neg_freq == freq) { + wpa_printf(MSG_DEBUG, + "DPP: Responder accepted request for different negotiation channel"); + auth->curr_freq = freq; + } + + eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL); + msg = dpp_auth_resp_rx(auth, hdr, buf, len); + if (!msg) { + if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) { + wpa_printf(MSG_DEBUG, + "DPP: Start wait for full response"); + offchannel_send_action_done(wpa_s); + wpas_dpp_listen_start(wpa_s, auth->curr_freq); + return; + } + wpa_printf(MSG_DEBUG, "DPP: No confirm generated"); + return; + } + os_memcpy(auth->peer_mac_addr, src, ETH_ALEN); + + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", + MAC2STR(src), auth->curr_freq, DPP_PA_AUTHENTICATION_CONF); + offchannel_send_action(wpa_s, auth->curr_freq, + src, wpa_s->own_addr, broadcast, + wpabuf_head(msg), wpabuf_len(msg), + 500, wpas_dpp_tx_status, 0); + wpabuf_free(msg); + wpa_s->dpp_auth_ok_on_ack = 1; +} + + +static void wpas_dpp_rx_auth_conf(struct wpa_supplicant *wpa_s, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len) +{ + struct dpp_authentication *auth = wpa_s->dpp_auth; + + wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation from " MACSTR, + MAC2STR(src)); + + if (!auth) { + wpa_printf(MSG_DEBUG, + "DPP: No DPP Authentication in progress - drop"); + return; + } + + if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected " + MACSTR ") - drop", MAC2STR(auth->peer_mac_addr)); + return; + } + + if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Authentication failed"); + return; + } + + wpas_dpp_auth_success(wpa_s, 0); +} + + +static void wpas_dpp_rx_peer_disc_resp(struct wpa_supplicant *wpa_s, + const u8 *src, + const u8 *buf, size_t len) +{ + struct wpa_ssid *ssid; + const u8 *connector, *trans_id, *status; + u16 connector_len, trans_id_len, status_len; + struct dpp_introduction intro; + struct rsn_pmksa_cache_entry *entry; + struct os_time now; + struct os_reltime rnow; + os_time_t expiry; + unsigned int seconds; + enum dpp_status_error res; + + wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Response from " MACSTR, + MAC2STR(src)); + if (is_zero_ether_addr(wpa_s->dpp_intro_bssid) || + os_memcmp(src, wpa_s->dpp_intro_bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: Not waiting for response from " + MACSTR " - drop", MAC2STR(src)); + return; + } + offchannel_send_action_done(wpa_s); + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid == wpa_s->dpp_intro_network) + break; + } + if (!ssid || !ssid->dpp_connector || !ssid->dpp_netaccesskey || + !ssid->dpp_csign) { + wpa_printf(MSG_DEBUG, + "DPP: Profile not found for network introduction"); + return; + } + + trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID, + &trans_id_len); + if (!trans_id || trans_id_len != 1) { + wpa_printf(MSG_DEBUG, + "DPP: Peer did not include Transaction ID"); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR + " fail=missing_transaction_id", MAC2STR(src)); + goto fail; + } + if (trans_id[0] != TRANSACTION_ID) { + wpa_printf(MSG_DEBUG, + "DPP: Ignore frame with unexpected Transaction ID %u", + trans_id[0]); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR + " fail=transaction_id_mismatch", MAC2STR(src)); + goto fail; + } + + status = dpp_get_attr(buf, len, DPP_ATTR_STATUS, &status_len); + if (!status || status_len != 1) { + wpa_printf(MSG_DEBUG, "DPP: Peer did not include Status"); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR + " fail=missing_status", MAC2STR(src)); + goto fail; + } + if (status[0] != DPP_STATUS_OK) { + wpa_printf(MSG_DEBUG, + "DPP: Peer rejected network introduction: Status %u", + status[0]); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR + " status=%u", MAC2STR(src), status[0]); + goto fail; + } + + connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len); + if (!connector) { + wpa_printf(MSG_DEBUG, + "DPP: Peer did not include its Connector"); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR + " fail=missing_connector", MAC2STR(src)); + goto fail; + } + + res = dpp_peer_intro(&intro, ssid->dpp_connector, + ssid->dpp_netaccesskey, + ssid->dpp_netaccesskey_len, + ssid->dpp_csign, + ssid->dpp_csign_len, + connector, connector_len, &expiry); + if (res != DPP_STATUS_OK) { + wpa_printf(MSG_INFO, + "DPP: Network Introduction protocol resulted in failure"); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR + " fail=peer_connector_validation_failed", MAC2STR(src)); + goto fail; + } + + entry = os_zalloc(sizeof(*entry)); + if (!entry) + goto fail; + os_memcpy(entry->aa, src, ETH_ALEN); + os_memcpy(entry->pmkid, intro.pmkid, PMKID_LEN); + os_memcpy(entry->pmk, intro.pmk, intro.pmk_len); + entry->pmk_len = intro.pmk_len; + entry->akmp = WPA_KEY_MGMT_DPP; + if (expiry) { + os_get_time(&now); + seconds = expiry - now.sec; + } else { + seconds = 86400 * 7; + } + os_get_reltime(&rnow); + entry->expiration = rnow.sec + seconds; + entry->reauth_time = rnow.sec + seconds; + entry->network_ctx = ssid; + wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry); + + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR + " status=%u", MAC2STR(src), status[0]); + + wpa_printf(MSG_DEBUG, + "DPP: Try connection again after successful network introduction"); + if (wpa_supplicant_fast_associate(wpa_s) != 1) { + wpa_supplicant_cancel_sched_scan(wpa_s); + wpa_supplicant_req_scan(wpa_s, 0, 0); + } +fail: + os_memset(&intro, 0, sizeof(intro)); +} + + +static int wpas_dpp_allow_ir(struct wpa_supplicant *wpa_s, unsigned int freq) +{ + int i, j; + + if (!wpa_s->hw.modes) + return -1; + + for (i = 0; i < wpa_s->hw.num_modes; i++) { + struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i]; + + for (j = 0; j < mode->num_channels; j++) { + struct hostapd_channel_data *chan = &mode->channels[j]; + + if (chan->freq != (int) freq) + continue; + + if (chan->flag & (HOSTAPD_CHAN_DISABLED | + HOSTAPD_CHAN_NO_IR | + HOSTAPD_CHAN_RADAR)) + continue; + + return 1; + } + } + + wpa_printf(MSG_DEBUG, + "DPP: Frequency %u MHz not supported or does not allow PKEX initiation in the current channel list", + freq); + + return 0; +} + + +static int wpas_dpp_pkex_next_channel(struct wpa_supplicant *wpa_s, + struct dpp_pkex *pkex) +{ + if (pkex->freq == 2437) + pkex->freq = 5745; + else if (pkex->freq == 5745) + pkex->freq = 5220; + else if (pkex->freq == 5220) + pkex->freq = 60480; + else + return -1; /* no more channels to try */ + + if (wpas_dpp_allow_ir(wpa_s, pkex->freq) == 1) { + wpa_printf(MSG_DEBUG, "DPP: Try to initiate on %u MHz", + pkex->freq); + return 0; + } + + /* Could not use this channel - try the next one */ + return wpas_dpp_pkex_next_channel(wpa_s, pkex); +} + + +static void wpas_dpp_pkex_retry_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct dpp_pkex *pkex = wpa_s->dpp_pkex; + + if (!pkex || !pkex->exchange_req) + return; + if (pkex->exch_req_tries >= 5) { + if (wpas_dpp_pkex_next_channel(wpa_s, pkex) < 0) { + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_FAIL + "No response from PKEX peer"); + dpp_pkex_free(pkex); + wpa_s->dpp_pkex = NULL; + return; + } + pkex->exch_req_tries = 0; + } + + pkex->exch_req_tries++; + wpa_printf(MSG_DEBUG, "DPP: Retransmit PKEX Exchange Request (try %u)", + pkex->exch_req_tries); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", + MAC2STR(broadcast), pkex->freq, DPP_PA_PKEX_EXCHANGE_REQ); + offchannel_send_action(wpa_s, pkex->freq, broadcast, + wpa_s->own_addr, broadcast, + wpabuf_head(pkex->exchange_req), + wpabuf_len(pkex->exchange_req), + pkex->exch_req_wait_time, + wpas_dpp_tx_pkex_status, 0); +} + + +static void +wpas_dpp_tx_pkex_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) +{ + const char *res_txt; + struct dpp_pkex *pkex = wpa_s->dpp_pkex; + + res_txt = result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" : + (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" : + "FAILED"); + wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR + " result=%s (PKEX)", + freq, MAC2STR(dst), res_txt); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR + " freq=%u result=%s", MAC2STR(dst), freq, res_txt); + + if (!pkex) { + wpa_printf(MSG_DEBUG, + "DPP: Ignore TX status since there is no ongoing PKEX exchange"); + return; + } + + if (pkex->failed) { + wpa_printf(MSG_DEBUG, + "DPP: Terminate PKEX exchange due to an earlier error"); + if (pkex->t > pkex->own_bi->pkex_t) + pkex->own_bi->pkex_t = pkex->t; + dpp_pkex_free(pkex); + wpa_s->dpp_pkex = NULL; + return; + } + + if (pkex->exch_req_wait_time && pkex->exchange_req) { + /* Wait for PKEX Exchange Response frame and retry request if + * no response is seen. */ + eloop_cancel_timeout(wpas_dpp_pkex_retry_timeout, wpa_s, NULL); + eloop_register_timeout(pkex->exch_req_wait_time / 1000, + (pkex->exch_req_wait_time % 1000) * 1000, + wpas_dpp_pkex_retry_timeout, wpa_s, + NULL); + } +} + + +static void +wpas_dpp_rx_pkex_exchange_req(struct wpa_supplicant *wpa_s, const u8 *src, + const u8 *buf, size_t len, unsigned int freq) +{ + struct wpabuf *msg; + unsigned int wait_time; + + wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request from " MACSTR, + MAC2STR(src)); + + /* TODO: Support multiple PKEX codes by iterating over all the enabled + * values here */ + + if (!wpa_s->dpp_pkex_code || !wpa_s->dpp_pkex_bi) { + wpa_printf(MSG_DEBUG, + "DPP: No PKEX code configured - ignore request"); + return; + } + + if (wpa_s->dpp_pkex) { + /* TODO: Support parallel operations */ + wpa_printf(MSG_DEBUG, + "DPP: Already in PKEX session - ignore new request"); + return; + } + + wpa_s->dpp_pkex = dpp_pkex_rx_exchange_req(wpa_s, wpa_s->dpp_pkex_bi, + wpa_s->own_addr, src, + wpa_s->dpp_pkex_identifier, + wpa_s->dpp_pkex_code, + buf, len); + if (!wpa_s->dpp_pkex) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to process the request - ignore it"); + return; + } + + msg = wpa_s->dpp_pkex->exchange_resp; + wait_time = wpa_s->max_remain_on_chan; + if (wait_time > 2000) + wait_time = 2000; + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", + MAC2STR(src), freq, DPP_PA_PKEX_EXCHANGE_RESP); + offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr, + broadcast, + wpabuf_head(msg), wpabuf_len(msg), + wait_time, wpas_dpp_tx_pkex_status, 0); +} + + +static void +wpas_dpp_rx_pkex_exchange_resp(struct wpa_supplicant *wpa_s, const u8 *src, + const u8 *buf, size_t len, unsigned int freq) +{ + struct wpabuf *msg; + unsigned int wait_time; + + wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response from " MACSTR, + MAC2STR(src)); + + /* TODO: Support multiple PKEX codes by iterating over all the enabled + * values here */ + + if (!wpa_s->dpp_pkex || !wpa_s->dpp_pkex->initiator || + wpa_s->dpp_pkex->exchange_done) { + wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session"); + return; + } + + eloop_cancel_timeout(wpas_dpp_pkex_retry_timeout, wpa_s, NULL); + wpa_s->dpp_pkex->exch_req_wait_time = 0; + + msg = dpp_pkex_rx_exchange_resp(wpa_s->dpp_pkex, src, buf, len); + if (!msg) { + wpa_printf(MSG_DEBUG, "DPP: Failed to process the response"); + return; + } + + wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Request to " MACSTR, + MAC2STR(src)); + + wait_time = wpa_s->max_remain_on_chan; + if (wait_time > 2000) + wait_time = 2000; + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", + MAC2STR(src), freq, DPP_PA_PKEX_COMMIT_REVEAL_REQ); + offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr, + broadcast, + wpabuf_head(msg), wpabuf_len(msg), + wait_time, wpas_dpp_tx_pkex_status, 0); + wpabuf_free(msg); +} + + +static struct dpp_bootstrap_info * +wpas_dpp_pkex_finish(struct wpa_supplicant *wpa_s, const u8 *peer, + unsigned int freq) +{ + struct dpp_pkex *pkex = wpa_s->dpp_pkex; + struct dpp_bootstrap_info *bi; + + bi = os_zalloc(sizeof(*bi)); + if (!bi) + return NULL; + bi->id = wpas_dpp_next_id(wpa_s); + bi->type = DPP_BOOTSTRAP_PKEX; + os_memcpy(bi->mac_addr, peer, ETH_ALEN); + bi->num_freq = 1; + bi->freq[0] = freq; + bi->curve = pkex->own_bi->curve; + bi->pubkey = pkex->peer_bootstrap_key; + pkex->peer_bootstrap_key = NULL; + dpp_pkex_free(pkex); + wpa_s->dpp_pkex = NULL; + if (dpp_bootstrap_key_hash(bi) < 0) { + dpp_bootstrap_info_free(bi); + return NULL; + } + dl_list_add(&wpa_s->dpp_bootstrap, &bi->list); + return bi; +} + + +static void +wpas_dpp_rx_pkex_commit_reveal_req(struct wpa_supplicant *wpa_s, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len, + unsigned int freq) +{ + struct wpabuf *msg; + unsigned int wait_time; + struct dpp_pkex *pkex = wpa_s->dpp_pkex; + + wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Request from " MACSTR, + MAC2STR(src)); + + if (!pkex || pkex->initiator || !pkex->exchange_done) { + wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session"); + return; + } + + msg = dpp_pkex_rx_commit_reveal_req(pkex, hdr, buf, len); + if (!msg) { + wpa_printf(MSG_DEBUG, "DPP: Failed to process the request"); + if (pkex->failed) { + wpa_printf(MSG_DEBUG, "DPP: Terminate PKEX exchange"); + if (pkex->t > pkex->own_bi->pkex_t) + pkex->own_bi->pkex_t = pkex->t; + dpp_pkex_free(wpa_s->dpp_pkex); + wpa_s->dpp_pkex = NULL; + } + return; + } + + wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response to " + MACSTR, MAC2STR(src)); + + wait_time = wpa_s->max_remain_on_chan; + if (wait_time > 2000) + wait_time = 2000; + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", + MAC2STR(src), freq, DPP_PA_PKEX_COMMIT_REVEAL_RESP); + offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr, + broadcast, + wpabuf_head(msg), wpabuf_len(msg), + wait_time, wpas_dpp_tx_pkex_status, 0); + wpabuf_free(msg); + + wpas_dpp_pkex_finish(wpa_s, src, freq); +} + + +static void +wpas_dpp_rx_pkex_commit_reveal_resp(struct wpa_supplicant *wpa_s, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len, + unsigned int freq) +{ + int res; + struct dpp_bootstrap_info *bi; + struct dpp_pkex *pkex = wpa_s->dpp_pkex; + char cmd[500]; + + wpa_printf(MSG_DEBUG, "DPP: PKEX Commit-Reveal Response from " MACSTR, + MAC2STR(src)); + + if (!pkex || !pkex->initiator || !pkex->exchange_done) { + wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session"); + return; + } + + res = dpp_pkex_rx_commit_reveal_resp(pkex, hdr, buf, len); + if (res < 0) { + wpa_printf(MSG_DEBUG, "DPP: Failed to process the response"); + return; + } + + bi = wpas_dpp_pkex_finish(wpa_s, src, freq); + if (!bi) + return; + + os_snprintf(cmd, sizeof(cmd), " peer=%u %s", + bi->id, + wpa_s->dpp_pkex_auth_cmd ? wpa_s->dpp_pkex_auth_cmd : ""); + wpa_printf(MSG_DEBUG, + "DPP: Start authentication after PKEX with parameters: %s", + cmd); + if (wpas_dpp_auth_init(wpa_s, cmd) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Authentication initialization failed"); + return; + } +} + + +void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src, + const u8 *buf, size_t len, unsigned int freq) +{ + u8 crypto_suite; + enum dpp_public_action_frame_type type; + const u8 *hdr; + unsigned int pkex_t; + + if (len < DPP_HDR_LEN) + return; + if (WPA_GET_BE24(buf) != OUI_WFA || buf[3] != DPP_OUI_TYPE) + return; + hdr = buf; + buf += 4; + len -= 4; + crypto_suite = *buf++; + type = *buf++; + len -= 2; + + wpa_printf(MSG_DEBUG, + "DPP: Received DPP Public Action frame crypto suite %u type %d from " + MACSTR " freq=%u", + crypto_suite, type, MAC2STR(src), freq); + if (crypto_suite != 1) { + wpa_printf(MSG_DEBUG, "DPP: Unsupported crypto suite %u", + crypto_suite); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_RX "src=" MACSTR + " freq=%u type=%d ignore=unsupported-crypto-suite", + MAC2STR(src), freq, type); + return; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", buf, len); + if (dpp_check_attrs(buf, len) < 0) { + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_RX "src=" MACSTR + " freq=%u type=%d ignore=invalid-attributes", + MAC2STR(src), freq, type); + return; + } + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_RX "src=" MACSTR " freq=%u type=%d", + MAC2STR(src), freq, type); + + switch (type) { + case DPP_PA_AUTHENTICATION_REQ: + wpas_dpp_rx_auth_req(wpa_s, src, hdr, buf, len, freq); + break; + case DPP_PA_AUTHENTICATION_RESP: + wpas_dpp_rx_auth_resp(wpa_s, src, hdr, buf, len, freq); + break; + case DPP_PA_AUTHENTICATION_CONF: + wpas_dpp_rx_auth_conf(wpa_s, src, hdr, buf, len); + break; + case DPP_PA_PEER_DISCOVERY_RESP: + wpas_dpp_rx_peer_disc_resp(wpa_s, src, buf, len); + break; + case DPP_PA_PKEX_EXCHANGE_REQ: + wpas_dpp_rx_pkex_exchange_req(wpa_s, src, buf, len, freq); + break; + case DPP_PA_PKEX_EXCHANGE_RESP: + wpas_dpp_rx_pkex_exchange_resp(wpa_s, src, buf, len, freq); + break; + case DPP_PA_PKEX_COMMIT_REVEAL_REQ: + wpas_dpp_rx_pkex_commit_reveal_req(wpa_s, src, hdr, buf, len, + freq); + break; + case DPP_PA_PKEX_COMMIT_REVEAL_RESP: + wpas_dpp_rx_pkex_commit_reveal_resp(wpa_s, src, hdr, buf, len, + freq); + break; + default: + wpa_printf(MSG_DEBUG, + "DPP: Ignored unsupported frame subtype %d", type); + break; + } + + if (wpa_s->dpp_pkex) + pkex_t = wpa_s->dpp_pkex->t; + else if (wpa_s->dpp_pkex_bi) + pkex_t = wpa_s->dpp_pkex_bi->pkex_t; + else + pkex_t = 0; + if (pkex_t >= PKEX_COUNTER_T_LIMIT) { + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_PKEX_T_LIMIT "id=0"); + wpas_dpp_pkex_remove(wpa_s, "*"); + } +} + + +static struct wpabuf * +wpas_dpp_gas_req_handler(void *ctx, const u8 *sa, const u8 *query, + size_t query_len) +{ + struct wpa_supplicant *wpa_s = ctx; + struct dpp_authentication *auth = wpa_s->dpp_auth; + struct wpabuf *resp; + + wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, + MAC2STR(sa)); + if (!auth || !auth->auth_success || + os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); + return NULL; + } + wpa_hexdump(MSG_DEBUG, + "DPP: Received Configuration Request (GAS Query Request)", + query, query_len); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_REQ_RX "src=" MACSTR, + MAC2STR(sa)); + resp = dpp_conf_req_rx(auth, query, query_len); + if (!resp) + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED); + auth->conf_resp = resp; + return resp; +} + + +static void +wpas_dpp_gas_status_handler(void *ctx, struct wpabuf *resp, int ok) +{ + struct wpa_supplicant *wpa_s = ctx; + struct dpp_authentication *auth = wpa_s->dpp_auth; + + if (!auth) { + wpabuf_free(resp); + return; + } + if (auth->conf_resp != resp) { + wpa_printf(MSG_DEBUG, + "DPP: Ignore GAS status report (ok=%d) for unknown response", + ok); + wpabuf_free(resp); + return; + } + + wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)", + ok); + eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s, NULL); + offchannel_send_action_done(wpa_s); + wpas_dpp_listen_stop(wpa_s); + if (ok) + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_SENT); + else + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_CONF_FAILED); + dpp_auth_deinit(wpa_s->dpp_auth); + wpa_s->dpp_auth = NULL; + wpabuf_free(resp); +} + + +static unsigned int wpas_dpp_next_configurator_id(struct wpa_supplicant *wpa_s) +{ + struct dpp_configurator *conf; + unsigned int max_id = 0; + + dl_list_for_each(conf, &wpa_s->dpp_configurator, + struct dpp_configurator, list) { + if (conf->id > max_id) + max_id = conf->id; + } + return max_id + 1; +} + + +int wpas_dpp_configurator_add(struct wpa_supplicant *wpa_s, const char *cmd) +{ + char *curve = NULL; + char *key = NULL; + u8 *privkey = NULL; + size_t privkey_len = 0; + int ret = -1; + struct dpp_configurator *conf = NULL; + + curve = get_param(cmd, " curve="); + key = get_param(cmd, " key="); + + if (key) { + privkey_len = os_strlen(key) / 2; + privkey = os_malloc(privkey_len); + if (!privkey || + hexstr2bin(key, privkey, privkey_len) < 0) + goto fail; + } + + conf = dpp_keygen_configurator(curve, privkey, privkey_len); + if (!conf) + goto fail; + + conf->id = wpas_dpp_next_configurator_id(wpa_s); + dl_list_add(&wpa_s->dpp_configurator, &conf->list); + ret = conf->id; + conf = NULL; +fail: + os_free(curve); + str_clear_free(key); + bin_clear_free(privkey, privkey_len); + dpp_configurator_free(conf); + return ret; +} + + +static int dpp_configurator_del(struct wpa_supplicant *wpa_s, unsigned int id) +{ + struct dpp_configurator *conf, *tmp; + int found = 0; + + dl_list_for_each_safe(conf, tmp, &wpa_s->dpp_configurator, + struct dpp_configurator, list) { + if (id && conf->id != id) + continue; + found = 1; + dl_list_del(&conf->list); + dpp_configurator_free(conf); + } + + if (id == 0) + return 0; /* flush succeeds regardless of entries found */ + return found ? 0 : -1; +} + + +int wpas_dpp_configurator_remove(struct wpa_supplicant *wpa_s, const char *id) +{ + unsigned int id_val; + + if (os_strcmp(id, "*") == 0) { + id_val = 0; + } else { + id_val = atoi(id); + if (id_val == 0) + return -1; + } + + return dpp_configurator_del(wpa_s, id_val); +} + + +int wpas_dpp_configurator_sign(struct wpa_supplicant *wpa_s, const char *cmd) +{ + struct dpp_authentication *auth; + int ret = -1; + char *curve = NULL; + + auth = os_zalloc(sizeof(*auth)); + if (!auth) + return -1; + + curve = get_param(cmd, " curve="); + wpas_dpp_set_testing_options(wpa_s, auth); + if (wpas_dpp_set_configurator(wpa_s, auth, cmd) == 0 && + dpp_configurator_own_config(auth, curve, 0) == 0) { + wpas_dpp_handle_config_obj(wpa_s, auth); + ret = 0; + } + + dpp_auth_deinit(auth); + os_free(curve); + + return ret; +} + + +int wpas_dpp_configurator_get_key(struct wpa_supplicant *wpa_s, unsigned int id, + char *buf, size_t buflen) +{ + struct dpp_configurator *conf; + + conf = dpp_configurator_get_id(wpa_s, id); + if (!conf) + return -1; + + return dpp_configurator_get_key(conf, buf, buflen); +} + + +static void +wpas_dpp_tx_introduction_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) +{ + const char *res_txt; + + res_txt = result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" : + (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" : + "FAILED"); + wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR + " result=%s (DPP Peer Discovery Request)", + freq, MAC2STR(dst), res_txt); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR + " freq=%u result=%s", MAC2STR(dst), freq, res_txt); + /* TODO: Time out wait for response more quickly in error cases? */ +} + + +int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + struct wpa_bss *bss) +{ + struct os_time now; + struct wpabuf *msg; + unsigned int wait_time; + + if (!(ssid->key_mgmt & WPA_KEY_MGMT_DPP) || !bss) + return 0; /* Not using DPP AKM - continue */ + if (wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid)) + return 0; /* PMKSA exists for DPP AKM - continue */ + + if (!ssid->dpp_connector || !ssid->dpp_netaccesskey || + !ssid->dpp_csign) { + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_MISSING_CONNECTOR + "missing %s", + !ssid->dpp_connector ? "Connector" : + (!ssid->dpp_netaccesskey ? "netAccessKey" : + "C-sign-key")); + return -1; + } + + os_get_time(&now); + + if (ssid->dpp_netaccesskey_expiry && + (os_time_t) ssid->dpp_netaccesskey_expiry < now.sec) { + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_MISSING_CONNECTOR + "netAccessKey expired"); + return -1; + } + + wpa_printf(MSG_DEBUG, + "DPP: Starting network introduction protocol to derive PMKSA for " + MACSTR, MAC2STR(bss->bssid)); + + msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_REQ, + 5 + 4 + os_strlen(ssid->dpp_connector)); + if (!msg) + return -1; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Transaction ID"); + goto skip_trans_id; + } + if (dpp_test == DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Transaction ID"); + wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID); + wpabuf_put_le16(msg, 0); + goto skip_trans_id; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* Transaction ID */ + wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, TRANSACTION_ID); + +#ifdef CONFIG_TESTING_OPTIONS +skip_trans_id: + if (dpp_test == DPP_TEST_NO_CONNECTOR_PEER_DISC_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Connector"); + goto skip_connector; + } + if (dpp_test == DPP_TEST_INVALID_CONNECTOR_PEER_DISC_REQ) { + char *connector; + + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Connector"); + connector = dpp_corrupt_connector_signature( + ssid->dpp_connector); + if (!connector) { + wpabuf_free(msg); + return -1; + } + wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR); + wpabuf_put_le16(msg, os_strlen(connector)); + wpabuf_put_str(msg, connector); + os_free(connector); + goto skip_connector; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* DPP Connector */ + wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR); + wpabuf_put_le16(msg, os_strlen(ssid->dpp_connector)); + wpabuf_put_str(msg, ssid->dpp_connector); + +#ifdef CONFIG_TESTING_OPTIONS +skip_connector: +#endif /* CONFIG_TESTING_OPTIONS */ + + /* TODO: Timeout on AP response */ + wait_time = wpa_s->max_remain_on_chan; + if (wait_time > 2000) + wait_time = 2000; + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d", + MAC2STR(bss->bssid), bss->freq, DPP_PA_PEER_DISCOVERY_REQ); + offchannel_send_action(wpa_s, bss->freq, bss->bssid, wpa_s->own_addr, + broadcast, + wpabuf_head(msg), wpabuf_len(msg), + wait_time, wpas_dpp_tx_introduction_status, 0); + wpabuf_free(msg); + + /* Request this connection attempt to terminate - new one will be + * started when network introduction protocol completes */ + os_memcpy(wpa_s->dpp_intro_bssid, bss->bssid, ETH_ALEN); + wpa_s->dpp_intro_network = ssid; + return 1; +} + + +int wpas_dpp_pkex_add(struct wpa_supplicant *wpa_s, const char *cmd) +{ + struct dpp_bootstrap_info *own_bi; + const char *pos, *end; + unsigned int wait_time; + + pos = os_strstr(cmd, " own="); + if (!pos) + return -1; + pos += 5; + own_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos)); + if (!own_bi) { + wpa_printf(MSG_DEBUG, + "DPP: Identified bootstrap info not found"); + return -1; + } + if (own_bi->type != DPP_BOOTSTRAP_PKEX) { + wpa_printf(MSG_DEBUG, + "DPP: Identified bootstrap info not for PKEX"); + return -1; + } + wpa_s->dpp_pkex_bi = own_bi; + own_bi->pkex_t = 0; /* clear pending errors on new code */ + + os_free(wpa_s->dpp_pkex_identifier); + wpa_s->dpp_pkex_identifier = NULL; + pos = os_strstr(cmd, " identifier="); + if (pos) { + pos += 12; + end = os_strchr(pos, ' '); + if (!end) + return -1; + wpa_s->dpp_pkex_identifier = os_malloc(end - pos + 1); + if (!wpa_s->dpp_pkex_identifier) + return -1; + os_memcpy(wpa_s->dpp_pkex_identifier, pos, end - pos); + wpa_s->dpp_pkex_identifier[end - pos] = '\0'; + } + + pos = os_strstr(cmd, " code="); + if (!pos) + return -1; + os_free(wpa_s->dpp_pkex_code); + wpa_s->dpp_pkex_code = os_strdup(pos + 6); + if (!wpa_s->dpp_pkex_code) + return -1; + + if (os_strstr(cmd, " init=1")) { + struct dpp_pkex *pkex; + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "DPP: Initiating PKEX"); + dpp_pkex_free(wpa_s->dpp_pkex); + wpa_s->dpp_pkex = dpp_pkex_init(wpa_s, own_bi, wpa_s->own_addr, + wpa_s->dpp_pkex_identifier, + wpa_s->dpp_pkex_code); + pkex = wpa_s->dpp_pkex; + if (!pkex) + return -1; + + msg = pkex->exchange_req; + wait_time = wpa_s->max_remain_on_chan; + if (wait_time > 2000) + wait_time = 2000; + pkex->freq = 2437; + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", + MAC2STR(broadcast), pkex->freq, + DPP_PA_PKEX_EXCHANGE_REQ); + offchannel_send_action(wpa_s, pkex->freq, broadcast, + wpa_s->own_addr, broadcast, + wpabuf_head(msg), wpabuf_len(msg), + wait_time, wpas_dpp_tx_pkex_status, 0); + if (wait_time == 0) + wait_time = 2000; + pkex->exch_req_wait_time = wait_time; + pkex->exch_req_tries = 1; + } + + /* TODO: Support multiple PKEX info entries */ + + os_free(wpa_s->dpp_pkex_auth_cmd); + wpa_s->dpp_pkex_auth_cmd = os_strdup(cmd); + + return 1; +} + + +int wpas_dpp_pkex_remove(struct wpa_supplicant *wpa_s, const char *id) +{ + unsigned int id_val; + + if (os_strcmp(id, "*") == 0) { + id_val = 0; + } else { + id_val = atoi(id); + if (id_val == 0) + return -1; + } + + if ((id_val != 0 && id_val != 1) || !wpa_s->dpp_pkex_code) + return -1; + + /* TODO: Support multiple PKEX entries */ + os_free(wpa_s->dpp_pkex_code); + wpa_s->dpp_pkex_code = NULL; + os_free(wpa_s->dpp_pkex_identifier); + wpa_s->dpp_pkex_identifier = NULL; + os_free(wpa_s->dpp_pkex_auth_cmd); + wpa_s->dpp_pkex_auth_cmd = NULL; + wpa_s->dpp_pkex_bi = NULL; + /* TODO: Remove dpp_pkex only if it is for the identified PKEX code */ + dpp_pkex_free(wpa_s->dpp_pkex); + wpa_s->dpp_pkex = NULL; + return 0; +} + + +void wpas_dpp_stop(struct wpa_supplicant *wpa_s) +{ + dpp_auth_deinit(wpa_s->dpp_auth); + wpa_s->dpp_auth = NULL; + dpp_pkex_free(wpa_s->dpp_pkex); + wpa_s->dpp_pkex = NULL; + if (wpa_s->dpp_gas_client && wpa_s->dpp_gas_dialog_token >= 0) + gas_query_stop(wpa_s->gas, wpa_s->dpp_gas_dialog_token); +} + + +int wpas_dpp_init(struct wpa_supplicant *wpa_s) +{ + u8 adv_proto_id[7]; + + adv_proto_id[0] = WLAN_EID_VENDOR_SPECIFIC; + adv_proto_id[1] = 5; + WPA_PUT_BE24(&adv_proto_id[2], OUI_WFA); + adv_proto_id[5] = DPP_OUI_TYPE; + adv_proto_id[6] = 0x01; + + if (gas_server_register(wpa_s->gas_server, adv_proto_id, + sizeof(adv_proto_id), wpas_dpp_gas_req_handler, + wpas_dpp_gas_status_handler, wpa_s) < 0) + return -1; + dl_list_init(&wpa_s->dpp_bootstrap); + dl_list_init(&wpa_s->dpp_configurator); + wpa_s->dpp_init_done = 1; + return 0; +} + + +void wpas_dpp_deinit(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_TESTING_OPTIONS + os_free(wpa_s->dpp_config_obj_override); + wpa_s->dpp_config_obj_override = NULL; + os_free(wpa_s->dpp_discovery_override); + wpa_s->dpp_discovery_override = NULL; + os_free(wpa_s->dpp_groups_override); + wpa_s->dpp_groups_override = NULL; + wpa_s->dpp_ignore_netaccesskey_mismatch = 0; +#endif /* CONFIG_TESTING_OPTIONS */ + if (!wpa_s->dpp_init_done) + return; + eloop_cancel_timeout(wpas_dpp_pkex_retry_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_dpp_init_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_dpp_auth_resp_retry_timeout, wpa_s, NULL); + offchannel_send_action_done(wpa_s); + wpas_dpp_listen_stop(wpa_s); + dpp_bootstrap_del(wpa_s, 0); + dpp_configurator_del(wpa_s, 0); + wpas_dpp_stop(wpa_s); + wpas_dpp_pkex_remove(wpa_s, "*"); + os_memset(wpa_s->dpp_intro_bssid, 0, ETH_ALEN); + os_free(wpa_s->dpp_configurator_params); + wpa_s->dpp_configurator_params = NULL; +} diff --git a/contrib/wpa/wpa_supplicant/dpp_supplicant.h b/contrib/wpa/wpa_supplicant/dpp_supplicant.h new file mode 100644 index 000000000000..5a4f06e2e97e --- /dev/null +++ b/contrib/wpa/wpa_supplicant/dpp_supplicant.h @@ -0,0 +1,39 @@ +/* + * wpa_supplicant - DPP + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DPP_SUPPLICANT_H +#define DPP_SUPPLICANT_H + +int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd); +int wpas_dpp_bootstrap_gen(struct wpa_supplicant *wpa_s, const char *cmd); +int wpas_dpp_bootstrap_remove(struct wpa_supplicant *wpa_s, const char *id); +const char * wpas_dpp_bootstrap_get_uri(struct wpa_supplicant *wpa_s, + unsigned int id); +int wpas_dpp_bootstrap_info(struct wpa_supplicant *wpa_s, int id, + char *reply, int reply_size); +int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd); +int wpas_dpp_listen(struct wpa_supplicant *wpa_s, const char *cmd); +void wpas_dpp_listen_stop(struct wpa_supplicant *wpa_s); +void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq); +void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src, + const u8 *buf, size_t len, unsigned int freq); +int wpas_dpp_configurator_add(struct wpa_supplicant *wpa_s, const char *cmd); +int wpas_dpp_configurator_remove(struct wpa_supplicant *wpa_s, const char *id); +int wpas_dpp_configurator_sign(struct wpa_supplicant *wpa_s, const char *cmd); +int wpas_dpp_configurator_get_key(struct wpa_supplicant *wpa_s, unsigned int id, + char *buf, size_t buflen); +int wpas_dpp_pkex_add(struct wpa_supplicant *wpa_s, const char *cmd); +int wpas_dpp_pkex_remove(struct wpa_supplicant *wpa_s, const char *id); +void wpas_dpp_stop(struct wpa_supplicant *wpa_s); +int wpas_dpp_init(struct wpa_supplicant *wpa_s); +void wpas_dpp_deinit(struct wpa_supplicant *wpa_s); +int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, + struct wpa_bss *bss); + +#endif /* DPP_SUPPLICANT_H */ diff --git a/contrib/wpa/wpa_supplicant/driver_i.h b/contrib/wpa/wpa_supplicant/driver_i.h index 220b7ba3ddca..078de23f794f 100644 --- a/contrib/wpa/wpa_supplicant/driver_i.h +++ b/contrib/wpa/wpa_supplicant/driver_i.h @@ -189,20 +189,19 @@ static inline int wpa_drv_deauthenticate(struct wpa_supplicant *wpa_s, } static inline int wpa_drv_add_pmkid(struct wpa_supplicant *wpa_s, - const u8 *bssid, const u8 *pmkid) + struct wpa_pmkid_params *params) { if (wpa_s->driver->add_pmkid) { - return wpa_s->driver->add_pmkid(wpa_s->drv_priv, bssid, pmkid); + return wpa_s->driver->add_pmkid(wpa_s->drv_priv, params); } return -1; } static inline int wpa_drv_remove_pmkid(struct wpa_supplicant *wpa_s, - const u8 *bssid, const u8 *pmkid) + struct wpa_pmkid_params *params) { if (wpa_s->driver->remove_pmkid) { - return wpa_s->driver->remove_pmkid(wpa_s->drv_priv, bssid, - pmkid); + return wpa_s->driver->remove_pmkid(wpa_s->drv_priv, params); } return -1; } @@ -276,11 +275,12 @@ static inline int wpa_drv_mlme_setprotection(struct wpa_supplicant *wpa_s, static inline struct hostapd_hw_modes * wpa_drv_get_hw_feature_data(struct wpa_supplicant *wpa_s, u16 *num_modes, - u16 *flags) + u16 *flags, u8 *dfs_domain) { if (wpa_s->driver->get_hw_feature_data) return wpa_s->driver->get_hw_feature_data(wpa_s->drv_priv, - num_modes, flags); + num_modes, flags, + dfs_domain); return NULL; } @@ -689,6 +689,14 @@ static inline int wpa_drv_roaming(struct wpa_supplicant *wpa_s, int allowed, return wpa_s->driver->roaming(wpa_s->drv_priv, allowed, bssid); } +static inline int wpa_drv_disable_fils(struct wpa_supplicant *wpa_s, + int disable) +{ + if (!wpa_s->driver->disable_fils) + return -1; + return wpa_s->driver->disable_fils(wpa_s->drv_priv, disable); +} + static inline int wpa_drv_set_mac_addr(struct wpa_supplicant *wpa_s, const u8 *addr) { @@ -715,6 +723,14 @@ static inline int wpa_drv_macsec_deinit(struct wpa_supplicant *wpa_s) return wpa_s->driver->macsec_deinit(wpa_s->drv_priv); } +static inline int wpa_drv_macsec_get_capability(struct wpa_supplicant *wpa_s, + enum macsec_cap *cap) +{ + if (!wpa_s->driver->macsec_get_capability) + return -1; + return wpa_s->driver->macsec_get_capability(wpa_s->drv_priv, cap); +} + static inline int wpa_drv_enable_protect_frames(struct wpa_supplicant *wpa_s, Boolean enabled) { @@ -723,6 +739,14 @@ static inline int wpa_drv_enable_protect_frames(struct wpa_supplicant *wpa_s, return wpa_s->driver->enable_protect_frames(wpa_s->drv_priv, enabled); } +static inline int wpa_drv_enable_encrypt(struct wpa_supplicant *wpa_s, + Boolean enabled) +{ + if (!wpa_s->driver->enable_encrypt) + return -1; + return wpa_s->driver->enable_encrypt(wpa_s->drv_priv, enabled); +} + static inline int wpa_drv_set_replay_protect(struct wpa_supplicant *wpa_s, Boolean enabled, u32 window) { @@ -749,145 +773,127 @@ static inline int wpa_drv_enable_controlled_port(struct wpa_supplicant *wpa_s, } static inline int wpa_drv_get_receive_lowest_pn(struct wpa_supplicant *wpa_s, - u32 channel, u8 an, - u32 *lowest_pn) + struct receive_sa *sa) { if (!wpa_s->driver->get_receive_lowest_pn) return -1; - return wpa_s->driver->get_receive_lowest_pn(wpa_s->drv_priv, channel, - an, lowest_pn); + return wpa_s->driver->get_receive_lowest_pn(wpa_s->drv_priv, sa); } static inline int wpa_drv_get_transmit_next_pn(struct wpa_supplicant *wpa_s, - u32 channel, u8 an, - u32 *next_pn) + struct transmit_sa *sa) { 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); + return wpa_s->driver->get_transmit_next_pn(wpa_s->drv_priv, sa); } static inline int wpa_drv_set_transmit_next_pn(struct wpa_supplicant *wpa_s, - u32 channel, u8 an, - u32 next_pn) + struct transmit_sa *sa) { 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); + return wpa_s->driver->set_transmit_next_pn(wpa_s->drv_priv, sa); } static inline int -wpa_drv_create_receive_sc(struct wpa_supplicant *wpa_s, u32 channel, - const u8 *sci_addr, u16 sci_port, +wpa_drv_create_receive_sc(struct wpa_supplicant *wpa_s, struct receive_sc *sc, 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); + return wpa_s->driver->create_receive_sc(wpa_s->drv_priv, sc, + conf_offset, validation); } static inline int wpa_drv_delete_receive_sc(struct wpa_supplicant *wpa_s, - u32 channel) + struct receive_sc *sc) { if (!wpa_s->driver->delete_receive_sc) return -1; - return wpa_s->driver->delete_receive_sc(wpa_s->drv_priv, channel); + return wpa_s->driver->delete_receive_sc(wpa_s->drv_priv, sc); } static inline int wpa_drv_create_receive_sa(struct wpa_supplicant *wpa_s, - u32 channel, u8 an, - u32 lowest_pn, const u8 *sak) + struct receive_sa *sa) { 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); + return wpa_s->driver->create_receive_sa(wpa_s->drv_priv, sa); } -static inline int wpa_drv_enable_receive_sa(struct wpa_supplicant *wpa_s, - u32 channel, u8 an) +static inline int wpa_drv_delete_receive_sa(struct wpa_supplicant *wpa_s, + struct receive_sa *sa) { - if (!wpa_s->driver->enable_receive_sa) + if (!wpa_s->driver->delete_receive_sa) return -1; - return wpa_s->driver->enable_receive_sa(wpa_s->drv_priv, channel, an); + return wpa_s->driver->delete_receive_sa(wpa_s->drv_priv, sa); } -static inline int wpa_drv_disable_receive_sa(struct wpa_supplicant *wpa_s, - u32 channel, u8 an) +static inline int wpa_drv_enable_receive_sa(struct wpa_supplicant *wpa_s, + struct receive_sa *sa) { - if (!wpa_s->driver->disable_receive_sa) + if (!wpa_s->driver->enable_receive_sa) return -1; - return wpa_s->driver->disable_receive_sa(wpa_s->drv_priv, channel, an); + return wpa_s->driver->enable_receive_sa(wpa_s->drv_priv, sa); } -static inline int -wpa_drv_get_available_transmit_sc(struct wpa_supplicant *wpa_s, u32 *channel) +static inline int wpa_drv_disable_receive_sa(struct wpa_supplicant *wpa_s, + struct receive_sa *sa) { - if (!wpa_s->driver->get_available_transmit_sc) + if (!wpa_s->driver->disable_receive_sa) return -1; - return wpa_s->driver->get_available_transmit_sc(wpa_s->drv_priv, - channel); + return wpa_s->driver->disable_receive_sa(wpa_s->drv_priv, sa); } static inline int -wpa_drv_create_transmit_sc(struct wpa_supplicant *wpa_s, u32 channel, - const u8 *sci_addr, u16 sci_port, +wpa_drv_create_transmit_sc(struct wpa_supplicant *wpa_s, struct transmit_sc *sc, 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, + return wpa_s->driver->create_transmit_sc(wpa_s->drv_priv, sc, conf_offset); } static inline int wpa_drv_delete_transmit_sc(struct wpa_supplicant *wpa_s, - u32 channel) + struct transmit_sc *sc) { if (!wpa_s->driver->delete_transmit_sc) return -1; - return wpa_s->driver->delete_transmit_sc(wpa_s->drv_priv, channel); + return wpa_s->driver->delete_transmit_sc(wpa_s->drv_priv, sc); } 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) + struct transmit_sa *sa) { 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); + return wpa_s->driver->create_transmit_sa(wpa_s->drv_priv, sa); +} + +static inline int wpa_drv_delete_transmit_sa(struct wpa_supplicant *wpa_s, + struct transmit_sa *sa) +{ + if (!wpa_s->driver->delete_transmit_sa) + return -1; + return wpa_s->driver->delete_transmit_sa(wpa_s->drv_priv, sa); } static inline int wpa_drv_enable_transmit_sa(struct wpa_supplicant *wpa_s, - u32 channel, u8 an) + struct transmit_sa *sa) { if (!wpa_s->driver->enable_transmit_sa) return -1; - return wpa_s->driver->enable_transmit_sa(wpa_s->drv_priv, channel, an); + return wpa_s->driver->enable_transmit_sa(wpa_s->drv_priv, sa); } static inline int wpa_drv_disable_transmit_sa(struct wpa_supplicant *wpa_s, - u32 channel, u8 an) + struct transmit_sa *sa) { if (!wpa_s->driver->disable_transmit_sa) return -1; - return wpa_s->driver->disable_transmit_sa(wpa_s->drv_priv, channel, an); + return wpa_s->driver->disable_transmit_sa(wpa_s->drv_priv, sa); } #endif /* CONFIG_MACSEC */ @@ -904,6 +910,11 @@ static inline int wpa_drv_get_pref_freq_list(struct wpa_supplicant *wpa_s, unsigned int *num, unsigned int *freq_list) { +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->get_pref_freq_list_override) + return wpas_ctrl_iface_get_pref_freq_list_override( + wpa_s, if_type, num, freq_list); +#endif /* CONFIG_TESTING_OPTIONS */ if (!wpa_s->driver->get_pref_freq_list) return -1; return wpa_s->driver->get_pref_freq_list(wpa_s->drv_priv, if_type, @@ -918,11 +929,12 @@ static inline int wpa_drv_set_prob_oper_freq(struct wpa_supplicant *wpa_s, return wpa_s->driver->set_prob_oper_freq(wpa_s->drv_priv, freq); } -static inline int wpa_drv_abort_scan(struct wpa_supplicant *wpa_s) +static inline int wpa_drv_abort_scan(struct wpa_supplicant *wpa_s, + u64 scan_cookie) { if (!wpa_s->driver->abort_scan) return -1; - return wpa_s->driver->abort_scan(wpa_s->drv_priv); + return wpa_s->driver->abort_scan(wpa_s->drv_priv, scan_cookie); } static inline int wpa_drv_configure_frame_filters(struct wpa_supplicant *wpa_s, @@ -976,4 +988,62 @@ static inline int wpa_drv_set_default_scan_ies(struct wpa_supplicant *wpa_s, return wpa_s->driver->set_default_scan_ies(wpa_s->drv_priv, ies, len); } +static inline int wpa_drv_set_tdls_mode(struct wpa_supplicant *wpa_s, + int tdls_external_control) +{ + if (!wpa_s->driver->set_tdls_mode) + return -1; + return wpa_s->driver->set_tdls_mode(wpa_s->drv_priv, + tdls_external_control); +} + +static inline struct wpa_bss_candidate_info * +wpa_drv_get_bss_trans_status(struct wpa_supplicant *wpa_s, + struct wpa_bss_trans_info *params) +{ + if (!wpa_s->driver->get_bss_transition_status) + return NULL; + return wpa_s->driver->get_bss_transition_status(wpa_s->drv_priv, + params); +} + +static inline int wpa_drv_ignore_assoc_disallow(struct wpa_supplicant *wpa_s, + int val) +{ + if (!wpa_s->driver->ignore_assoc_disallow) + return -1; + return wpa_s->driver->ignore_assoc_disallow(wpa_s->drv_priv, val); +} + +static inline int wpa_drv_set_bssid_blacklist(struct wpa_supplicant *wpa_s, + unsigned int num_bssid, + const u8 *bssids) +{ + if (!wpa_s->driver->set_bssid_blacklist) + return -1; + return wpa_s->driver->set_bssid_blacklist(wpa_s->drv_priv, num_bssid, + bssids); +} + +static inline int wpa_drv_update_connect_params( + struct wpa_supplicant *wpa_s, + struct wpa_driver_associate_params *params, + enum wpa_drv_update_connect_params_mask mask) +{ + if (!wpa_s->driver->update_connect_params) + return -1; + return wpa_s->driver->update_connect_params(wpa_s->drv_priv, params, + mask); +} + +static inline int +wpa_drv_send_external_auth_status(struct wpa_supplicant *wpa_s, + struct external_auth *params) +{ + if (!wpa_s->driver->send_external_auth_status) + return -1; + return wpa_s->driver->send_external_auth_status(wpa_s->drv_priv, + params); +} + #endif /* DRIVER_I_H */ diff --git a/contrib/wpa/wpa_supplicant/events.c b/contrib/wpa/wpa_supplicant/events.c index abe3b476773d..37d429d33022 100644 --- a/contrib/wpa/wpa_supplicant/events.c +++ b/contrib/wpa/wpa_supplicant/events.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - Driver event processing - * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -28,6 +28,7 @@ #include "notify.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "common/gas_server.h" #include "crypto/random.h" #include "blacklist.h" #include "wpas_glue.h" @@ -46,6 +47,10 @@ #include "mesh.h" #include "mesh_mpm.h" #include "wmm_ac.h" +#include "dpp_supplicant.h" + + +#define MAX_OWE_TRANSITION_BSS_SELECT_COUNT 5 #ifndef CONFIG_NO_SCAN_PROCESSING @@ -54,8 +59,7 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, #endif /* CONFIG_NO_SCAN_PROCESSING */ -static int wpas_temp_disabled(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid) +int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { struct os_reltime now; @@ -302,7 +306,9 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); eapol_sm_notify_portValid(wpa_s->eapol, FALSE); - if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) + if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || + wpa_s->key_mgmt == WPA_KEY_MGMT_OWE || + wpa_s->key_mgmt == WPA_KEY_MGMT_DPP) eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); wpa_s->ap_ies_from_associnfo = 0; wpa_s->current_ssid = NULL; @@ -311,6 +317,13 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) wpas_rrm_reset(wpa_s); wpa_s->wnmsleep_used = 0; + wnm_clear_coloc_intf_reporting(wpa_s); + +#ifdef CONFIG_TESTING_OPTIONS + wpa_s->last_tk_alg = WPA_ALG_NONE; + os_memset(wpa_s->last_tk, 0, sizeof(wpa_s->last_tk)); +#endif /* CONFIG_TESTING_OPTIONS */ + wpa_s->ieee80211ac = 0; } @@ -327,7 +340,7 @@ static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s) for (i = 0; i < ie.num_pmkid; i++) { pmksa_set = pmksa_cache_set_current(wpa_s->wpa, ie.pmkid + i * PMKID_LEN, - NULL, NULL, 0); + NULL, NULL, 0, NULL, 0); if (pmksa_set == 0) { eapol_sm_notify_pmkid_attempt(wpa_s->eapol); break; @@ -479,6 +492,11 @@ static int wpa_supplicant_match_privacy(struct wpa_bss *bss, return 1; #endif /* CONFIG_WPS */ +#ifdef CONFIG_OWE + if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) && !ssid->owe_only) + return 1; +#endif /* CONFIG_OWE */ + if (has_wep_key(ssid)) privacy = 1; @@ -503,7 +521,7 @@ static int wpa_supplicant_match_privacy(struct wpa_bss *bss, static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, - struct wpa_bss *bss) + struct wpa_bss *bss, int debug_print) { struct wpa_ie_data ie; int proto_match = 0; @@ -522,44 +540,59 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)); rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); - while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) { + while ((ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)) && rsn_ie) { proto_match++; if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - parse " - "failed"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip RSN IE - parse failed"); break; } if (wep_ok && (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104))) { - wpa_dbg(wpa_s, MSG_DEBUG, " selected based on TSN " - "in RSN IE"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " selected based on TSN in RSN IE"); return 1; } - if (!(ie.proto & ssid->proto)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - proto " - "mismatch"); + if (!(ie.proto & ssid->proto) && + !(ssid->proto & WPA_PROTO_OSEN)) { + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip RSN IE - proto mismatch"); break; } if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - PTK " - "cipher mismatch"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip RSN IE - PTK cipher mismatch"); break; } if (!(ie.group_cipher & ssid->group_cipher)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - GTK " - "cipher mismatch"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip RSN IE - GTK cipher mismatch"); + break; + } + + if (ssid->group_mgmt_cipher && + !(ie.mgmt_group_cipher & ssid->group_mgmt_cipher)) { + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip RSN IE - group mgmt cipher mismatch"); break; } if (!(ie.key_mgmt & ssid->key_mgmt)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - key mgmt " - "mismatch"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip RSN IE - key mgmt mismatch"); break; } @@ -567,16 +600,18 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, if (!(ie.capabilities & WPA_CAPABILITY_MFPC) && wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip RSN IE - no mgmt " - "frame protection"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip RSN IE - no mgmt frame protection"); break; } #endif /* CONFIG_IEEE80211W */ if ((ie.capabilities & WPA_CAPABILITY_MFPR) && wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION) { - wpa_dbg(wpa_s, MSG_DEBUG, - " skip RSN IE - no mgmt frame protection enabled but AP requires it"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip RSN IE - no mgmt frame protection enabled but AP requires it"); break; } #ifdef CONFIG_MBO @@ -584,20 +619,25 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_AP_CAPA_IND) && wpas_get_ssid_pmf(wpa_s, ssid) != NO_MGMT_FRAME_PROTECTION) { - wpa_dbg(wpa_s, MSG_DEBUG, - " skip RSN IE - no mgmt frame protection enabled on MBO AP"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip RSN IE - no mgmt frame protection enabled on MBO AP"); break; } #endif /* CONFIG_MBO */ - wpa_dbg(wpa_s, MSG_DEBUG, " selected based on RSN IE"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " selected based on RSN IE"); return 1; } #ifdef CONFIG_IEEE80211W - if (wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) { - wpa_dbg(wpa_s, MSG_DEBUG, - " skip - MFP Required but network not MFP Capable"); + if (wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED && + (!(ssid->key_mgmt & WPA_KEY_MGMT_OWE) || ssid->owe_only)) { + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - MFP Required but network not MFP Capable"); return 0; } #endif /* CONFIG_IEEE80211W */ @@ -607,72 +647,110 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s, proto_match++; if (wpa_parse_wpa_ie(wpa_ie, 2 + wpa_ie[1], &ie)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - parse " - "failed"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip WPA IE - parse failed"); break; } if (wep_ok && (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104))) { - wpa_dbg(wpa_s, MSG_DEBUG, " selected based on TSN " - "in WPA IE"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " selected based on TSN in WPA IE"); return 1; } if (!(ie.proto & ssid->proto)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - proto " - "mismatch"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip WPA IE - proto mismatch"); break; } if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - PTK " - "cipher mismatch"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip WPA IE - PTK cipher mismatch"); break; } if (!(ie.group_cipher & ssid->group_cipher)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - GTK " - "cipher mismatch"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip WPA IE - GTK cipher mismatch"); break; } if (!(ie.key_mgmt & ssid->key_mgmt)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip WPA IE - key mgmt " - "mismatch"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip WPA IE - key mgmt mismatch"); break; } - wpa_dbg(wpa_s, MSG_DEBUG, " selected based on WPA IE"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " selected based on WPA IE"); return 1; } if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && !wpa_ie && !rsn_ie) { - wpa_dbg(wpa_s, MSG_DEBUG, " allow for non-WPA IEEE 802.1X"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " allow for non-WPA IEEE 802.1X"); return 1; } +#ifdef CONFIG_OWE + if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) && !ssid->owe_only && + !wpa_ie && !rsn_ie) { + if (wpa_s->owe_transition_select && + wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE) && + ssid->owe_transition_bss_select_count + 1 <= + MAX_OWE_TRANSITION_BSS_SELECT_COUNT) { + ssid->owe_transition_bss_select_count++; + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip OWE transition BSS (selection count %d does not exceed %d)", + ssid->owe_transition_bss_select_count, + MAX_OWE_TRANSITION_BSS_SELECT_COUNT); + wpa_s->owe_transition_search = 1; + return 0; + } + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " allow in OWE transition mode"); + return 1; + } +#endif /* CONFIG_OWE */ + if ((ssid->proto & (WPA_PROTO_WPA | WPA_PROTO_RSN)) && wpa_key_mgmt_wpa(ssid->key_mgmt) && proto_match == 0) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - no WPA/RSN proto match"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - no WPA/RSN proto match"); 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"); + if (debug_print) + 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"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, " allow in non-WPA/WPA2"); return 1; } - wpa_dbg(wpa_s, MSG_DEBUG, " reject due to mismatch with " - "WPA/WPA2"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " reject due to mismatch with WPA/WPA2"); return 0; } @@ -692,7 +770,8 @@ static int freq_allowed(int *freqs, int freq) } -static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) +static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + int debug_print) { const struct hostapd_hw_modes *mode = NULL, *modes; const u8 scan_ie[2] = { WLAN_EID_SUPP_RATES, WLAN_EID_EXT_SUPP_RATES }; @@ -749,9 +828,9 @@ static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) if (flagged && ((rate_ie[j] & 0x7f) == BSS_MEMBERSHIP_SELECTOR_HT_PHY)) { if (!ht_supported(mode)) { - wpa_dbg(wpa_s, MSG_DEBUG, - " hardware does not support " - "HT PHY"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " hardware does not support HT PHY"); return 0; } continue; @@ -761,9 +840,9 @@ static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) 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"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " hardware does not support VHT PHY"); return 0; } continue; @@ -783,10 +862,11 @@ 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 (freq=%d mode==%d num_rates=%d)", - r / 10, r % 10, - bss->freq, mode->mode, mode->num_rates); + if (debug_print) + 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; } } @@ -839,39 +919,124 @@ static int addr_in_list(const u8 *addr, const u8 *list, size_t num) } +static void owe_trans_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, + const u8 **ret_ssid, size_t *ret_ssid_len) +{ +#ifdef CONFIG_OWE + const u8 *owe, *pos, *end, *bssid; + u8 ssid_len; + struct wpa_bss *open_bss; + + owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE); + if (!owe || !wpa_bss_get_ie(bss, WLAN_EID_RSN)) + return; + + pos = owe + 6; + end = owe + 2 + owe[1]; + + if (end - pos < ETH_ALEN + 1) + return; + bssid = pos; + pos += ETH_ALEN; + ssid_len = *pos++; + if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN) + return; + + /* Match the profile SSID against the OWE transition mode SSID on the + * open network. */ + wpa_dbg(wpa_s, MSG_DEBUG, "OWE: transition mode BSSID: " MACSTR + " SSID: %s", MAC2STR(bssid), wpa_ssid_txt(pos, ssid_len)); + *ret_ssid = pos; + *ret_ssid_len = ssid_len; + + if (bss->ssid_len > 0) + return; + + open_bss = wpa_bss_get_bssid_latest(wpa_s, bssid); + if (!open_bss) + return; + if (ssid_len != open_bss->ssid_len || + os_memcmp(pos, open_bss->ssid, ssid_len) != 0) { + wpa_dbg(wpa_s, MSG_DEBUG, + "OWE: transition mode SSID mismatch: %s", + wpa_ssid_txt(open_bss->ssid, open_bss->ssid_len)); + return; + } + + owe = wpa_bss_get_vendor_ie(open_bss, OWE_IE_VENDOR_TYPE); + if (!owe || wpa_bss_get_ie(open_bss, WLAN_EID_RSN)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "OWE: transition mode open BSS unexpected info"); + return; + } + + pos = owe + 6; + end = owe + 2 + owe[1]; + + if (end - pos < ETH_ALEN + 1) + return; + if (os_memcmp(pos, bss->bssid, ETH_ALEN) != 0) { + wpa_dbg(wpa_s, MSG_DEBUG, + "OWE: transition mode BSSID mismatch: " MACSTR, + MAC2STR(pos)); + return; + } + pos += ETH_ALEN; + ssid_len = *pos++; + if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN) + return; + wpa_dbg(wpa_s, MSG_DEBUG, "OWE: learned transition mode OWE SSID: %s", + wpa_ssid_txt(pos, ssid_len)); + os_memcpy(bss->ssid, pos, ssid_len); + bss->ssid_len = ssid_len; +#endif /* CONFIG_OWE */ +} + + struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, int i, struct wpa_bss *bss, struct wpa_ssid *group, - int only_first_ssid) + int only_first_ssid, int debug_print) { u8 wpa_ie_len, rsn_ie_len; int wpa; struct wpa_blacklist *e; const u8 *ie; struct wpa_ssid *ssid; - int osen; + int osen, rsn_osen = 0; #ifdef CONFIG_MBO const u8 *assoc_disallow; #endif /* CONFIG_MBO */ + const u8 *match_ssid; + size_t match_ssid_len; + struct wpa_ie_data data; ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE); wpa_ie_len = ie ? ie[1] : 0; ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); rsn_ie_len = ie ? ie[1] : 0; + if (ie && wpa_parse_wpa_ie_rsn(ie, 2 + ie[1], &data) == 0 && + (data.key_mgmt & WPA_KEY_MGMT_OSEN)) + rsn_osen = 1; 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 freq=%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, bss->freq, - 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" : ""); + if (debug_print) { + wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR + " ssid='%s' wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%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, + bss->freq, + 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) { @@ -888,24 +1053,34 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, limit = 0; } if (e->count > limit) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted " - "(count=%d limit=%d)", e->count, limit); + if (debug_print) { + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - blacklisted (count=%d limit=%d)", + e->count, limit); + } return NULL; } } - if (bss->ssid_len == 0) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID not known"); + match_ssid = bss->ssid; + match_ssid_len = bss->ssid_len; + owe_trans_ssid(wpa_s, bss, &match_ssid, &match_ssid_len); + + if (match_ssid_len == 0) { + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID not known"); return NULL; } if (disallowed_bssid(wpa_s, bss->bssid)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID disallowed"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID disallowed"); return NULL; } - if (disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID disallowed"); + if (disallowed_ssid(wpa_s, match_ssid, match_ssid_len)) { + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID disallowed"); return NULL; } @@ -916,21 +1091,25 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, int res; if (wpas_network_disabled(wpa_s, ssid)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled"); continue; } res = wpas_temp_disabled(wpa_s, ssid); if (res > 0) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled " - "temporarily for %d second(s)", res); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - disabled temporarily for %d second(s)", + res); continue; } #ifdef CONFIG_WPS if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted " - "(WPS)"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - blacklisted (WPS)"); continue; } @@ -954,15 +1133,19 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, check_ssid = 0; if (check_ssid && - (bss->ssid_len != ssid->ssid_len || - os_memcmp(bss->ssid, ssid->ssid, bss->ssid_len) != 0)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID mismatch"); + (match_ssid_len != ssid->ssid_len || + os_memcmp(match_ssid, ssid->ssid, match_ssid_len) != 0)) { + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - SSID mismatch"); continue; } if (ssid->bssid_set && os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID mismatch"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - BSSID mismatch"); continue; } @@ -970,8 +1153,9 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, 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"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - BSSID blacklisted"); continue; } @@ -979,79 +1163,108 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, 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"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - BSSID not in whitelist"); continue; } - if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss)) + if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss, + debug_print)) continue; if (!osen && !wpa && !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) && !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) && + !(ssid->key_mgmt & WPA_KEY_MGMT_OWE) && !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - non-WPA network " - "not allowed"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - non-WPA network not allowed"); 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"); + if (debug_print) + 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"); + if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && !osen && + !rsn_osen) { + if (debug_print) + 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"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - privacy mismatch"); continue; } if (ssid->mode != IEEE80211_MODE_MESH && !bss_is_ess(bss) && !bss_is_pbss(bss)) { - wpa_dbg(wpa_s, MSG_DEBUG, - " skip - not ESS, PBSS, or MBSS"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - not ESS, PBSS, or MBSS"); continue; } if (ssid->pbss != 2 && ssid->pbss != bss_is_pbss(bss)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - PBSS mismatch (ssid %d bss %d)", - ssid->pbss, bss_is_pbss(bss)); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - PBSS mismatch (ssid %d bss %d)", + ssid->pbss, bss_is_pbss(bss)); continue; } if (!freq_allowed(ssid->freq_list, bss->freq)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - frequency not " - "allowed"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - frequency not allowed"); continue; } #ifdef CONFIG_MESH if (ssid->mode == IEEE80211_MODE_MESH && ssid->frequency > 0 && ssid->frequency != bss->freq) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - frequency not allowed (mesh)"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - frequency not allowed (mesh)"); continue; } #endif /* CONFIG_MESH */ - if (!rate_match(wpa_s, bss)) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - rate sets do " - "not match"); + if (!rate_match(wpa_s, bss, debug_print)) { + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - rate sets do not match"); continue; } +#ifndef CONFIG_IBSS_RSN + if (ssid->mode == WPAS_MODE_IBSS && + !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE | + WPA_KEY_MGMT_WPA_NONE))) { + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - IBSS RSN not supported in the build"); + continue; + } +#endif /* !CONFIG_IBSS_RSN */ + #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"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - no P2P IE seen"); continue; } @@ -1061,20 +1274,26 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, ie = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE); if (ie == NULL) { - wpa_dbg(wpa_s, MSG_DEBUG, " skip - no P2P element"); + if (debug_print) + 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"); + if (debug_print) + 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"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - no matching GO P2P Device Address in P2P element"); wpabuf_free(p2p_ie); continue; } @@ -1094,8 +1313,9 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, os_reltime_sub(&wpa_s->scan_min_time, &bss->last_update, &diff); - wpa_dbg(wpa_s, MSG_DEBUG, - " skip - scan result not recent enough (%u.%06u seconds too old)", + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - scan result not recent enough (%u.%06u seconds too old)", (unsigned int) diff.sec, (unsigned int) diff.usec); continue; @@ -1108,15 +1328,17 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, assoc_disallow = wpas_mbo_get_bss_attr( bss, MBO_ATTR_ID_ASSOC_DISALLOW); if (assoc_disallow && assoc_disallow[1] >= 1) { - wpa_dbg(wpa_s, MSG_DEBUG, - " skip - MBO association disallowed (reason %u)", + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - MBO association disallowed (reason %u)", assoc_disallow[2]); continue; } if (wpa_is_bss_tmp_disallowed(wpa_s, bss->bssid)) { - wpa_dbg(wpa_s, MSG_DEBUG, - " skip - MBO retry delay has not passed yet"); + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - MBO retry delay has not passed yet"); continue; } #ifdef CONFIG_TESTING_OPTIONS @@ -1124,6 +1346,19 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, #endif /* CONFIG_TESTING_OPTIONS */ #endif /* CONFIG_MBO */ +#ifdef CONFIG_DPP + if ((ssid->key_mgmt & WPA_KEY_MGMT_DPP) && + !wpa_sm_pmksa_exists(wpa_s->wpa, bss->bssid, ssid) && + (!ssid->dpp_connector || + !ssid->dpp_netaccesskey || + !ssid->dpp_csign)) { + if (debug_print) + wpa_dbg(wpa_s, MSG_DEBUG, + " skip - no PMKSA entry for DPP"); + continue; + } +#endif /* CONFIG_DPP */ + /* Matching configuration found */ return ssid; } @@ -1141,6 +1376,25 @@ wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, { unsigned int i; + if (wpa_s->current_ssid) { + struct wpa_ssid *ssid; + + wpa_dbg(wpa_s, MSG_DEBUG, + "Scan results matching the currently selected network"); + for (i = 0; i < wpa_s->last_scan_res_used; i++) { + struct wpa_bss *bss = wpa_s->last_scan_res[i]; + + ssid = wpa_scan_res_match(wpa_s, i, bss, group, + only_first_ssid, 0); + if (ssid != wpa_s->current_ssid) + continue; + wpa_dbg(wpa_s, MSG_DEBUG, "%u: " MACSTR + " freq=%d level=%d snr=%d est_throughput=%u", + i, MAC2STR(bss->bssid), bss->freq, bss->level, + bss->snr, bss->est_throughput); + } + } + if (only_first_ssid) wpa_dbg(wpa_s, MSG_DEBUG, "Try to find BSS matching pre-selected network id=%d", group->id); @@ -1150,8 +1404,11 @@ wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s, for (i = 0; i < wpa_s->last_scan_res_used; i++) { struct wpa_bss *bss = wpa_s->last_scan_res[i]; + + wpa_s->owe_transition_select = 1; *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group, - only_first_ssid); + only_first_ssid, 1); + wpa_s->owe_transition_select = 0; if (!*selected_ssid) continue; wpa_dbg(wpa_s, MSG_DEBUG, " selected BSS " MACSTR @@ -1332,6 +1589,17 @@ wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s) { if (wpas_network_disabled(wpa_s, ssid)) continue; +#ifndef CONFIG_IBSS_RSN + if (ssid->mode == WPAS_MODE_IBSS && + !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE | + WPA_KEY_MGMT_WPA_NONE))) { + wpa_msg(wpa_s, MSG_INFO, + "IBSS RSN not supported in the build - cannot use the profile for SSID '%s'", + wpa_ssid_txt(ssid->ssid, + ssid->ssid_len)); + continue; + } +#endif /* !CONFIG_IBSS_RSN */ if (ssid->mode == IEEE80211_MODE_IBSS || ssid->mode == IEEE80211_MODE_AP || ssid->mode == IEEE80211_MODE_MESH) @@ -1375,8 +1643,9 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, { struct wpa_bss *current_bss = NULL; #ifndef CONFIG_NO_ROAMING - int min_diff; + int min_diff, diff; int to_5ghz; + int cur_est, sel_est; #endif /* CONFIG_NO_ROAMING */ if (wpa_s->reassociate) @@ -1410,12 +1679,13 @@ 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 snr=%d est_throughput=%u", - MAC2STR(current_bss->bssid), current_bss->level, + " freq=%d level=%d snr=%d est_throughput=%u", + MAC2STR(current_bss->bssid), + current_bss->freq, 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, + " freq=%d level=%d snr=%d est_throughput=%u", + MAC2STR(selected->bssid), selected->freq, selected->level, selected->snr, selected->est_throughput); if (wpa_s->current_ssid->bssid_set && @@ -1441,6 +1711,14 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, return 0; } + if (current_bss->est_throughput > selected->est_throughput + 5000) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Skip roam - Current BSS has better estimated throughput"); + return 0; + } + + cur_est = current_bss->est_throughput; + sel_est = selected->est_throughput; min_diff = 2; if (current_bss->level < 0) { if (current_bss->level < -85) @@ -1453,20 +1731,42 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s, min_diff = 4; else min_diff = 5; + if (cur_est > sel_est * 1.5) + min_diff += 10; + else if (cur_est > sel_est * 1.2) + min_diff += 5; + else if (cur_est > sel_est * 1.1) + min_diff += 2; + else if (cur_est > sel_est) + min_diff++; } if (to_5ghz) { + int reduce = 2; + /* Make it easier to move to 5 GHz band */ - if (min_diff > 2) - min_diff -= 2; + if (sel_est > cur_est * 1.5) + reduce = 5; + else if (sel_est > cur_est * 1.2) + reduce = 4; + else if (sel_est > cur_est * 1.1) + reduce = 3; + + if (min_diff > reduce) + min_diff -= reduce; else min_diff = 0; } - if (abs(current_bss->level - selected->level) < min_diff) { - wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - too small difference " - "in signal level"); + diff = abs(current_bss->level - selected->level); + if (diff < min_diff) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Skip roam - too small difference in signal level (%d < %d)", + diff, min_diff); return 0; } + wpa_dbg(wpa_s, MSG_DEBUG, + "Allow reassociation due to difference in signal level (%d >= %d)", + diff, min_diff); return 1; #else /* CONFIG_NO_ROAMING */ return 0; @@ -1474,11 +1774,18 @@ 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. */ +/* + * Return a negative value if no scan results could be fetched or if scan + * results should not be shared with other virtual interfaces. + * Return 0 if scan results were fetched and may be shared with other + * interfaces. + * Return 1 if scan results may be shared with other virtual interfaces but may + * not trigger any operations. + * Return 2 if the interface was removed and cannot be used. + */ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, union wpa_event_data *data, - int own_request) + int own_request, int update_only) { struct wpa_scan_results *scan_res = NULL; int ret = 0; @@ -1528,6 +1835,11 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_NO_RANDOM_POOL */ + if (update_only) { + ret = 1; + goto scan_work_done; + } + if (own_request && wpa_s->scan_res_handler && !(data && data->scan_info.external_scan)) { void (*scan_res_handler)(struct wpa_supplicant *wpa_s, @@ -1536,7 +1848,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, scan_res_handler = wpa_s->scan_res_handler; wpa_s->scan_res_handler = NULL; scan_res_handler(wpa_s, scan_res); - ret = -2; + ret = 1; goto scan_work_done; } @@ -1577,6 +1889,10 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, if (sme_proc_obss_scan(wpa_s) > 0) goto scan_work_done; + if (own_request && + wpas_beacon_rep_scan_process(wpa_s, scan_res, &data->scan_info) > 0) + goto scan_work_done; + if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) goto scan_work_done; @@ -1639,6 +1955,7 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_mgmt) return 0; /* no normal connection on p2p_mgmt interface */ + wpa_s->owe_transition_search = 0; selected = wpa_supplicant_pick_network(wpa_s, &ssid); #ifdef CONFIG_MESH @@ -1672,8 +1989,9 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *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. + * Do not allow other virtual radios to trigger operations based + * on these scan results since we do not want them to start + * other associations at the same time. */ return 1; } else { @@ -1739,6 +2057,17 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s, return 0; } #endif /* CONFIG_WPS */ +#ifdef CONFIG_OWE + if (wpa_s->owe_transition_search) { + wpa_dbg(wpa_s, MSG_DEBUG, + "OWE: Use shorter wait during transition mode search"); + timeout_sec = 0; + timeout_usec = 500000; + wpa_supplicant_req_new_scan(wpa_s, timeout_sec, + timeout_usec); + return 0; + } +#endif /* CONFIG_OWE */ if (wpa_supplicant_req_sched_scan(wpa_s)) wpa_supplicant_req_new_scan(wpa_s, timeout_sec, timeout_usec); @@ -1757,7 +2086,7 @@ static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, struct wpa_supplicant *ifs; int res; - res = _wpa_supplicant_event_scan_results(wpa_s, data, 1); + res = _wpa_supplicant_event_scan_results(wpa_s, data, 1, 0); if (res == 2) { /* * Interface may have been removed, so must not dereference @@ -1765,7 +2094,8 @@ static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, */ return 1; } - if (res != 0) { + + if (res < 0) { /* * If no scan results could be fetched, then no need to * notify those interfaces that did not actually request @@ -1785,7 +2115,10 @@ static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, if (ifs != wpa_s) { wpa_printf(MSG_DEBUG, "%s: Updating scan results from " "sibling", ifs->ifname); - _wpa_supplicant_event_scan_results(ifs, data, 0); + res = _wpa_supplicant_event_scan_results(ifs, data, 0, + res > 0); + if (res < 0) + return 0; } } @@ -1802,6 +2135,8 @@ int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s) #else /* CONFIG_NO_SCAN_PROCESSING */ struct os_reltime now; + wpa_s->ignore_post_flush_scan_res = 0; + if (wpa_s->last_scan_res_used == 0) return -1; @@ -1981,9 +2316,9 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, { int l, len, found = 0, wpa_found, rsn_found; const u8 *p; -#ifdef CONFIG_IEEE80211R +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_OWE) u8 bssid[ETH_ALEN]; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R || CONFIG_OWE */ wpa_dbg(wpa_s, MSG_DEBUG, "Association info event"); if (data->assoc_info.req_ies) @@ -2004,6 +2339,10 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies, data->assoc_info.resp_ies_len); #endif /* CONFIG_INTERWORKING */ + if (wpa_s->hw_capab == CAPAB_VHT && + get_ie(data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len, WLAN_EID_VHT_CAP)) + wpa_s->ieee80211ac = 1; } if (data->assoc_info.beacon_ies) wpa_hexdump(MSG_DEBUG, "beacon_ies", @@ -2041,6 +2380,36 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s, if (!found && data->assoc_info.req_ies) wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); +#ifdef CONFIG_FILS +#ifdef CONFIG_SME + if ((wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS || + wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS_SK_PFS) && + (!data->assoc_info.resp_frame || + fils_process_assoc_resp(wpa_s->wpa, + data->assoc_info.resp_frame, + data->assoc_info.resp_frame_len) < 0)) { + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED); + return -1; + } +#endif /* CONFIG_SME */ + + /* Additional processing for FILS when SME is in driver */ + if (wpa_s->auth_alg == WPA_AUTH_ALG_FILS && + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) + wpa_sm_set_reset_fils_completed(wpa_s->wpa, 1); +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE + if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE && + (wpa_drv_get_bssid(wpa_s, bssid) < 0 || + owe_process_assoc_resp(wpa_s->wpa, bssid, + data->assoc_info.resp_ies, + data->assoc_info.resp_ies_len) < 0)) { + wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED); + return -1; + } +#endif /* CONFIG_OWE */ + #ifdef CONFIG_IEEE80211R #ifdef CONFIG_SME if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) { @@ -2262,6 +2631,13 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, ft_completed = wpa_ft_is_completed(wpa_s->wpa); if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0) return; + /* + * FILS authentication can share the same mechanism to mark the + * connection fully authenticated, so set ft_completed also based on + * FILS result. + */ + if (!ft_completed) + ft_completed = wpa_fils_is_completed(wpa_s->wpa); if (wpa_drv_get_bssid(wpa_s, bssid) < 0) { wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID"); @@ -2331,7 +2707,9 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE); eapol_sm_notify_portValid(wpa_s->eapol, FALSE); } - if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || ft_completed || + if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || + wpa_s->key_mgmt == WPA_KEY_MGMT_DPP || + wpa_s->key_mgmt == WPA_KEY_MGMT_OWE || ft_completed || already_authorized) eapol_sm_notify_eap_success(wpa_s->eapol, FALSE); /* 802.1X::portControl = Auto */ @@ -2395,7 +2773,7 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, 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 && + if (age.sec == 0 && age.usec < 200000 && os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) == 0) { wpa_dbg(wpa_s, MSG_DEBUG, "Process pending EAPOL " @@ -2446,6 +2824,16 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s, if (wpa_s->reassoc_same_bss) wmm_ac_restore_tspecs(wpa_s); } + +#ifdef CONFIG_FILS + if (wpa_key_mgmt_fils(wpa_s->key_mgmt)) { + struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, bssid); + const u8 *fils_cache_id = wpa_bss_get_fils_cache_id(bss); + + if (fils_cache_id) + wpa_sm_set_fils_cache_id(wpa_s->wpa, fils_cache_id); + } +#endif /* CONFIG_FILS */ } @@ -2837,18 +3225,6 @@ wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s, } -#ifdef CONFIG_PEERKEY -static void -wpa_supplicant_event_stkstart(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) -{ - if (data == NULL) - return; - wpa_sm_stkstart(wpa_s->wpa, data->stkstart.peer); -} -#endif /* CONFIG_PEERKEY */ - - #ifdef CONFIG_TDLS static void wpa_supplicant_event_tdls(struct wpa_supplicant *wpa_s, union wpa_event_data *data) @@ -3211,6 +3587,7 @@ static void wpa_supplicant_update_channel_list( struct wpa_supplicant *wpa_s, struct channel_list_changed *info) { struct wpa_supplicant *ifs; + u8 dfs_domain; /* * To allow backwards compatibility with higher level layers that @@ -3235,7 +3612,7 @@ static void wpa_supplicant_update_channel_list( ifs->ifname); free_hw_features(ifs); ifs->hw.modes = wpa_drv_get_hw_feature_data( - ifs, &ifs->hw.num_modes, &ifs->hw.flags); + ifs, &ifs->hw.num_modes, &ifs->hw.flags, &dfs_domain); /* Restart PNO/sched_scan with updated channel list */ if (ifs->pno) { @@ -3310,6 +3687,15 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, return; #endif /* CONFIG_GAS */ +#ifdef CONFIG_GAS_SERVER + if ((mgmt->u.action.category == WLAN_ACTION_PUBLIC || + mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) && + gas_server_rx(wpa_s->gas_server, mgmt->da, mgmt->sa, mgmt->bssid, + mgmt->u.action.category, + payload, plen, freq) == 0) + return; +#endif /* CONFIG_GAS_SERVER */ + #ifdef CONFIG_TDLS if (category == WLAN_ACTION_PUBLIC && plen >= 4 && payload[0] == WLAN_TDLS_DISCOVERY_RESPONSE) { @@ -3338,6 +3724,7 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, if (category == WLAN_ACTION_RADIO_MEASUREMENT && payload[0] == WLAN_RRM_RADIO_MEASUREMENT_REQUEST) { wpas_rrm_handle_radio_measurement_request(wpa_s, mgmt->sa, + mgmt->da, payload + 1, plen - 1); return; @@ -3364,6 +3751,18 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_FST */ +#ifdef CONFIG_DPP + if (category == WLAN_ACTION_PUBLIC && plen >= 5 && + payload[0] == WLAN_PA_VENDOR_SPECIFIC && + WPA_GET_BE24(&payload[1]) == OUI_WFA && + payload[4] == DPP_OUI_TYPE) { + payload++; + plen--; + wpas_dpp_rx_action(wpa_s, mgmt->sa, payload, plen, freq); + return; + } +#endif /* CONFIG_DPP */ + wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid, category, payload, plen, freq); if (wpa_s->ifmsh) @@ -3404,23 +3803,226 @@ static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s, } -static void wpa_supplicant_event_assoc_auth(struct wpa_supplicant *wpa_s, - union wpa_event_data *data) +static void wpa_supplicant_event_port_authorized(struct wpa_supplicant *wpa_s) { - 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); } +} + + +static unsigned int wpas_event_cac_ms(const struct wpa_supplicant *wpa_s, + int freq) +{ + size_t i; + int j; + + for (i = 0; i < wpa_s->hw.num_modes; i++) { + const struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i]; + + for (j = 0; j < mode->num_channels; j++) { + const struct hostapd_channel_data *chan; + + chan = &mode->channels[j]; + if (chan->freq == freq) + return chan->dfs_cac_ms; + } + } + + return 0; +} + + +static void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s, + struct dfs_event *radar) +{ +#if defined(NEED_AP_MLME) && defined(CONFIG_AP) + if (wpa_s->ap_iface) { + wpas_ap_event_dfs_cac_started(wpa_s, radar); + } else +#endif /* NEED_AP_MLME && CONFIG_AP */ + { + unsigned int cac_time = wpas_event_cac_ms(wpa_s, radar->freq); + + cac_time /= 1000; /* convert from ms to sec */ + if (!cac_time) + cac_time = 10 * 60; /* max timeout: 10 minutes */ + + /* Restart auth timeout: CAC time added to initial timeout */ + wpas_auth_timeout_restart(wpa_s, cac_time); + } +} + + +static void wpas_event_dfs_cac_finished(struct wpa_supplicant *wpa_s, + struct dfs_event *radar) +{ +#if defined(NEED_AP_MLME) && defined(CONFIG_AP) + if (wpa_s->ap_iface) { + wpas_ap_event_dfs_cac_finished(wpa_s, radar); + } else +#endif /* NEED_AP_MLME && CONFIG_AP */ + { + /* Restart auth timeout with original value after CAC is + * finished */ + wpas_auth_timeout_restart(wpa_s, 0); + } +} + + +static void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s, + struct dfs_event *radar) +{ +#if defined(NEED_AP_MLME) && defined(CONFIG_AP) + if (wpa_s->ap_iface) { + wpas_ap_event_dfs_cac_aborted(wpa_s, radar); + } else +#endif /* NEED_AP_MLME && CONFIG_AP */ + { + /* Restart auth timeout with original value after CAC is + * aborted */ + wpas_auth_timeout_restart(wpa_s, 0); + } +} + + +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); + + wpa_supplicant_event_port_authorized(wpa_s); + 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); +#ifdef CONFIG_FILS + if (wpa_s->auth_alg == WPA_AUTH_ALG_FILS) { + struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, wpa_s->bssid); + const u8 *fils_cache_id = wpa_bss_get_fils_cache_id(bss); + + /* Update ERP next sequence number */ + eapol_sm_update_erp_next_seq_num( + wpa_s->eapol, data->assoc_info.fils_erp_next_seq_num); + + if (data->assoc_info.fils_pmk && data->assoc_info.fils_pmkid) { + /* Add the new PMK and PMKID to the PMKSA cache */ + wpa_sm_pmksa_cache_add(wpa_s->wpa, + data->assoc_info.fils_pmk, + data->assoc_info.fils_pmk_len, + data->assoc_info.fils_pmkid, + wpa_s->bssid, fils_cache_id); + } else if (data->assoc_info.fils_pmkid) { + /* Update the current PMKSA used for this connection */ + pmksa_cache_set_current(wpa_s->wpa, + data->assoc_info.fils_pmkid, + NULL, NULL, 0, NULL, 0); + } + } +#endif /* CONFIG_FILS */ +} + + +static void wpas_event_assoc_reject(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + const u8 *bssid = data->assoc_reject.bssid; + + if (!bssid || is_zero_ether_addr(bssid)) + bssid = wpa_s->pending_bssid; + + if (data->assoc_reject.bssid) + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT + "bssid=" MACSTR " status_code=%u%s%s%s", + MAC2STR(data->assoc_reject.bssid), + data->assoc_reject.status_code, + data->assoc_reject.timed_out ? " timeout" : "", + data->assoc_reject.timeout_reason ? "=" : "", + data->assoc_reject.timeout_reason ? + data->assoc_reject.timeout_reason : ""); + else + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT + "status_code=%u%s%s%s", + data->assoc_reject.status_code, + data->assoc_reject.timed_out ? " timeout" : "", + data->assoc_reject.timeout_reason ? "=" : "", + data->assoc_reject.timeout_reason ? + data->assoc_reject.timeout_reason : ""); + wpa_s->assoc_status_code = data->assoc_reject.status_code; + wpas_notify_assoc_status_code(wpa_s); + +#ifdef CONFIG_OWE + if (data->assoc_reject.status_code == + WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED && + wpa_s->key_mgmt == WPA_KEY_MGMT_OWE && + wpa_s->current_ssid && + wpa_s->current_ssid->owe_group == 0 && + wpa_s->last_owe_group != 21) { + struct wpa_ssid *ssid = wpa_s->current_ssid; + struct wpa_bss *bss = wpa_s->current_bss; + + if (!bss) { + bss = wpa_supplicant_get_new_bss(wpa_s, bssid); + if (!bss) { + wpas_connection_failed(wpa_s, bssid); + wpa_supplicant_mark_disassoc(wpa_s); + return; + } + } + wpa_printf(MSG_DEBUG, "OWE: Try next supported DH group"); + wpas_connect_work_done(wpa_s); + wpa_supplicant_mark_disassoc(wpa_s); + wpa_supplicant_connect(wpa_s, bss, ssid); + return; + } +#endif /* CONFIG_OWE */ + + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) { + sme_event_assoc_reject(wpa_s, data); + return; + } + + /* Driver-based SME cases */ + +#ifdef CONFIG_SAE + if (wpa_s->current_ssid && + wpa_key_mgmt_sae(wpa_s->current_ssid->key_mgmt) && + !data->assoc_reject.timed_out) { + wpa_dbg(wpa_s, MSG_DEBUG, "SAE: Drop PMKSA cache entry"); + wpa_sm_aborted_cached(wpa_s->wpa); + wpa_sm_pmksa_cache_flush(wpa_s->wpa, wpa_s->current_ssid); + } +#endif /* CONFIG_SAE */ + +#ifdef CONFIG_DPP + if (wpa_s->current_ssid && + wpa_s->current_ssid->key_mgmt == WPA_KEY_MGMT_DPP && + !data->assoc_reject.timed_out) { + wpa_dbg(wpa_s, MSG_DEBUG, "DPP: Drop PMKSA cache entry"); + wpa_sm_aborted_cached(wpa_s->wpa); + wpa_sm_pmksa_cache_flush(wpa_s->wpa, wpa_s->current_ssid); + } +#endif /* CONFIG_DPP */ + +#ifdef CONFIG_FILS + /* Update ERP next sequence number */ + if (wpa_s->auth_alg == WPA_AUTH_ALG_FILS) { + eapol_sm_update_erp_next_seq_num( + wpa_s->eapol, + data->assoc_reject.fils_erp_next_seq_num); + fils_connection_failure(wpa_s); + } +#endif /* CONFIG_FILS */ + + wpas_connection_failed(wpa_s, bssid); + wpa_supplicant_mark_disassoc(wpa_s); } @@ -3429,6 +4031,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, { struct wpa_supplicant *wpa_s = ctx; int resched; +#ifndef CONFIG_NO_STDOUT_DEBUG + int level = MSG_DEBUG; +#endif /* CONFIG_NO_STDOUT_DEBUG */ if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED && event != EVENT_INTERFACE_ENABLED && @@ -3442,9 +4047,6 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, } #ifndef CONFIG_NO_STDOUT_DEBUG -{ - int level = MSG_DEBUG; - if (event == EVENT_RX_MGMT && data->rx_mgmt.frame_len >= 24) { const struct ieee80211_hdr *hdr; u16 fc; @@ -3457,7 +4059,6 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, wpa_dbg(wpa_s, level, "Event %s (%d) received", event_to_string(event), event); -} #endif /* CONFIG_NO_STDOUT_DEBUG */ switch (event) { @@ -3477,9 +4078,18 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, "EVENT_ASSOC - ignore_auth_resp active!"); break; } + if (wpa_s->testing_resend_assoc) { + wpa_printf(MSG_INFO, + "EVENT_DEAUTH - testing_resend_assoc"); + break; + } #endif /* CONFIG_TESTING_OPTIONS */ wpa_supplicant_event_assoc(wpa_s, data); - if (data && data->assoc_info.authorized) + wpa_s->assoc_status_code = WLAN_STATUS_SUCCESS; + if (data && + (data->assoc_info.authorized || + (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && + wpa_fils_is_completed(wpa_s->wpa)))) wpa_supplicant_event_assoc_auth(wpa_s, data); if (data) { wpa_msg(wpa_s, MSG_INFO, @@ -3498,6 +4108,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, "EVENT_DEAUTH - ignore_auth_resp active!"); break; } + if (wpa_s->testing_resend_assoc) { + wpa_printf(MSG_INFO, + "EVENT_DEAUTH - testing_resend_assoc"); + break; + } #endif /* CONFIG_TESTING_OPTIONS */ wpas_event_deauth(wpa_s, data ? &data->deauth_info : NULL); @@ -3570,11 +4185,6 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, case EVENT_PMKID_CANDIDATE: wpa_supplicant_event_pmkid_candidate(wpa_s, data); break; -#ifdef CONFIG_PEERKEY - case EVENT_STKSTART: - wpa_supplicant_event_stkstart(wpa_s, data); - break; -#endif /* CONFIG_PEERKEY */ #ifdef CONFIG_TDLS case EVENT_TDLS: wpa_supplicant_event_tdls(wpa_s, data); @@ -3596,28 +4206,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; #endif /* CONFIG_IBSS_RSN */ case EVENT_ASSOC_REJECT: - if (data->assoc_reject.bssid) - wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT - "bssid=" MACSTR " status_code=%u%s", - MAC2STR(data->assoc_reject.bssid), - data->assoc_reject.status_code, - data->assoc_reject.timed_out ? " timeout" : ""); - else - wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT - "status_code=%u%s", - data->assoc_reject.status_code, - data->assoc_reject.timed_out ? " timeout" : ""); - wpa_s->assoc_status_code = data->assoc_reject.status_code; - wpas_notify_assoc_status_code(wpa_s); - if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) - sme_event_assoc_reject(wpa_s, data); - else { - const u8 *bssid = data->assoc_reject.bssid; - if (bssid == NULL || is_zero_ether_addr(bssid)) - bssid = wpa_s->pending_bssid; - wpas_connection_failed(wpa_s, bssid); - wpa_supplicant_mark_disassoc(wpa_s); - } + wpas_event_assoc_reject(wpa_s, data); break; case EVENT_AUTH_TIMED_OUT: /* It is possible to get this event from earlier connection */ @@ -3719,6 +4308,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.addr, data->rx_from_unknown.wds); break; +#endif /* CONFIG_AP */ case EVENT_CH_SWITCH: if (!data || !wpa_s->current_ssid) break; @@ -3735,6 +4325,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, wpa_s->assoc_freq = data->ch_switch.freq; wpa_s->current_ssid->frequency = data->ch_switch.freq; +#ifdef CONFIG_AP if (wpa_s->current_ssid->mode == WPAS_MODE_AP || wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO || wpa_s->current_ssid->mode == @@ -3746,14 +4337,25 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->ch_switch.cf1, data->ch_switch.cf2); } +#endif /* CONFIG_AP */ wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_CS); + wnm_clear_coloc_intf_reporting(wpa_s); break; +#ifdef CONFIG_AP #ifdef NEED_AP_MLME case EVENT_DFS_RADAR_DETECTED: if (data) - wpas_event_dfs_radar_detected(wpa_s, &data->dfs_event); + wpas_ap_event_dfs_radar_detected(wpa_s, + &data->dfs_event); break; + case EVENT_DFS_NOP_FINISHED: + if (data) + wpas_ap_event_dfs_cac_nop_finished(wpa_s, + &data->dfs_event); + break; +#endif /* NEED_AP_MLME */ +#endif /* CONFIG_AP */ case EVENT_DFS_CAC_STARTED: if (data) wpas_event_dfs_cac_started(wpa_s, &data->dfs_event); @@ -3766,13 +4368,6 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, 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; @@ -3844,6 +4439,16 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; } +#ifdef CONFIG_SAE + if (stype == WLAN_FC_STYPE_AUTH && + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE)) { + sme_external_auth_mgmt_rx( + wpa_s, data->rx_mgmt.frame, + data->rx_mgmt.frame_len); + break; + } +#endif /* CONFIG_SAE */ wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received " "management frame in non-AP mode"); break; @@ -3908,6 +4513,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, #endif /* CONFIG_OFFCHANNEL */ wpas_p2p_cancel_remain_on_channel_cb( wpa_s, data->remain_on_channel.freq); +#ifdef CONFIG_DPP + wpas_dpp_cancel_remain_on_channel_cb( + wpa_s, data->remain_on_channel.freq); +#endif /* CONFIG_DPP */ break; case EVENT_EAPOL_RX: wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src, @@ -3929,6 +4538,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->signal_change.current_noise, data->signal_change.current_txrate); break; + case EVENT_INTERFACE_MAC_CHANGED: + wpa_supplicant_update_mac_addr(wpa_s); + break; case EVENT_INTERFACE_ENABLED: wpa_dbg(wpa_s, MSG_DEBUG, "Interface was enabled"); if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { @@ -4129,12 +4741,14 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, #endif /* CONFIG_AP */ break; case EVENT_ACS_CHANNEL_SELECTED: +#ifdef CONFIG_AP #ifdef CONFIG_ACS if (!wpa_s->ap_iface) break; hostapd_acs_channel_selected(wpa_s->ap_iface->bss[0], &data->acs_selected_channels); #endif /* CONFIG_ACS */ +#endif /* CONFIG_AP */ break; case EVENT_P2P_LO_STOP: #ifdef CONFIG_P2P @@ -4144,6 +4758,36 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, data->p2p_lo_stop.reason_code); #endif /* CONFIG_P2P */ break; + case EVENT_BEACON_LOSS: + if (!wpa_s->current_bss || !wpa_s->current_ssid) + break; + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_BEACON_LOSS); + bgscan_notify_beacon_loss(wpa_s); + break; + case EVENT_EXTERNAL_AUTH: +#ifdef CONFIG_SAE + if (!wpa_s->current_ssid) { + wpa_printf(MSG_DEBUG, "SAE: current_ssid is NULL"); + break; + } + sme_external_auth_trigger(wpa_s, data); +#endif /* CONFIG_SAE */ + break; + case EVENT_PORT_AUTHORIZED: + wpa_supplicant_event_port_authorized(wpa_s); + break; + case EVENT_STATION_OPMODE_CHANGED: +#ifdef CONFIG_AP + if (!wpa_s->ap_iface || !data) + break; + + hostapd_event_sta_opmode_changed(wpa_s->ap_iface->bss[0], + data->sta_opmode.addr, + data->sta_opmode.smps_mode, + data->sta_opmode.chan_width, + data->sta_opmode.rx_nss); +#endif /* CONFIG_AP */ + break; default: wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event); break; diff --git a/contrib/wpa/wpa_supplicant/examples/dpp-qrcode.py b/contrib/wpa/wpa_supplicant/examples/dpp-qrcode.py new file mode 100755 index 000000000000..e2a00c910812 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/examples/dpp-qrcode.py @@ -0,0 +1,130 @@ +#!/usr/bin/python +# +# Example Android logcat to wpa_supplicant wrapper for QR Code scans +# Copyright (c) 2017, Qualcomm Atheros, Inc. +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. + +import os +import sys +import argparse +import logging +import qrcode + +scriptsdir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__)) +sys.path.append(os.path.join(scriptsdir, '..', '..', 'wpaspy')) + +import wpaspy + +wpas_ctrl = '/var/run/wpa_supplicant' + +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: + try: + wpas = wpaspy.Ctrl(ctrl) + return wpas + except Exception, e: + pass + return None + +def dpp_logcat(): + for line in iter(sys.stdin.readline, ''): + if "ResultHandler: Launching intent: Intent" not in line: + continue + if "act=android.intent.action.VIEW" not in line: + continue + uri = None + for val in line.split(' '): + if val.startswith('dat='): + uri = val.split('=', 1)[1] + break + if not uri: + continue + if not uri.startswith('DPP:'): + continue + print "Found DPP bootstrap info URI:" + print uri + wpas = wpas_connect() + if not wpas: + print "Could not connect to wpa_supplicant" + print + continue + res = wpas.request("DPP_QR_CODE " + uri); + try: + id = int(res) + except ValueError: + print "QR Code URI rejected" + continue + print "QR Code URI accepted - ID=%d" % id + print wpas.request("DPP_BOOTSTRAP_INFO %d" % id) + del wpas + +def dpp_display(curve): + wpas = wpas_connect() + if not wpas: + print "Could not connect to wpa_supplicant" + return + res = wpas.request("STATUS") + addr = None + for line in res.splitlines(): + if line.startswith("address="): + addr = line.split('=')[1] + break + cmd = "DPP_BOOTSTRAP_GEN type=qrcode" + cmd += " chan=81/1" + if addr: + cmd += " mac=" + addr.replace(':','') + if curve: + cmd += " curve=" + curve + res = wpas.request(cmd) + try: + id = int(res) + except ValueError: + print "Failed to generate bootstrap info URI" + return + print "Bootstrap information - ID=%d" % id + print wpas.request("DPP_BOOTSTRAP_INFO %d" % id) + uri = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % id) + print uri + print "ID=%d" % id + qr = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_M, + border=3) + qr.add_data(uri, optimize=5) + qr.print_ascii(tty=True) + print "ID=%d" % id + del wpas + +def main(): + parser = argparse.ArgumentParser(description='Android logcat to wpa_supplicant integration for DPP QR Code operations') + parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO, + action='store_const', dest='loglevel', + help='verbose debug output') + parser.add_argument('--curve', '-c', + help='set a specific curve (P-256, P-384, P-521, BP-256R1, BP-384R1, BP-512R1) for key generation') + parser.add_argument('command', choices=['logcat', + 'display'], + nargs='?') + args = parser.parse_args() + + logging.basicConfig(level=args.loglevel) + + if args.command == "logcat": + dpp_logcat() + elif args.command == "display": + dpp_display(args.curve) + +if __name__ == '__main__': + main() diff --git a/contrib/wpa/wpa_supplicant/examples/wps-ap-cli b/contrib/wpa/wpa_supplicant/examples/wps-ap-cli index cc2cff2ebc24..15d913ef1fae 100755 --- a/contrib/wpa/wpa_supplicant/examples/wps-ap-cli +++ b/contrib/wpa/wpa_supplicant/examples/wps-ap-cli @@ -14,12 +14,12 @@ pbc() enter_pin() { echo "Enter a PIN from a station to be enrolled to the network." - echo -n "Enrollee PIN: " + printf "Enrollee PIN: " read pin cpin=`$CLI wps_check_pin "$pin" | tail -1` if [ "$cpin" = "FAIL-CHECKSUM" ]; then echo "Checksum digit is not valid" - echo -n "Do you want to use this PIN (y/n)? " + printf "Do you want to use this PIN (y/n)? " read resp case "$resp" in y*) @@ -52,7 +52,7 @@ main_menu() echo "3: Show current configuration" echo "0: Exit wps-ap-cli" - echo -n "Command: " + printf "Command: " read cmd case "$cmd" in diff --git a/contrib/wpa/wpa_supplicant/gas_query.c b/contrib/wpa/wpa_supplicant/gas_query.c index 691de0345d13..f4f60c58bee5 100644 --- a/contrib/wpa/wpa_supplicant/gas_query.c +++ b/contrib/wpa/wpa_supplicant/gas_query.c @@ -42,6 +42,7 @@ struct gas_query_pending { unsigned int wait_comeback:1; unsigned int offchannel_tx_started:1; unsigned int retry:1; + unsigned int wildcard_bssid:1; int freq; u16 status_code; struct wpabuf *req; @@ -53,6 +54,7 @@ struct gas_query_pending { const struct wpabuf *adv_proto, const struct wpabuf *resp, u16 status_code); void *ctx; + u8 sa[ETH_ALEN]; }; /** @@ -63,6 +65,9 @@ struct gas_query { struct dl_list pending; /* struct gas_query_pending */ struct gas_query_pending *current; struct wpa_radio_work *work; + struct os_reltime last_mac_addr_rand; + int last_rand_sa_type; + u8 rand_addr[ETH_ALEN]; }; @@ -117,6 +122,8 @@ static const char * gas_result_txt(enum gas_query_result result) return "PEER_ERROR"; case GAS_QUERY_INTERNAL_ERROR: return "INTERNAL_ERROR"; + case GAS_QUERY_STOPPED: + return "STOPPED"; case GAS_QUERY_DELETED_AT_DEINIT: return "DELETED_AT_DEINIT"; } @@ -239,10 +246,17 @@ static void gas_query_tx_status(struct wpa_supplicant *wpa_s, } os_get_reltime(&query->last_oper); - if (result == OFFCHANNEL_SEND_ACTION_SUCCESS) { + if (result == OFFCHANNEL_SEND_ACTION_SUCCESS || + result == OFFCHANNEL_SEND_ACTION_NO_ACK) { 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_NO_ACK) { + wpa_printf(MSG_DEBUG, "GAS: No ACK to GAS request"); + eloop_register_timeout(0, 250000, + gas_query_timeout, gas, query); + } else { + eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, + gas_query_timeout, gas, query); + } if (query->wait_comeback && !query->retry) { eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query); @@ -278,8 +292,9 @@ static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query, }; wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u " - "freq=%d prot=%d", MAC2STR(query->addr), - (unsigned int) wpabuf_len(req), query->freq, prot); + "freq=%d prot=%d using src addr " MACSTR, + MAC2STR(query->addr), (unsigned int) wpabuf_len(req), + query->freq, prot, MAC2STR(query->sa)); if (prot) { u8 *categ = wpabuf_mhead_u8(req); *categ = WLAN_ACTION_PROTECTED_DUAL; @@ -288,17 +303,20 @@ static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query, 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; - if (!gas->wpa_s->conf->gas_address3 || - (gas->wpa_s->current_ssid && - gas->wpa_s->wpa_state >= WPA_ASSOCIATED && - os_memcmp(query->addr, gas->wpa_s->bssid, ETH_ALEN) == 0)) + if (!query->wildcard_bssid && + (!gas->wpa_s->conf->gas_address3 || + (gas->wpa_s->current_ssid && + gas->wpa_s->wpa_state >= WPA_ASSOCIATED && + os_memcmp(query->addr, gas->wpa_s->bssid, ETH_ALEN) == 0))) bssid = query->addr; else bssid = wildcard_bssid; + res = offchannel_send_action(gas->wpa_s, query->freq, query->addr, - gas->wpa_s->own_addr, bssid, - wpabuf_head(req), wpabuf_len(req), - wait_time, gas_query_tx_status, 0); + query->sa, bssid, wpabuf_head(req), + wpabuf_len(req), wait_time, + gas_query_tx_status, 0); + if (res == 0) query->offchannel_tx_started = 1; return res; @@ -407,6 +425,7 @@ static void gas_query_rx_initial(struct gas_query *gas, } if (comeback_delay) { + eloop_cancel_timeout(gas_query_timeout, gas, query); query->wait_comeback = 1; gas_query_tx_comeback_req_delay(gas, query, comeback_delay); return; @@ -724,6 +743,58 @@ static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst) } +static int gas_query_set_sa(struct gas_query *gas, + struct gas_query_pending *query) +{ + struct wpa_supplicant *wpa_s = gas->wpa_s; + struct os_reltime now; + + if (!wpa_s->conf->gas_rand_mac_addr || + !(wpa_s->current_bss ? + (wpa_s->drv_flags & + WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED) : + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA))) { + /* Use own MAC address as the transmitter address */ + os_memcpy(query->sa, wpa_s->own_addr, ETH_ALEN); + return 0; + } + + os_get_reltime(&now); + + if (wpa_s->conf->gas_rand_mac_addr == gas->last_rand_sa_type && + gas->last_mac_addr_rand.sec != 0 && + !os_reltime_expired(&now, &gas->last_mac_addr_rand, + wpa_s->conf->gas_rand_addr_lifetime)) { + wpa_printf(MSG_DEBUG, + "GAS: Use the previously selected random transmitter address " + MACSTR, MAC2STR(gas->rand_addr)); + os_memcpy(query->sa, gas->rand_addr, ETH_ALEN); + return 0; + } + + if (wpa_s->conf->gas_rand_mac_addr == 1 && + random_mac_addr(gas->rand_addr) < 0) { + wpa_printf(MSG_ERROR, "GAS: Failed to get random address"); + return -1; + } + + if (wpa_s->conf->gas_rand_mac_addr == 2 && + random_mac_addr_keep_oui(gas->rand_addr) < 0) { + wpa_printf(MSG_ERROR, + "GAS: Failed to get random address with same OUI"); + return -1; + } + + wpa_printf(MSG_DEBUG, "GAS: Use a new random transmitter address " + MACSTR, MAC2STR(gas->rand_addr)); + os_memcpy(query->sa, gas->rand_addr, ETH_ALEN); + os_get_reltime(&gas->last_mac_addr_rand); + gas->last_rand_sa_type = wpa_s->conf->gas_rand_mac_addr; + + return 0; +} + + /** * gas_query_req - Request a GAS query * @gas: GAS query data from gas_query_init() @@ -736,7 +807,7 @@ static int gas_query_new_dialog_token(struct gas_query *gas, const u8 *dst) * Returns: dialog token (>= 0) on success or -1 on failure */ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq, - struct wpabuf *req, + int wildcard_bssid, struct wpabuf *req, void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, enum gas_query_result result, const struct wpabuf *adv_proto, @@ -758,8 +829,13 @@ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq, return -1; query->gas = gas; + if (gas_query_set_sa(gas, query)) { + os_free(query); + return -1; + } os_memcpy(query->addr, dst, ETH_ALEN); query->dialog_token = dialog_token; + query->wildcard_bssid = !!wildcard_bssid; query->freq = freq; query->cb = cb; query->ctx = ctx; @@ -781,3 +857,27 @@ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq, return dialog_token; } + + +int gas_query_stop(struct gas_query *gas, u8 dialog_token) +{ + struct gas_query_pending *query; + + dl_list_for_each(query, &gas->pending, struct gas_query_pending, list) { + if (query->dialog_token == dialog_token) { + if (!gas->work) { + /* The pending radio work has not yet been + * started, but the pending entry has a + * reference to the soon to be freed query. + * Need to remove that radio work now to avoid + * leaving behind a reference to freed memory. + */ + radio_remove_pending_work(gas->wpa_s, query); + } + gas_query_done(gas, query, GAS_QUERY_STOPPED); + return 0; + } + } + + return -1; +} diff --git a/contrib/wpa/wpa_supplicant/gas_query.h b/contrib/wpa/wpa_supplicant/gas_query.h index ef82097e2424..982c0f7ce60e 100644 --- a/contrib/wpa/wpa_supplicant/gas_query.h +++ b/contrib/wpa/wpa_supplicant/gas_query.h @@ -29,16 +29,18 @@ enum gas_query_result { GAS_QUERY_TIMEOUT, GAS_QUERY_PEER_ERROR, GAS_QUERY_INTERNAL_ERROR, + GAS_QUERY_STOPPED, GAS_QUERY_DELETED_AT_DEINIT }; int gas_query_req(struct gas_query *gas, const u8 *dst, int freq, - struct wpabuf *req, + int wildcard_bssid, struct wpabuf *req, void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, enum gas_query_result result, const struct wpabuf *adv_proto, const struct wpabuf *resp, u16 status_code), void *ctx); +int gas_query_stop(struct gas_query *gas, u8 dialog_token); #else /* CONFIG_GAS */ diff --git a/contrib/wpa/wpa_supplicant/hs20_supplicant.c b/contrib/wpa/wpa_supplicant/hs20_supplicant.c index e88f147bbd1b..f4187900ed42 100644 --- a/contrib/wpa/wpa_supplicant/hs20_supplicant.c +++ b/contrib/wpa/wpa_supplicant/hs20_supplicant.c @@ -49,9 +49,12 @@ struct osu_provider { u8 bssid[ETH_ALEN]; u8 osu_ssid[SSID_MAX_LEN]; u8 osu_ssid_len; + u8 osu_ssid2[SSID_MAX_LEN]; + u8 osu_ssid2_len; char server_uri[256]; u32 osu_methods; /* bit 0 = OMA-DM, bit 1 = SOAP-XML SPP */ char osu_nai[256]; + char osu_nai2[256]; struct osu_lang_string friendly_name[OSU_MAX_ITEMS]; size_t friendly_name_count; struct osu_lang_string serv_desc[OSU_MAX_ITEMS]; @@ -118,6 +121,22 @@ void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id) } +void wpas_hs20_add_roam_cons_sel(struct wpabuf *buf, + const struct wpa_ssid *ssid) +{ + if (!ssid->roaming_consortium_selection || + !ssid->roaming_consortium_selection_len) + return; + + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 4 + ssid->roaming_consortium_selection_len); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ROAMING_CONS_SEL_OUI_TYPE); + wpabuf_put_data(buf, ssid->roaming_consortium_selection, + ssid->roaming_consortium_selection_len); +} + + int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct wpa_bss *bss) { @@ -248,7 +267,7 @@ int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, if (buf == NULL) return -1; - res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s); + res = gas_query_req(wpa_s->gas, dst, freq, 0, buf, anqp_resp_cb, wpa_s); if (res < 0) { wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request"); wpabuf_free(buf); @@ -429,10 +448,9 @@ static int hs20_process_icon_binary_file(struct wpa_supplicant *wpa_s, dl_list_for_each(icon, &wpa_s->icon_head, struct icon_entry, list) { if (icon->dialog_token == dialog_token && !icon->image && os_memcmp(icon->bssid, sa, ETH_ALEN) == 0) { - icon->image = os_malloc(slen); + icon->image = os_memdup(pos, slen); if (!icon->image) return -1; - os_memcpy(icon->image, pos, slen); icon->image_len = slen; hs20_remove_duplicate_icons(wpa_s, icon); wpa_msg(wpa_s, MSG_INFO, @@ -646,6 +664,25 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s, wpa_s, NULL); } break; + case HS20_STYPE_OPERATOR_ICON_METADATA: + wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR + " Operator Icon Metadata", MAC2STR(sa)); + wpa_hexdump(MSG_DEBUG, "Operator Icon Metadata", pos, slen); + if (anqp) { + wpabuf_free(anqp->hs20_operator_icon_metadata); + anqp->hs20_operator_icon_metadata = + wpabuf_alloc_copy(pos, slen); + } + break; + case HS20_STYPE_OSU_PROVIDERS_NAI_LIST: + wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR + " OSU Providers NAI List", MAC2STR(sa)); + if (anqp) { + wpabuf_free(anqp->hs20_osu_providers_nai_list); + anqp->hs20_osu_providers_nai_list = + wpabuf_alloc_copy(pos, slen); + } + break; default: wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype); break; @@ -725,8 +762,15 @@ static void hs20_osu_fetch_done(struct wpa_supplicant *wpa_s) wpa_ssid_txt(osu->osu_ssid, osu->osu_ssid_len)); } + if (osu->osu_ssid2_len) { + fprintf(f, "osu_ssid2=%s\n", + wpa_ssid_txt(osu->osu_ssid2, + osu->osu_ssid2_len)); + } if (osu->osu_nai[0]) fprintf(f, "osu_nai=%s\n", osu->osu_nai); + if (osu->osu_nai2[0]) + fprintf(f, "osu_nai2=%s\n", osu->osu_nai2); for (j = 0; j < osu->friendly_name_count; j++) { fprintf(f, "friendly_name=%s:%s\n", osu->friendly_name[j].lang, @@ -790,6 +834,7 @@ void hs20_next_osu_icon(struct wpa_supplicant *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 *osu_ssid2, u8 osu_ssid2_len, const u8 *pos, size_t len) { struct osu_provider *prov; @@ -811,6 +856,9 @@ static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, 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; + if (osu_ssid2) + os_memcpy(prov->osu_ssid2, osu_ssid2, osu_ssid2_len); + prov->osu_ssid2_len = osu_ssid2_len; /* OSU Friendly Name Length */ if (end - pos < 2) { @@ -992,18 +1040,30 @@ void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s) struct wpabuf *prov_anqp; const u8 *pos, *end; u16 len; - const u8 *osu_ssid; - u8 osu_ssid_len; + const u8 *osu_ssid, *osu_ssid2; + u8 osu_ssid_len, osu_ssid2_len; u8 num_providers; hs20_free_osu_prov(wpa_s); dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + struct wpa_ie_data data; + const u8 *ie; + if (bss->anqp == NULL) continue; prov_anqp = bss->anqp->hs20_osu_providers_list; if (prov_anqp == NULL) continue; + ie = wpa_bss_get_ie(bss, WLAN_EID_RSN); + if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &data) == 0 && + (data.key_mgmt & WPA_KEY_MGMT_OSEN)) { + osu_ssid2 = bss->ssid; + osu_ssid2_len = bss->ssid_len; + } else { + osu_ssid2 = NULL; + osu_ssid2_len = 0; + } 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", @@ -1045,7 +1105,8 @@ void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s) if (len > (unsigned int) (end - pos)) break; hs20_osu_add_prov(wpa_s, bss, osu_ssid, - osu_ssid_len, pos, len); + osu_ssid_len, osu_ssid2, + osu_ssid2_len, pos, len); pos += len; } @@ -1054,6 +1115,35 @@ void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s) "extra data after OSU Providers", (int) (end - pos)); } + + prov_anqp = bss->anqp->hs20_osu_providers_nai_list; + if (!prov_anqp) + continue; + wpa_printf(MSG_DEBUG, + "HS 2.0: Parsing OSU Providers NAI List from " + MACSTR, MAC2STR(bss->bssid)); + wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers NAI List", + prov_anqp); + pos = wpabuf_head(prov_anqp); + end = pos + wpabuf_len(prov_anqp); + num_providers = 0; + while (end - pos > 0) { + len = *pos++; + if (end - pos < len) { + wpa_printf(MSG_DEBUG, + "HS 2.0: Not enough room for OSU_NAI"); + break; + } + if (num_providers >= wpa_s->osu_prov_count) { + wpa_printf(MSG_DEBUG, + "HS 2.0: Ignore unexpected OSU Provider NAI List entries"); + break; + } + os_memcpy(wpa_s->osu_prov[num_providers].osu_nai2, + pos, len); + pos += len; + num_providers++; + } } wpa_s->fetch_osu_icon_in_progress = 1; @@ -1207,6 +1297,18 @@ void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code, } +void hs20_rx_t_c_acceptance(struct wpa_supplicant *wpa_s, const char *url) +{ + if (!wpa_sm_pmf_enabled(wpa_s->wpa)) { + wpa_printf(MSG_DEBUG, + "HS 2.0: Ignore Terms and Conditions Acceptance since PMF was not enabled"); + return; + } + + wpa_msg(wpa_s, MSG_INFO, HS20_T_C_ACCEPTANCE "%s", url); +} + + void hs20_init(struct wpa_supplicant *wpa_s) { dl_list_init(&wpa_s->icon_head); diff --git a/contrib/wpa/wpa_supplicant/hs20_supplicant.h b/contrib/wpa/wpa_supplicant/hs20_supplicant.h index 0dd559fdbf01..66fc540be3e4 100644 --- a/contrib/wpa/wpa_supplicant/hs20_supplicant.h +++ b/contrib/wpa/wpa_supplicant/hs20_supplicant.h @@ -10,6 +10,8 @@ void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s); void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id); +void wpas_hs20_add_roam_cons_sel(struct wpabuf *buf, + const struct wpa_ssid *ssid); int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes, const u8 *payload, size_t payload_len, int inmem); @@ -27,6 +29,7 @@ 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_rx_t_c_acceptance(struct wpa_supplicant *wpa_s, const char *url); void hs20_free_osu_prov(struct wpa_supplicant *wpa_s); void hs20_next_osu_icon(struct wpa_supplicant *wpa_s); diff --git a/contrib/wpa/wpa_supplicant/ibss_rsn.c b/contrib/wpa/wpa_supplicant/ibss_rsn.c index 53d7d57bde35..00919d14a55e 100644 --- a/contrib/wpa/wpa_supplicant/ibss_rsn.c +++ b/contrib/wpa/wpa_supplicant/ibss_rsn.c @@ -259,9 +259,13 @@ 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 *p2p_dev_addr, const u8 *prev_psk) + const u8 *p2p_dev_addr, const u8 *prev_psk, + size_t *psk_len) { struct ibss_rsn *ibss_rsn = ctx; + + if (psk_len) + *psk_len = PMK_LEN; wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)", __func__, MAC2STR(addr), prev_psk); if (prev_psk) @@ -408,7 +412,15 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn, const u8 *own_addr, struct wpa_ssid *ssid) { struct wpa_auth_config conf; - struct wpa_auth_callbacks cb; + static const struct wpa_auth_callbacks cb = { + .logger = auth_logger, + .set_eapol = auth_set_eapol, + .send_eapol = auth_send_eapol, + .get_psk = auth_get_psk, + .set_key = auth_set_key, + .for_each_sta = auth_for_each_sta, + .disconnect = ibss_rsn_disconnect, + }; wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine"); @@ -420,18 +432,10 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn, conf.wpa_group = WPA_CIPHER_CCMP; conf.eapol_version = 2; conf.wpa_group_rekey = ssid->group_rekey ? ssid->group_rekey : 600; + conf.wpa_group_update_count = 4; + conf.wpa_pairwise_update_count = 4; - os_memset(&cb, 0, sizeof(cb)); - cb.ctx = ibss_rsn; - cb.logger = auth_logger; - cb.set_eapol = auth_set_eapol; - cb.send_eapol = auth_send_eapol; - cb.get_psk = auth_get_psk; - cb.set_key = auth_set_key; - cb.for_each_sta = auth_for_each_sta; - cb.disconnect = ibss_rsn_disconnect; - - ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb); + ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb, ibss_rsn); if (ibss_rsn->auth_group == NULL) { wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed"); return -1; @@ -458,7 +462,7 @@ static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn, "\x00\x0f\xac\x04" "\x01\x00\x00\x0f\xac\x04" "\x01\x00\x00\x0f\xac\x02" - "\x00\x00", 22, NULL, 0) != + "\x00\x00", 22, NULL, 0, NULL, 0) != WPA_IE_OK) { wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed"); return -1; @@ -760,10 +764,9 @@ static int ibss_rsn_process_rx_eapol(struct ibss_rsn *ibss_rsn, if (supp < 0) return -1; - tmp = os_malloc(len); + tmp = os_memdup(buf, len); if (tmp == NULL) return -1; - os_memcpy(tmp, buf, len); if (supp) { peer->authentication_status |= IBSS_RSN_AUTH_EAPOL_BY_PEER; wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant from " @@ -838,6 +841,18 @@ static void ibss_rsn_handle_auth_1_of_2(struct ibss_rsn *ibss_rsn, MAC2STR(addr)); if (peer && + peer->authentication_status & (IBSS_RSN_SET_PTK_SUPP | + IBSS_RSN_SET_PTK_AUTH)) { + /* Clear the TK for this pair to allow recovery from the case + * where the peer STA has restarted and lost its key while we + * still have a pairwise key configured. */ + wpa_printf(MSG_DEBUG, "RSN: Clear pairwise key for peer " + MACSTR, MAC2STR(addr)); + wpa_drv_set_key(ibss_rsn->wpa_s, WPA_ALG_NONE, addr, 0, 0, + NULL, 0, NULL, 0); + } + + if (peer && peer->authentication_status & IBSS_RSN_AUTH_EAPOL_BY_PEER) { if (peer->own_auth_tx.sec) { struct os_reltime now, diff; diff --git a/contrib/wpa/wpa_supplicant/interworking.c b/contrib/wpa/wpa_supplicant/interworking.c index 1fb40c74e5cf..60c8be9a6c6a 100644 --- a/contrib/wpa/wpa_supplicant/interworking.c +++ b/contrib/wpa/wpa_supplicant/interworking.c @@ -106,10 +106,12 @@ static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids, if (buf == NULL) return NULL; - len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST); - for (i = 0; i < num_ids; i++) - wpabuf_put_le16(buf, info_ids[i]); - gas_anqp_set_element_len(buf, len_pos); + if (num_ids > 0) { + len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST); + for (i = 0; i < num_ids; i++) + wpabuf_put_le16(buf, info_ids[i]); + gas_anqp_set_element_len(buf, len_pos); + } if (extra) wpabuf_put_buf(buf, extra); @@ -146,6 +148,8 @@ static int cred_with_roaming_consortium(struct wpa_supplicant *wpa_s) return 1; if (cred->required_roaming_consortium_len) return 1; + if (cred->num_roaming_consortiums) + return 1; } return 0; } @@ -299,8 +303,10 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s, wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY); if (all) wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS); - if (all) + if (all) { wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_LIST); + wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_NAI_LIST); + } gas_anqp_set_element_len(extra, len_pos); } #endif /* CONFIG_HS20 */ @@ -310,7 +316,7 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s, if (buf == NULL) return -1; - res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf, + res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, 0, buf, interworking_anqp_resp_cb, wpa_s); if (res < 0) { wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request"); @@ -1143,6 +1149,23 @@ static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp, } +static int cred_roaming_consortiums_match(const u8 *ie, + const struct wpabuf *anqp, + const struct wpa_cred *cred) +{ + unsigned int i; + + for (i = 0; i < cred->num_roaming_consortiums; i++) { + if (roaming_consortium_match(ie, anqp, + cred->roaming_consortiums[i], + cred->roaming_consortiums_len[i])) + return 1; + } + + return 0; +} + + static int cred_no_required_oi_match(struct wpa_cred *cred, struct wpa_bss *bss) { const u8 *ie; @@ -1347,27 +1370,28 @@ static struct wpa_cred * interworking_credentials_available_roaming_consortium( { struct wpa_cred *cred, *selected = NULL; const u8 *ie; + const struct wpabuf *anqp; int is_excluded = 0; ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM); + anqp = bss->anqp ? bss->anqp->roaming_consortium : NULL; - if (ie == NULL && - (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL)) + if (!ie && !anqp) return NULL; if (wpa_s->conf->cred == NULL) return NULL; for (cred = wpa_s->conf->cred; cred; cred = cred->next) { - if (cred->roaming_consortium_len == 0) + if (cred->roaming_consortium_len == 0 && + cred->num_roaming_consortiums == 0) continue; - if (!roaming_consortium_match(ie, - bss->anqp ? - bss->anqp->roaming_consortium : - NULL, - cred->roaming_consortium, - cred->roaming_consortium_len)) + if ((cred->roaming_consortium_len == 0 || + !roaming_consortium_match(ie, anqp, + cred->roaming_consortium, + cred->roaming_consortium_len)) && + !cred_roaming_consortiums_match(ie, anqp, cred)) continue; if (cred_no_required_oi_match(cred, bss)) @@ -1533,6 +1557,9 @@ static int interworking_connect_roaming_consortium( struct wpa_bss *bss, int only_add) { struct wpa_ssid *ssid; + const u8 *ie; + const struct wpabuf *anqp; + unsigned int i; wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR " based on roaming consortium match", MAC2STR(bss->bssid)); @@ -1562,6 +1589,26 @@ static int interworking_connect_roaming_consortium( if (interworking_set_hs20_params(wpa_s, ssid) < 0) goto fail; + ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM); + anqp = bss->anqp ? bss->anqp->roaming_consortium : NULL; + for (i = 0; (ie || anqp) && i < cred->num_roaming_consortiums; i++) { + if (!roaming_consortium_match( + ie, anqp, cred->roaming_consortiums[i], + cred->roaming_consortiums_len[i])) + continue; + + ssid->roaming_consortium_selection = + os_malloc(cred->roaming_consortiums_len[i]); + if (!ssid->roaming_consortium_selection) + goto fail; + os_memcpy(ssid->roaming_consortium_selection, + cred->roaming_consortiums[i], + cred->roaming_consortiums_len[i]); + ssid->roaming_consortium_selection_len = + cred->roaming_consortiums_len[i]; + break; + } + if (cred->eap_method == NULL) { wpa_msg(wpa_s, MSG_DEBUG, "Interworking: No EAP method set for credential using roaming consortium"); @@ -1769,9 +1816,10 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, switch (eap->method) { case EAP_TYPE_TTLS: if (eap->inner_method) { - os_snprintf(buf, sizeof(buf), "\"autheap=%s\"", - eap_get_name(EAP_VENDOR_IETF, - eap->inner_method)); + name = eap_get_name(EAP_VENDOR_IETF, eap->inner_method); + if (!name) + goto fail; + os_snprintf(buf, sizeof(buf), "\"autheap=%s\"", name); if (wpa_config_set(ssid, "phase2", buf, 0) < 0) goto fail; break; @@ -1894,7 +1942,7 @@ static struct wpa_cred * interworking_credentials_available_3gpp( 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->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, -1, wpa_s->imsi, &len); if (wpa_s->mnc_len > 0) { @@ -2530,7 +2578,8 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s) wpa_msg(wpa_s, MSG_INFO, INTERWORKING_SELECTED MACSTR, MAC2STR(selected->bssid)); interworking_connect(wpa_s, selected, 0); - } + } else if (wpa_s->wpa_state == WPA_SCANNING) + wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); } @@ -2693,7 +2742,7 @@ 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, u32 subtypes, - int get_cell_pref) + u32 mbo_subtypes) { struct wpabuf *buf; struct wpabuf *extra_buf = NULL; @@ -2727,13 +2776,14 @@ int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, #endif /* CONFIG_HS20 */ #ifdef CONFIG_MBO - if (get_cell_pref) { + if (mbo_subtypes) { struct wpabuf *mbo; - mbo = mbo_build_anqp_buf(wpa_s, bss); + mbo = mbo_build_anqp_buf(wpa_s, bss, mbo_subtypes); if (mbo) { if (wpabuf_resize(&extra_buf, wpabuf_len(mbo))) { wpabuf_free(extra_buf); + wpabuf_free(mbo); return -1; } wpabuf_put_buf(extra_buf, mbo); @@ -2747,7 +2797,7 @@ int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, if (buf == NULL) return -1; - res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s); + res = gas_query_req(wpa_s->gas, dst, freq, 0, buf, anqp_resp_cb, wpa_s); if (res < 0) { wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request"); wpabuf_free(buf); @@ -2796,6 +2846,31 @@ static void anqp_add_extra(struct wpa_supplicant *wpa_s, } +static void interworking_parse_venue_url(struct wpa_supplicant *wpa_s, + const u8 *data, size_t len) +{ + const u8 *pos = data, *end = data + len; + char url[255]; + + while (end - pos >= 2) { + u8 slen, num; + + slen = *pos++; + if (slen < 1 || slen > end - pos) { + wpa_printf(MSG_DEBUG, + "ANQP: Truncated Venue URL Duple field"); + return; + } + + num = *pos++; + os_memcpy(url, pos, slen - 1); + url[slen - 1] = '\0'; + wpa_msg(wpa_s, MSG_INFO, RX_VENUE_URL "%u %s", num, url); + pos += slen - 1; + } +} + + static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, const u8 *sa, u16 info_id, @@ -2804,9 +2879,7 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, { const u8 *pos = data; struct wpa_bss_anqp *anqp = NULL; -#ifdef CONFIG_HS20 u8 type; -#endif /* CONFIG_HS20 */ if (bss) anqp = bss->anqp; @@ -2892,12 +2965,35 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, anqp->domain_name = wpabuf_alloc_copy(pos, slen); } break; +#ifdef CONFIG_FILS + case ANQP_FILS_REALM_INFO: + wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR + " FILS Realm Information", MAC2STR(sa)); + wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: FILS Realm Information", + pos, slen); + if (anqp) { + wpabuf_free(anqp->fils_realm_info); + anqp->fils_realm_info = wpabuf_alloc_copy(pos, slen); + } + break; +#endif /* CONFIG_FILS */ + case ANQP_VENUE_URL: + wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR " Venue URL", + MAC2STR(sa)); + anqp_add_extra(wpa_s, anqp, info_id, pos, slen); + + if (!wpa_sm_pmf_enabled(wpa_s->wpa)) { + wpa_printf(MSG_DEBUG, + "ANQP: Ignore Venue URL since PMF was not enabled"); + break; + } + interworking_parse_venue_url(wpa_s, pos, slen); + break; case ANQP_VENDOR_SPECIFIC: if (slen < 3) return; switch (WPA_GET_BE24(pos)) { -#ifdef CONFIG_HS20 case OUI_WFA: pos += 3; slen -= 3; @@ -2908,19 +3004,26 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, slen--; switch (type) { +#ifdef CONFIG_HS20 case HS20_ANQP_OUI_TYPE: hs20_parse_rx_hs20_anqp_resp(wpa_s, bss, sa, pos, slen, dialog_token); break; +#endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + case MBO_ANQP_OUI_TYPE: + mbo_parse_rx_anqp_resp(wpa_s, bss, sa, + pos, slen); + break; +#endif /* CONFIG_MBO */ default: wpa_msg(wpa_s, MSG_DEBUG, - "HS20: Unsupported ANQP vendor type %u", + "ANQP: Unsupported ANQP vendor type %u", type); break; } break; -#endif /* CONFIG_HS20 */ default: wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Unsupported vendor-specific ANQP OUI %06x", @@ -3133,7 +3236,7 @@ int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst, } else wpabuf_put_le16(buf, 0); - res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s); + res = gas_query_req(wpa_s->gas, dst, freq, 0, buf, gas_resp_cb, wpa_s); if (res < 0) { wpa_msg(wpa_s, MSG_DEBUG, "GAS: Failed to send Query Request"); wpabuf_free(buf); diff --git a/contrib/wpa/wpa_supplicant/interworking.h b/contrib/wpa/wpa_supplicant/interworking.h index 3d22292618b2..37ee2e904e48 100644 --- a/contrib/wpa/wpa_supplicant/interworking.h +++ b/contrib/wpa/wpa_supplicant/interworking.h @@ -13,7 +13,7 @@ enum gas_query_result; int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u16 info_ids[], size_t num_ids, u32 subtypes, - int get_cell_pref); + u32 mbo_subtypes); void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token, enum gas_query_result result, const struct wpabuf *adv_proto, diff --git a/contrib/wpa/wpa_supplicant/mbo.c b/contrib/wpa/wpa_supplicant/mbo.c index 7e049be3df41..5adf61e58bd0 100644 --- a/contrib/wpa/wpa_supplicant/mbo.c +++ b/contrib/wpa/wpa_supplicant/mbo.c @@ -38,6 +38,19 @@ static int wpas_mbo_validate_non_pref_chan(u8 oper_class, u8 chan, u8 reason) } +const u8 * mbo_attr_from_mbo_ie(const u8 *mbo_ie, enum mbo_attr_id attr) +{ + const u8 *mbo; + u8 ie_len = mbo_ie[1]; + + if (ie_len < MBO_IE_HEADER - 2) + return NULL; + mbo = mbo_ie + MBO_IE_HEADER; + + return get_ie(mbo, 2 + ie_len - MBO_IE_HEADER, attr); +} + + const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr) { const u8 *mbo, *end; @@ -149,12 +162,14 @@ static void wpas_mbo_non_pref_chan_attrs(struct wpa_supplicant *wpa_s, } -int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len) +int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len, + int add_oce_capa) { struct wpabuf *mbo; int res; - if (len < MBO_IE_HEADER + 3 + 7) + if (len < MBO_IE_HEADER + 3 + 7 + + ((wpa_s->enable_oce & OCE_STA) ? 3 : 0)) return 0; /* Leave room for the MBO IE header */ @@ -173,9 +188,16 @@ int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len) wpabuf_put_u8(mbo, 1); wpabuf_put_u8(mbo, wpa_s->conf->mbo_cell_capa); + /* Add OCE capability indication attribute if OCE is enabled */ + if ((wpa_s->enable_oce & OCE_STA) && add_oce_capa) { + wpabuf_put_u8(mbo, OCE_ATTR_ID_CAPA_IND); + wpabuf_put_u8(mbo, 1); + wpabuf_put_u8(mbo, OCE_RELEASE); + } + res = mbo_add_ie(buf, len, wpabuf_head_u8(mbo), wpabuf_len(mbo)); if (!res) - wpa_printf(MSG_ERROR, "Failed to add MBO IE"); + wpa_printf(MSG_ERROR, "Failed to add MBO/OCE IE"); wpabuf_free(mbo); return res; @@ -277,11 +299,10 @@ int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s, non_pref_chan ? non_pref_chan : "N/A"); /* - * The shortest channel configuration is 10 characters - commas, 3 - * colons, and 4 values that one of them (oper_class) is 2 digits or - * more. + * The shortest channel configuration is 7 characters - 3 colons and + * 4 values. */ - if (!non_pref_chan || os_strlen(non_pref_chan) < 10) + if (!non_pref_chan || os_strlen(non_pref_chan) < 7) goto update; cmd = os_strdup(non_pref_chan); @@ -369,315 +390,30 @@ fail: void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie) { + u8 *len; + wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); - wpabuf_put_u8(ie, 7); + len = wpabuf_put(ie, 1); + wpabuf_put_be24(ie, OUI_WFA); wpabuf_put_u8(ie, MBO_OUI_TYPE); wpabuf_put_u8(ie, MBO_ATTR_ID_CELL_DATA_CAPA); wpabuf_put_u8(ie, 1); wpabuf_put_u8(ie, wpa_s->conf->mbo_cell_capa); -} - - -enum chan_allowed { - NOT_ALLOWED, ALLOWED -}; - -static enum chan_allowed allow_channel(struct hostapd_hw_modes *mode, u8 chan, - unsigned int *flags) -{ - int i; - - for (i = 0; i < mode->num_channels; i++) { - if (mode->channels[i].chan == chan) - break; + if (wpa_s->enable_oce & OCE_STA) { + wpabuf_put_u8(ie, OCE_ATTR_ID_CAPA_IND); + wpabuf_put_u8(ie, 1); + wpabuf_put_u8(ie, OCE_RELEASE); } - - if (i == mode->num_channels || - (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)) - return NOT_ALLOWED; - - if (flags) - *flags = mode->channels[i].flag; - - return ALLOWED; -} - - -static int get_center_80mhz(struct hostapd_hw_modes *mode, u8 channel) -{ - u8 center_channels[] = {42, 58, 106, 122, 138, 155}; - size_t i; - - if (mode->mode != HOSTAPD_MODE_IEEE80211A) - return 0; - - 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 verify_80mhz(struct hostapd_hw_modes *mode, u8 channel) -{ - u8 center_chan; - unsigned int i; - - center_chan = get_center_80mhz(mode, channel); - if (!center_chan) - return NOT_ALLOWED; - - /* check all the channels are available */ - for (i = 0; i < 4; i++) { - unsigned int flags; - u8 adj_chan = center_chan - 6 + i * 4; - - if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED) - return NOT_ALLOWED; - - if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) || - (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) || - (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) || - (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10))) - return NOT_ALLOWED; - } - - return ALLOWED; -} - - -static int get_center_160mhz(struct hostapd_hw_modes *mode, u8 channel) -{ - u8 center_channels[] = { 50, 114 }; - unsigned int i; - - if (mode->mode != HOSTAPD_MODE_IEEE80211A) - return 0; - - for (i = 0; i < ARRAY_SIZE(center_channels); i++) { - /* - * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64), - * so the center channel is 14 channels away from the start/end. - */ - if (channel >= center_channels[i] - 14 && - channel <= center_channels[i] + 14) - return center_channels[i]; - } - - return 0; -} - - -static enum chan_allowed verify_160mhz(struct hostapd_hw_modes *mode, - u8 channel) -{ - u8 center_chan; - unsigned int i; - - center_chan = get_center_160mhz(mode, channel); - if (!center_chan) - return NOT_ALLOWED; - - /* Check all the channels are available */ - for (i = 0; i < 8; i++) { - unsigned int flags; - u8 adj_chan = center_chan - 14 + i * 4; - - if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED) - return NOT_ALLOWED; - - if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) || - (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) || - (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) || - (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) || - (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) || - (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) || - (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) || - (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10))) - return NOT_ALLOWED; - } - - return ALLOWED; -} - - -static enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, - u8 channel, u8 bw) -{ - unsigned int flag = 0; - enum chan_allowed res, res2; - - res2 = res = allow_channel(mode, channel, &flag); - if (bw == BW40MINUS) { - if (!(flag & HOSTAPD_CHAN_HT40MINUS)) - return NOT_ALLOWED; - res2 = allow_channel(mode, channel - 4, NULL); - } else if (bw == BW40PLUS) { - if (!(flag & HOSTAPD_CHAN_HT40PLUS)) - return NOT_ALLOWED; - res2 = allow_channel(mode, channel + 4, NULL); - } else if (bw == BW80) { - /* - * channel is a center channel and as such, not necessarily a - * valid 20 MHz channels. Override earlier allow_channel() - * result and use only the 80 MHz specific version. - */ - res2 = res = verify_80mhz(mode, channel); - } else if (bw == BW160) { - /* - * channel is a center channel and as such, not necessarily a - * valid 20 MHz channels. Override earlier allow_channel() - * result and use only the 160 MHz specific version. - */ - res2 = res = verify_160mhz(mode, channel); - } else if (bw == BW80P80) { - /* - * channel is a center channel and as such, not necessarily a - * valid 20 MHz channels. Override earlier allow_channel() - * result and use only the 80 MHz specific version. - */ - res2 = res = verify_80mhz(mode, channel); - } - - if (res == NOT_ALLOWED || res2 == NOT_ALLOWED) - return NOT_ALLOWED; - - return ALLOWED; -} - - -static int wpas_op_class_supported(struct wpa_supplicant *wpa_s, - const struct oper_class_map *op_class) -{ - int chan; - size_t i; - struct hostapd_hw_modes *mode; - int found; - - mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode); - if (!mode) - return 0; - - if (op_class->op_class == 128) { - u8 channels[] = { 42, 58, 106, 122, 138, 155 }; - - for (i = 0; i < ARRAY_SIZE(channels); i++) { - if (verify_channel(mode, channels[i], op_class->bw) == - ALLOWED) - return 1; - } - - return 0; - } - - if (op_class->op_class == 129) { - /* Check if either 160 MHz channels is allowed */ - return verify_channel(mode, 50, op_class->bw) == ALLOWED || - verify_channel(mode, 114, op_class->bw) == ALLOWED; - } - - if (op_class->op_class == 130) { - /* Need at least two non-contiguous 80 MHz segments */ - found = 0; - - if (verify_channel(mode, 42, op_class->bw) == ALLOWED || - verify_channel(mode, 58, op_class->bw) == ALLOWED) - found++; - if (verify_channel(mode, 106, op_class->bw) == ALLOWED || - verify_channel(mode, 122, op_class->bw) == ALLOWED || - verify_channel(mode, 138, op_class->bw) == ALLOWED) - found++; - if (verify_channel(mode, 106, op_class->bw) == ALLOWED && - verify_channel(mode, 138, op_class->bw) == ALLOWED) - found++; - if (verify_channel(mode, 155, op_class->bw) == ALLOWED) - found++; - - if (found >= 2) - return 1; - - return 0; - } - - found = 0; - for (chan = op_class->min_chan; chan <= op_class->max_chan; - chan += op_class->inc) { - if (verify_channel(mode, chan, op_class->bw) == ALLOWED) { - found = 1; - break; - } - } - - return found; -} - - -int wpas_mbo_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos, - size_t len) -{ - struct wpabuf *buf; - u8 op, current, chan; - u8 *ie_len; - int res; - - /* - * Assume 20 MHz channel for now. - * TODO: Use the secondary channel and VHT channel width that will be - * used after association. - */ - if (ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT, - ¤t, &chan) == NUM_HOSTAPD_MODES) - return 0; - - /* - * Need 3 bytes for EID, length, and current operating class, plus - * 1 byte for every other supported operating class. - */ - buf = wpabuf_alloc(global_op_class_size + 3); - if (!buf) - return 0; - - wpabuf_put_u8(buf, WLAN_EID_SUPPORTED_OPERATING_CLASSES); - /* Will set the length later, putting a placeholder */ - ie_len = wpabuf_put(buf, 1); - wpabuf_put_u8(buf, current); - - for (op = 0; global_op_class[op].op_class; op++) { - if (wpas_op_class_supported(wpa_s, &global_op_class[op])) - wpabuf_put_u8(buf, global_op_class[op].op_class); - } - - *ie_len = wpabuf_len(buf) - 2; - if (*ie_len < 2 || wpabuf_len(buf) > len) { - wpa_printf(MSG_ERROR, - "Failed to add supported operating classes IE"); - res = 0; - } else { - os_memcpy(pos, wpabuf_head(buf), wpabuf_len(buf)); - res = wpabuf_len(buf); - wpa_hexdump_buf(MSG_DEBUG, - "MBO: Added supported operating classes IE", - buf); - } - - wpabuf_free(buf); - return res; + *len = (u8 *) wpabuf_put(ie, 0) - len - 1; } void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie, size_t len) { - const u8 *pos, *cell_pref = NULL, *reason = NULL; + const u8 *pos, *cell_pref = NULL; u8 id, elen; u16 disallowed_sec = 0; @@ -712,7 +448,8 @@ void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie, if (elen != 1) goto fail; - reason = pos; + wpa_s->wnm_mbo_trans_reason_present = 1; + wpa_s->wnm_mbo_transition_reason = *pos; break; case MBO_ATTR_ID_ASSOC_RETRY_DELAY: if (elen != 2) @@ -726,6 +463,9 @@ void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie, } else if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { disallowed_sec = WPA_GET_LE16(pos); + wpa_printf(MSG_DEBUG, + "MBO: Association retry delay: %u", + disallowed_sec); } else { wpa_printf(MSG_DEBUG, "MBO: Association retry delay attribute not in disassoc imminent mode"); @@ -755,9 +495,9 @@ void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie, wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u", *cell_pref); - if (reason) + if (wpa_s->wnm_mbo_trans_reason_present) wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u", - *reason); + wpa_s->wnm_mbo_transition_reason); if (disallowed_sec && wpa_s->current_bss) wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid, @@ -809,10 +549,11 @@ void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa) struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss) + struct wpa_bss *bss, u32 mbo_subtypes) { struct wpabuf *anqp_buf; u8 *len_pos; + u8 i; if (!wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) { wpa_printf(MSG_INFO, "MBO: " MACSTR @@ -821,7 +562,8 @@ struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s, return NULL; } - anqp_buf = wpabuf_alloc(10); + /* Allocate size for the maximum case - all MBO subtypes are set */ + anqp_buf = wpabuf_alloc(9 + MAX_MBO_ANQP_SUBTYPE); if (!anqp_buf) return NULL; @@ -829,8 +571,43 @@ struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s, wpabuf_put_be24(anqp_buf, OUI_WFA); wpabuf_put_u8(anqp_buf, MBO_ANQP_OUI_TYPE); - wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF); + wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_QUERY_LIST); + + /* The first valid MBO subtype is 1 */ + for (i = 1; i <= MAX_MBO_ANQP_SUBTYPE; i++) { + if (mbo_subtypes & BIT(i)) + wpabuf_put_u8(anqp_buf, i); + } + gas_anqp_set_element_len(anqp_buf, len_pos); return anqp_buf; } + + +void mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, const u8 *sa, + const u8 *data, size_t slen) +{ + const u8 *pos = data; + u8 subtype; + + if (slen < 1) + return; + + subtype = *pos++; + slen--; + + switch (subtype) { + case MBO_ANQP_SUBTYPE_CELL_CONN_PREF: + if (slen < 1) + break; + wpa_msg(wpa_s, MSG_INFO, RX_MBO_ANQP MACSTR + " cell_conn_pref=%u", MAC2STR(sa), *pos); + break; + default: + wpa_printf(MSG_DEBUG, "MBO: Unsupported ANQP subtype %u", + subtype); + break; + } +} diff --git a/contrib/wpa/wpa_supplicant/mesh.c b/contrib/wpa/wpa_supplicant/mesh.c index d67d3b2aa390..38b9fb320ca9 100644 --- a/contrib/wpa/wpa_supplicant/mesh.c +++ b/contrib/wpa/wpa_supplicant/mesh.c @@ -84,6 +84,7 @@ static struct mesh_conf * mesh_config_create(struct wpa_supplicant *wpa_s, MESH_CONF_SEC_AMPE; else conf->security |= MESH_CONF_SEC_NONE; +#ifdef CONFIG_IEEE80211W conf->ieee80211w = ssid->ieee80211w; if (conf->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) { if (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP) @@ -91,6 +92,7 @@ static struct mesh_conf * mesh_config_create(struct wpa_supplicant *wpa_s, else conf->ieee80211w = NO_MGMT_FRAME_PROTECTION; } +#endif /* CONFIG_IEEE80211W */ cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher, 0); if (cipher < 0 || cipher == WPA_CIPHER_TKIP) { @@ -146,7 +148,8 @@ static void wpas_mesh_copy_groups(struct hostapd_data *bss, static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid) + struct wpa_ssid *ssid, + struct hostapd_freq_params *freq) { struct hostapd_iface *ifmsh; struct hostapd_data *bss; @@ -154,8 +157,10 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, 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 }; + const char *password; size_t len; int rate_len; + int frequency; if (!wpa_s->conf->user_mpm) { /* not much for us to do here */ @@ -164,7 +169,7 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, return 0; } - wpa_s->ifmsh = ifmsh = os_zalloc(sizeof(*wpa_s->ifmsh)); + wpa_s->ifmsh = ifmsh = hostapd_alloc_iface(); if (!ifmsh) return -ENOMEM; @@ -175,17 +180,23 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, if (!ifmsh->bss) goto out_free; - ifmsh->bss[0] = bss = os_zalloc(sizeof(struct hostapd_data)); + ifmsh->bss[0] = bss = hostapd_alloc_bss_data(NULL, NULL, NULL); if (!bss) goto out_free; - dl_list_init(&bss->nr_db); + ifmsh->bss[0]->msg_ctx = wpa_s; 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; + frequency = ssid->frequency; + if (frequency != freq->freq && + frequency == freq->freq + freq->sec_channel_offset * 20) { + wpa_printf(MSG_DEBUG, "mesh: pri/sec channels switched"); + frequency = freq->freq; + } + wpa_s->assoc_freq = frequency; wpa_s->current_ssid = ssid; /* setup an AP config for auth processing */ @@ -211,10 +222,10 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, ifmsh->mconf = mconf; /* need conf->hw_mode for supported rates. */ - conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency, &conf->channel); + conf->hw_mode = ieee80211_freq_to_chan(frequency, &conf->channel); if (conf->hw_mode == NUM_HOSTAPD_MODES) { wpa_printf(MSG_ERROR, "Unsupported mesh mode frequency: %d MHz", - ssid->frequency); + frequency); goto out_free; } if (ssid->ht40) @@ -225,13 +236,13 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, case VHT_CHANWIDTH_80MHZ: case VHT_CHANWIDTH_80P80MHZ: ieee80211_freq_to_chan( - ssid->frequency, + frequency, &conf->vht_oper_centr_freq_seg0_idx); conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2; break; case VHT_CHANWIDTH_160MHZ: ieee80211_freq_to_chan( - ssid->frequency, + frequency, &conf->vht_oper_centr_freq_seg0_idx); conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2; conf->vht_oper_centr_freq_seg0_idx += 40 / 5; @@ -250,11 +261,10 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, * 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)); + conf->basic_rates = os_memdup(basic_rates_erp, + 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; @@ -283,7 +293,10 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, } if (mconf->security != MESH_CONF_SEC_NONE) { - if (ssid->passphrase == NULL) { + password = ssid->sae_password; + if (!password) + password = ssid->passphrase; + if (!password) { wpa_printf(MSG_ERROR, "mesh: Passphrase for SAE not configured"); goto out_free; @@ -297,16 +310,15 @@ static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s, wpas_mesh_copy_groups(bss, wpa_s); } else { bss->conf->sae_groups = - os_malloc(sizeof(default_groups)); + os_memdup(default_groups, + 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); + len = os_strlen(password); bss->conf->ssid.wpa_passphrase = - dup_binstr(ssid->passphrase, len); + dup_binstr(password, len); wpa_s->mesh_rsn = mesh_rsn_auth_init(wpa_s, mconf); if (!wpa_s->mesh_rsn) @@ -406,6 +418,10 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, else if (wpa_s->conf->dtim_period > 0) params.dtim_period = wpa_s->conf->dtim_period; params.conf.max_peer_links = wpa_s->conf->max_peer_links; + if (ssid->mesh_rssi_threshold < DEFAULT_MESH_RSSI_THRESHOLD) { + params.conf.rssi_threshold = ssid->mesh_rssi_threshold; + params.conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD; + } if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) { params.flags |= WPA_DRIVER_MESH_FLAG_SAE_AUTH; @@ -422,7 +438,7 @@ int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s, } params.conf.peer_link_timeout = wpa_s->conf->mesh_max_inactivity; - if (wpa_supplicant_mesh_init(wpa_s, ssid)) { + if (wpa_supplicant_mesh_init(wpa_s, ssid, ¶ms.freq)) { wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh"); wpa_drv_leave_mesh(wpa_s); ret = -1; diff --git a/contrib/wpa/wpa_supplicant/mesh_mpm.c b/contrib/wpa/wpa_supplicant/mesh_mpm.c index d14c7e3b2045..eafb0af7b82a 100644 --- a/contrib/wpa/wpa_supplicant/mesh_mpm.c +++ b/contrib/wpa/wpa_supplicant/mesh_mpm.c @@ -11,6 +11,7 @@ #include "utils/common.h" #include "utils/eloop.h" #include "common/ieee802_11_defs.h" +#include "common/hw_features_common.h" #include "ap/hostapd.h" #include "ap/sta_info.h" #include "ap/ieee802_11.h" @@ -19,6 +20,7 @@ #include "driver_i.h" #include "mesh_mpm.h" #include "mesh_rsn.h" +#include "notify.h" struct mesh_peer_mgmt_ie { const u8 *proto_id; /* Mesh Peering Protocol Identifier (2 octets) */ @@ -220,13 +222,14 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, if (!sta) return; - buf_len = 2 + /* capability info */ + buf_len = 2 + /* Category and Action */ + 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 + 24 + /* peering management */ 2 + 96 + /* AMPE */ 2 + 16; /* MIC */ #ifdef CONFIG_IEEE80211N @@ -435,7 +438,7 @@ static void plink_timer(void *eloop_ctx, void *user_data) break; } reason = WLAN_REASON_MESH_MAX_RETRIES; - /* fall through on else */ + /* fall through */ case PLINK_CNF_RCVD: /* confirm timer */ @@ -646,6 +649,9 @@ static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s, struct mesh_conf *conf = wpa_s->ifmsh->mconf; struct hostapd_data *data = wpa_s->ifmsh->bss[0]; struct sta_info *sta; +#ifdef CONFIG_IEEE80211N + struct ieee80211_ht_operation *oper; +#endif /* CONFIG_IEEE80211N */ int ret; if (elems->mesh_config_len >= 7 && @@ -677,6 +683,17 @@ static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IEEE80211N copy_sta_ht_capab(data, sta, elems->ht_capabilities); + + oper = (struct ieee80211_ht_operation *) elems->ht_operation; + if (oper && + !(oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) && + sta->ht_capabilities) { + wpa_msg(wpa_s, MSG_DEBUG, MACSTR + " does not support 40 MHz bandwidth", + MAC2STR(sta->addr)); + set_disable_ht40(sta->ht_capabilities, 1); + } + update_ht_state(data, sta); #endif /* CONFIG_IEEE80211N */ @@ -842,6 +859,9 @@ static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s, /* Send ctrl event */ wpa_msg(wpa_s, MSG_INFO, MESH_PEER_CONNECTED MACSTR, MAC2STR(sta->addr)); + + /* Send D-Bus event */ + wpas_notify_mesh_peer_connected(wpa_s, sta->addr); } @@ -994,6 +1014,10 @@ static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta, wpa_msg(wpa_s, MSG_INFO, MESH_PEER_DISCONNECTED MACSTR, MAC2STR(sta->addr)); + /* Send D-Bus event */ + wpas_notify_mesh_peer_disconnected(wpa_s, sta->addr, + reason); + hapd->num_plinks--; mesh_mpm_send_plink_action(wpa_s, sta, @@ -1135,7 +1159,7 @@ void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s, */ if (!sta && action_field == PLINK_OPEN && (!(mconf->security & MESH_CONF_SEC_AMPE) || - wpa_auth_pmksa_get(hapd->wpa_auth, mgmt->sa))) + wpa_auth_pmksa_get(hapd->wpa_auth, mgmt->sa, NULL))) sta = mesh_mpm_add_peer(wpa_s, mgmt->sa, &elems); if (!sta) { diff --git a/contrib/wpa/wpa_supplicant/mesh_rsn.c b/contrib/wpa/wpa_supplicant/mesh_rsn.c index 27ab8cb36458..e74cb16b0725 100644 --- a/contrib/wpa/wpa_supplicant/mesh_rsn.c +++ b/contrib/wpa/wpa_supplicant/mesh_rsn.c @@ -75,12 +75,15 @@ 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 *p2p_dev_addr, const u8 *prev_psk) + const u8 *p2p_dev_addr, const u8 *prev_psk, + size_t *psk_len) { 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); + if (psk_len) + *psk_len = PMK_LEN; wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)", __func__, MAC2STR(addr), prev_psk); @@ -140,7 +143,12 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr, enum mfp_options ieee80211w) { struct wpa_auth_config conf; - struct wpa_auth_callbacks cb; + static const struct wpa_auth_callbacks cb = { + .logger = auth_logger, + .get_psk = auth_get_psk, + .set_key = auth_set_key, + .start_ampe = auth_start_ampe, + }; u8 seq[6] = {}; wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine"); @@ -153,20 +161,15 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr, conf.wpa_group = rsn->group_cipher; conf.eapol_version = 0; conf.wpa_group_rekey = -1; + conf.wpa_group_update_count = 4; + conf.wpa_pairwise_update_count = 4; #ifdef CONFIG_IEEE80211W conf.ieee80211w = ieee80211w; if (ieee80211w != NO_MGMT_FRAME_PROTECTION) conf.group_mgmt_cipher = rsn->mgmt_group_cipher; #endif /* CONFIG_IEEE80211W */ - 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); + rsn->auth = wpa_init(addr, &conf, &cb, rsn); if (rsn->auth == NULL) { wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed"); return -1; @@ -224,6 +227,9 @@ struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s, struct hostapd_data *bss = wpa_s->ifmsh->bss[0]; const u8 *ie; size_t ie_len; +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL + struct external_pmksa_cache *entry; +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ mesh_rsn = os_zalloc(sizeof(*mesh_rsn)); if (mesh_rsn == NULL) @@ -242,6 +248,22 @@ struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s, bss->wpa_auth = mesh_rsn->auth; +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL + while ((entry = dl_list_last(&wpa_s->mesh_external_pmksa_cache, + struct external_pmksa_cache, + list)) != NULL) { + int ret; + + ret = wpa_auth_pmksa_add_entry(bss->wpa_auth, + entry->pmksa_cache); + dl_list_del(&entry->list); + os_free(entry); + + if (ret < 0) + return NULL; + } +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ + ie = wpa_auth_get_wpa_ie(mesh_rsn->auth, &ie_len); conf->rsn_ie = (u8 *) ie; conf->rsn_ie_len = ie_len; @@ -295,7 +317,12 @@ static int mesh_rsn_build_sae_commit(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct sta_info *sta) { - if (ssid->passphrase == NULL) { + const char *password; + + password = ssid->sae_password; + if (!password) + password = ssid->passphrase; + if (!password) { wpa_msg(wpa_s, MSG_DEBUG, "SAE: No password available"); return -1; } @@ -305,9 +332,15 @@ static int mesh_rsn_build_sae_commit(struct wpa_supplicant *wpa_s, return -1; } + if (sta->sae->tmp && !sta->sae->tmp->pw_id && ssid->sae_password_id) { + sta->sae->tmp->pw_id = os_strdup(ssid->sae_password_id); + if (!sta->sae->tmp->pw_id) + return -1; + } return sae_prepare_commit(wpa_s->own_addr, sta->addr, - (u8 *) ssid->passphrase, - os_strlen(ssid->passphrase), sta->sae); + (u8 *) password, os_strlen(password), + ssid->sae_password_id, + sta->sae); } @@ -333,7 +366,7 @@ int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, return -1; } - pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr); + pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, NULL); if (pmksa) { if (!sta->wpa_sm) sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, @@ -579,7 +612,7 @@ skip_keys: /* encrypt after MIC */ mic_payload = wpabuf_put(buf, 2 + len + AES_BLOCK_SIZE); - if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + len, 3, + if (aes_siv_encrypt(sta->aek, sizeof(sta->aek), ampe_ie, 2 + len, 3, aad, aad_len, mic_payload)) { wpa_printf(MSG_ERROR, "protect frame: failed to encrypt"); ret = -ENOMEM; @@ -611,7 +644,7 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, if (!sta->sae) { struct hostapd_data *hapd = wpa_s->ifmsh->bss[0]; - if (!wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr)) { + if (!wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, NULL)) { wpa_printf(MSG_INFO, "Mesh RSN: SAE is not prepared yet"); return -1; @@ -650,7 +683,7 @@ int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta, os_memcpy(crypt, elems->mic, crypt_len); - if (aes_siv_decrypt(sta->aek, crypt, crypt_len, 3, + if (aes_siv_decrypt(sta->aek, sizeof(sta->aek), crypt, crypt_len, 3, aad, aad_len, ampe_buf)) { wpa_printf(MSG_ERROR, "Mesh RSN: frame verification failed!"); ret = -2; diff --git a/contrib/wpa/wpa_supplicant/notify.c b/contrib/wpa/wpa_supplicant/notify.c index 67e36ae34cb8..83df04f394c7 100644 --- a/contrib/wpa/wpa_supplicant/notify.c +++ b/contrib/wpa/wpa_supplicant/notify.c @@ -669,12 +669,12 @@ void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s, void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int persistent, - int client) + int client, const u8 *ip) { /* Notify a group has been started */ wpas_dbus_register_p2p_group(wpa_s, ssid); - wpas_dbus_signal_p2p_group_started(wpa_s, client, persistent); + wpas_dbus_signal_p2p_group_started(wpa_s, client, persistent, ip); } @@ -816,6 +816,12 @@ void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status, } +void wpas_notify_eap_error(struct wpa_supplicant *wpa_s, int error_code) +{ + wpa_msg(wpa_s, MSG_ERROR, WPA_EVENT_EAP_ERROR_CODE "%d", error_code); +} + + void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { @@ -850,3 +856,49 @@ void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_P2P */ } + + +#ifdef CONFIG_MESH + +void wpas_notify_mesh_group_started(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + if (wpa_s->p2p_mgmt) + return; + + wpas_dbus_signal_mesh_group_started(wpa_s, ssid); +} + + +void wpas_notify_mesh_group_removed(struct wpa_supplicant *wpa_s, + const u8 *meshid, u8 meshid_len, + int reason_code) +{ + if (wpa_s->p2p_mgmt) + return; + + wpas_dbus_signal_mesh_group_removed(wpa_s, meshid, meshid_len, + reason_code); +} + + +void wpas_notify_mesh_peer_connected(struct wpa_supplicant *wpa_s, + const u8 *peer_addr) +{ + if (wpa_s->p2p_mgmt) + return; + + wpas_dbus_signal_mesh_peer_connected(wpa_s, peer_addr); +} + + +void wpas_notify_mesh_peer_disconnected(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, int reason_code) +{ + if (wpa_s->p2p_mgmt) + return; + + wpas_dbus_signal_mesh_peer_disconnected(wpa_s, peer_addr, reason_code); +} + +#endif /* CONFIG_MESH */ diff --git a/contrib/wpa/wpa_supplicant/notify.h b/contrib/wpa/wpa_supplicant/notify.h index 8cce0f30c2a9..3ca933c7621a 100644 --- a/contrib/wpa/wpa_supplicant/notify.h +++ b/contrib/wpa/wpa_supplicant/notify.h @@ -114,7 +114,7 @@ void wpas_notify_p2p_provision_discovery(struct wpa_supplicant *wpa_s, unsigned int generated_pin); void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int persistent, - int client); + int client, const u8 *ip); void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s, const char *reason); void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s, @@ -134,6 +134,7 @@ void wpas_notify_preq(struct wpa_supplicant *wpa_s, 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_eap_error(struct wpa_supplicant *wpa_s, int error_code); 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, @@ -141,5 +142,14 @@ void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s, void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *go_dev_addr, const u8 *bssid, int id, int op_freq); +void wpas_notify_mesh_group_started(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); +void wpas_notify_mesh_group_removed(struct wpa_supplicant *wpa_s, + const u8 *meshid, u8 meshid_len, + int reason_code); +void wpas_notify_mesh_peer_connected(struct wpa_supplicant *wpa_s, + const u8 *peer_addr); +void wpas_notify_mesh_peer_disconnected(struct wpa_supplicant *wpa_s, + const u8 *peer_addr, int reason_code); #endif /* NOTIFY_H */ diff --git a/contrib/wpa/wpa_supplicant/offchannel.c b/contrib/wpa/wpa_supplicant/offchannel.c index 26d41a4ad5c6..b74be7dad4ac 100644 --- a/contrib/wpa/wpa_supplicant/offchannel.c +++ b/contrib/wpa/wpa_supplicant/offchannel.c @@ -310,6 +310,8 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq, iface = wpas_get_tx_interface(wpa_s, src); wpa_s->action_tx_wait_time = wait_time; + if (wait_time) + wpa_s->action_tx_wait_time_used = 1; ret = wpa_drv_send_action( iface, wpa_s->pending_action_freq, @@ -398,13 +400,14 @@ void offchannel_send_action_done(struct wpa_supplicant *wpa_s) 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_s->action_tx_wait_time || wpa_s->action_tx_wait_time_used)) wpa_drv_send_action_cancel_wait(wpa_s); 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; } + wpa_s->action_tx_wait_time_used = 0; } diff --git a/contrib/wpa/wpa_supplicant/op_classes.c b/contrib/wpa/wpa_supplicant/op_classes.c new file mode 100644 index 000000000000..d23b0094c440 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/op_classes.c @@ -0,0 +1,325 @@ +/* + * Operating classes + * Copyright(c) 2015 Intel Deutschland GmbH + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * 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 "common/ieee802_11_common.h" +#include "wpa_supplicant_i.h" + + +static enum chan_allowed allow_channel(struct hostapd_hw_modes *mode, u8 chan, + unsigned int *flags) +{ + int i; + + for (i = 0; i < mode->num_channels; i++) { + if (mode->channels[i].chan == chan) + break; + } + + if (i == mode->num_channels || + (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)) + return NOT_ALLOWED; + + if (flags) + *flags = mode->channels[i].flag; + + if (mode->channels[i].flag & HOSTAPD_CHAN_NO_IR) + return NO_IR; + + return ALLOWED; +} + + +static int get_center_80mhz(struct hostapd_hw_modes *mode, u8 channel) +{ + u8 center_channels[] = { 42, 58, 106, 122, 138, 155 }; + size_t i; + + if (mode->mode != HOSTAPD_MODE_IEEE80211A) + return 0; + + 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 verify_80mhz(struct hostapd_hw_modes *mode, u8 channel) +{ + u8 center_chan; + unsigned int i; + unsigned int no_ir = 0; + + center_chan = get_center_80mhz(mode, channel); + if (!center_chan) + return NOT_ALLOWED; + + /* check all the channels are available */ + for (i = 0; i < 4; i++) { + unsigned int flags; + u8 adj_chan = center_chan - 6 + i * 4; + + if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED) + return NOT_ALLOWED; + + if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) || + (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) || + (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) || + (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10))) + return NOT_ALLOWED; + + if (flags & HOSTAPD_CHAN_NO_IR) + no_ir = 1; + } + + if (no_ir) + return NO_IR; + + return ALLOWED; +} + + +static int get_center_160mhz(struct hostapd_hw_modes *mode, u8 channel) +{ + u8 center_channels[] = { 50, 114 }; + unsigned int i; + + if (mode->mode != HOSTAPD_MODE_IEEE80211A) + return 0; + + for (i = 0; i < ARRAY_SIZE(center_channels); i++) { + /* + * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64), + * so the center channel is 14 channels away from the start/end. + */ + if (channel >= center_channels[i] - 14 && + channel <= center_channels[i] + 14) + return center_channels[i]; + } + + return 0; +} + + +static enum chan_allowed verify_160mhz(struct hostapd_hw_modes *mode, + u8 channel) +{ + u8 center_chan; + unsigned int i; + unsigned int no_ir = 0; + + center_chan = get_center_160mhz(mode, channel); + if (!center_chan) + return NOT_ALLOWED; + + /* Check all the channels are available */ + for (i = 0; i < 8; i++) { + unsigned int flags; + u8 adj_chan = center_chan - 14 + i * 4; + + if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED) + return NOT_ALLOWED; + + if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) || + (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) || + (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) || + (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) || + (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) || + (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) || + (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) || + (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10))) + return NOT_ALLOWED; + + if (flags & HOSTAPD_CHAN_NO_IR) + no_ir = 1; + } + + if (no_ir) + return NO_IR; + + return ALLOWED; +} + + +enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 channel, + u8 bw) +{ + unsigned int flag = 0; + enum chan_allowed res, res2; + + res2 = res = allow_channel(mode, channel, &flag); + if (bw == BW40MINUS) { + if (!(flag & HOSTAPD_CHAN_HT40MINUS)) + return NOT_ALLOWED; + res2 = allow_channel(mode, channel - 4, NULL); + } else if (bw == BW40PLUS) { + if (!(flag & HOSTAPD_CHAN_HT40PLUS)) + return NOT_ALLOWED; + res2 = allow_channel(mode, channel + 4, NULL); + } else if (bw == BW80) { + /* + * channel is a center channel and as such, not necessarily a + * valid 20 MHz channels. Override earlier allow_channel() + * result and use only the 80 MHz specific version. + */ + res2 = res = verify_80mhz(mode, channel); + } else if (bw == BW160) { + /* + * channel is a center channel and as such, not necessarily a + * valid 20 MHz channels. Override earlier allow_channel() + * result and use only the 160 MHz specific version. + */ + res2 = res = verify_160mhz(mode, channel); + } else if (bw == BW80P80) { + /* + * channel is a center channel and as such, not necessarily a + * valid 20 MHz channels. Override earlier allow_channel() + * result and use only the 80 MHz specific version. + */ + res2 = res = verify_80mhz(mode, channel); + } + + if (res == NOT_ALLOWED || res2 == NOT_ALLOWED) + return NOT_ALLOWED; + + if (res == NO_IR || res2 == NO_IR) + return NO_IR; + + return ALLOWED; +} + + +static int wpas_op_class_supported(struct wpa_supplicant *wpa_s, + const struct oper_class_map *op_class) +{ + int chan; + size_t i; + struct hostapd_hw_modes *mode; + int found; + + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode); + if (!mode) + return 0; + + if (op_class->op_class == 128) { + u8 channels[] = { 42, 58, 106, 122, 138, 155 }; + + for (i = 0; i < ARRAY_SIZE(channels); i++) { + if (verify_channel(mode, channels[i], op_class->bw) != + NOT_ALLOWED) + return 1; + } + + return 0; + } + + if (op_class->op_class == 129) { + /* Check if either 160 MHz channels is allowed */ + return verify_channel(mode, 50, op_class->bw) != NOT_ALLOWED || + verify_channel(mode, 114, op_class->bw) != NOT_ALLOWED; + } + + if (op_class->op_class == 130) { + /* Need at least two non-contiguous 80 MHz segments */ + found = 0; + + if (verify_channel(mode, 42, op_class->bw) != NOT_ALLOWED || + verify_channel(mode, 58, op_class->bw) != NOT_ALLOWED) + found++; + if (verify_channel(mode, 106, op_class->bw) != NOT_ALLOWED || + verify_channel(mode, 122, op_class->bw) != NOT_ALLOWED || + verify_channel(mode, 138, op_class->bw) != NOT_ALLOWED) + found++; + if (verify_channel(mode, 106, op_class->bw) != NOT_ALLOWED && + verify_channel(mode, 138, op_class->bw) != NOT_ALLOWED) + found++; + if (verify_channel(mode, 155, op_class->bw) != NOT_ALLOWED) + found++; + + if (found >= 2) + return 1; + + return 0; + } + + found = 0; + for (chan = op_class->min_chan; chan <= op_class->max_chan; + chan += op_class->inc) { + if (verify_channel(mode, chan, op_class->bw) != NOT_ALLOWED) { + found = 1; + break; + } + } + + return found; +} + + +size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos, + size_t len) +{ + struct wpabuf *buf; + u8 op, current, chan; + u8 *ie_len; + size_t res; + + /* + * Assume 20 MHz channel for now. + * TODO: Use the secondary channel and VHT channel width that will be + * used after association. + */ + if (ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT, + ¤t, &chan) == NUM_HOSTAPD_MODES) + return 0; + + /* + * Need 3 bytes for EID, length, and current operating class, plus + * 1 byte for every other supported operating class. + */ + buf = wpabuf_alloc(global_op_class_size + 3); + if (!buf) + return 0; + + wpabuf_put_u8(buf, WLAN_EID_SUPPORTED_OPERATING_CLASSES); + /* Will set the length later, putting a placeholder */ + ie_len = wpabuf_put(buf, 1); + wpabuf_put_u8(buf, current); + + for (op = 0; global_op_class[op].op_class; op++) { + if (wpas_op_class_supported(wpa_s, &global_op_class[op])) + wpabuf_put_u8(buf, global_op_class[op].op_class); + } + + *ie_len = wpabuf_len(buf) - 2; + if (*ie_len < 2 || wpabuf_len(buf) > len) { + wpa_printf(MSG_ERROR, + "Failed to add supported operating classes IE"); + res = 0; + } else { + os_memcpy(pos, wpabuf_head(buf), wpabuf_len(buf)); + res = wpabuf_len(buf); + wpa_hexdump_buf(MSG_DEBUG, + "Added supported operating classes IE", buf); + } + + wpabuf_free(buf); + return res; +} diff --git a/contrib/wpa/wpa_supplicant/p2p_supplicant.c b/contrib/wpa/wpa_supplicant/p2p_supplicant.c index b1fdc2837ff0..c596d5ab6148 100644 --- a/contrib/wpa/wpa_supplicant/p2p_supplicant.c +++ b/contrib/wpa/wpa_supplicant/p2p_supplicant.c @@ -307,7 +307,14 @@ static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit) 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); + if (ret == 0) + wpa_s->curr_scan_cookie = params->scan_cookie; wpa_scan_free_params(params); work->ctx = NULL; if (ret) { @@ -320,6 +327,7 @@ static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit) 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->clear_driver_scan_cache = 0; wpa_s->p2p_scan_work = work; } @@ -740,6 +748,7 @@ static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role, conncap = P2PS_SETUP_GROUP_OWNER; goto grp_owner; } + /* fall through */ default: return P2PS_SETUP_NONE; @@ -807,7 +816,7 @@ grp_owner: wpa_s->own_addr); } else if (!s && !go_wpa_s) { if (wpas_p2p_add_group_interface(wpa_s, - WPA_IF_P2P_GO) < 0) { + WPA_IF_P2P_GROUP) < 0) { wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new interface for the group"); return P2PS_SETUP_NONE; @@ -1312,6 +1321,10 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, if (wpa_s->p2p_go_group_formation_completed) { wpa_s->global->p2p_group_formation = NULL; wpa_s->p2p_in_provisioning = 0; + } else if (wpa_s->p2p_in_provisioning && !success) { + wpa_msg(wpa_s, MSG_DEBUG, + "P2P: Stop provisioning state due to failure"); + wpa_s->p2p_in_provisioning = 0; } wpa_s->p2p_in_invitation = 0; wpa_s->group_formation_reported = 1; @@ -1383,7 +1396,7 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, } if (!client) { - wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 0); + wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 0, NULL); os_get_reltime(&wpa_s->global->p2p_go_wait_client); } } @@ -1701,14 +1714,23 @@ static void wpas_p2p_add_psk_list(struct wpa_supplicant *wpa_s, static void p2p_go_dump_common_freqs(struct wpa_supplicant *wpa_s) { + char buf[20 + P2P_MAX_CHANNELS * 6]; + char *pos, *end; unsigned int i; + int res; - wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Common group frequencies (len=%u):", - wpa_s->p2p_group_common_freqs_num); + pos = buf; + end = pos + sizeof(buf); + for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) { + res = os_snprintf(pos, end - pos, " %d", + wpa_s->p2p_group_common_freqs[i]); + if (os_snprintf_error(end - pos, res)) + break; + pos += res; + } + *pos = '\0'; - 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]); + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Common group frequencies:%s", buf); } @@ -1801,7 +1823,8 @@ static void p2p_go_configured(void *ctx, void *data) } wpas_notify_p2p_group_started(wpa_s, ssid, - params->persistent_group, 0); + params->persistent_group, 0, + NULL); wpas_p2p_cross_connect_setup(wpa_s); wpas_p2p_set_group_idle_timeout(wpa_s); @@ -1989,6 +2012,11 @@ do { \ d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey); } d->p2p_cli_probe = s->p2p_cli_probe; + d->go_interworking = s->go_interworking; + d->go_access_network_type = s->go_access_network_type; + d->go_internet = s->go_internet; + d->go_venue_group = s->go_venue_group; + d->go_venue_type = s->go_venue_type; } @@ -3331,10 +3359,6 @@ static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s, } -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) { @@ -5003,6 +5027,12 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, params.extra_ies = wpabuf_head(ies); params.extra_ies_len = wpabuf_len(ies); + 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; + } + /* * Run a scan to update BSS table and start Provision Discovery once * the new scan results become available. @@ -5012,6 +5042,7 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq, os_get_reltime(&wpa_s->scan_trigger_time); wpa_s->scan_res_handler = wpas_p2p_scan_res_join; wpa_s->own_scan_requested = 1; + wpa_s->clear_driver_scan_cache = 0; } wpabuf_free(ies); @@ -5183,7 +5214,8 @@ static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq, 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)) { + ieee80211_is_dfs(freq, wpa_s->hw.modes, + wpa_s->hw.num_modes)) { /* * If freq is a DFS channel and DFS is offloaded * to the driver, allow P2P GO to use it. @@ -5236,9 +5268,11 @@ static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq, if (!res && max_pref_freq > 0) { *num_pref_freq = max_pref_freq; i = 0; - while (wpas_p2p_disallowed_freq(wpa_s->global, - pref_freq_list[i]) && - i < *num_pref_freq) { + while (i < *num_pref_freq && + (!p2p_supported_freq(wpa_s->global->p2p, + pref_freq_list[i]) || + wpas_p2p_disallowed_freq(wpa_s->global, + pref_freq_list[i]))) { wpa_printf(MSG_DEBUG, "P2P: preferred_freq_list[%d]=%d is disallowed", i, pref_freq_list[i]); @@ -5601,9 +5635,11 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq) &size, pref_freq_list); if (!res && size > 0) { i = 0; - while (wpas_p2p_disallowed_freq(wpa_s->global, - pref_freq_list[i]) && - i < size) { + while (i < size && + (!p2p_supported_freq(wpa_s->global->p2p, + pref_freq_list[i]) || + wpas_p2p_disallowed_freq(wpa_s->global, + pref_freq_list[i]))) { wpa_printf(MSG_DEBUG, "P2P: preferred_freq_list[%d]=%d is disallowed", i, pref_freq_list[i]); @@ -5667,7 +5703,8 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int 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)) { + ieee80211_is_dfs(freq, wpa_s->hw.modes, + wpa_s->hw.num_modes)) { /* * If freq is a DFS channel and DFS is offloaded to the * driver, allow P2P GO to use it. @@ -5705,30 +5742,6 @@ static void wpas_p2p_select_go_freq_no_pref(struct wpa_supplicant *wpa_s, { unsigned int i, r; - /* first try some random selection of the social channels */ - if (os_get_random((u8 *) &r, sizeof(r)) < 0) - return; - - for (i = 0; i < 3; i++) { - params->freq = 2412 + ((r + i) % 3) * 25; - if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq)) - goto out; - } - - /* try all other channels in operating class 81 */ - for (i = 0; i < 11; i++) { - params->freq = 2412 + i * 5; - - /* skip social channels; covered in the previous loop */ - if (params->freq == 2412 || - params->freq == 2437 || - params->freq == 2462) - continue; - - if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq)) - goto out; - } - /* try all channels in operating class 115 */ for (i = 0; i < 4; i++) { params->freq = 5180 + i * 20; @@ -5763,6 +5776,30 @@ static void wpas_p2p_select_go_freq_no_pref(struct wpa_supplicant *wpa_s, goto out; } + /* try some random selection of the social channels */ + if (os_get_random((u8 *) &r, sizeof(r)) < 0) + return; + + for (i = 0; i < 3; i++) { + params->freq = 2412 + ((r + i) % 3) * 25; + if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq)) + goto out; + } + + /* try all other channels in operating class 81 */ + for (i = 0; i < 11; i++) { + params->freq = 2412 + i * 5; + + /* skip social channels; covered in the previous loop */ + if (params->freq == 2412 || + params->freq == 2437 || + params->freq == 2462) + continue; + + if (wpas_p2p_supported_freq_go(wpa_s, channels, params->freq)) + goto out; + } + params->freq = 0; wpa_printf(MSG_DEBUG, "P2P: No 2.4, 5, or 60 GHz channel allowed"); return; @@ -5772,6 +5809,19 @@ out: } +static int wpas_same_band(int freq1, int freq2) +{ + enum hostapd_hw_mode mode1, mode2; + u8 chan1, chan2; + + mode1 = ieee80211_freq_to_chan(freq1, &chan1); + mode2 = ieee80211_freq_to_chan(freq2, &chan2); + if (mode1 == NUM_HOSTAPD_MODES) + return 0; + return mode1 == mode2; +} + + static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, struct p2p_go_neg_results *params, int freq, int vht_center_freq2, int ht40, @@ -5822,12 +5872,31 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, /* try using the forced freq */ if (freq) { - if (!wpas_p2p_supported_freq_go(wpa_s, channels, freq)) { + if (wpas_p2p_disallowed_freq(wpa_s->global, freq) || + !freq_included(wpa_s, channels, freq)) { wpa_printf(MSG_DEBUG, - "P2P: Forced GO freq %d MHz not accepted", + "P2P: Forced GO freq %d MHz disallowed", freq); goto fail; } + if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) { + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) && + ieee80211_is_dfs(freq, wpa_s->hw.modes, + wpa_s->hw.num_modes)) { + /* + * 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) requires DFS and DFS is offloaded", + __func__, freq); + } else { + wpa_printf(MSG_DEBUG, + "P2P: The forced channel for GO (%u MHz) is not supported for P2P uses", + freq); + goto fail; + } + } for (i = 0; i < num; i++) { if (freqs[i].freq == freq) { @@ -5953,6 +6022,80 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s, goto success; } + /* Try using a channel that allows VHT to be used with 80 MHz */ + if (wpa_s->hw.modes && wpa_s->p2p_group_common_freqs) { + for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) { + enum hostapd_hw_mode mode; + struct hostapd_hw_modes *hwmode; + u8 chan; + + cand = wpa_s->p2p_group_common_freqs[i]; + mode = ieee80211_freq_to_chan(cand, &chan); + hwmode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, + mode); + if (!hwmode || + wpas_p2p_verify_channel(wpa_s, hwmode, chan, + BW80) != ALLOWED) + continue; + if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) { + params->freq = cand; + wpa_printf(MSG_DEBUG, + "P2P: Use freq %d MHz common with the peer and allowing VHT80", + params->freq); + goto success; + } + } + } + + /* Try using a channel that allows HT to be used with 40 MHz on the same + * band so that CSA can be used */ + if (wpa_s->current_ssid && wpa_s->hw.modes && + wpa_s->p2p_group_common_freqs) { + for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) { + enum hostapd_hw_mode mode; + struct hostapd_hw_modes *hwmode; + u8 chan; + + cand = wpa_s->p2p_group_common_freqs[i]; + mode = ieee80211_freq_to_chan(cand, &chan); + hwmode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, + mode); + if (!wpas_same_band(wpa_s->current_ssid->frequency, + cand) || + !hwmode || + (wpas_p2p_verify_channel(wpa_s, hwmode, chan, + BW40MINUS) != ALLOWED && + wpas_p2p_verify_channel(wpa_s, hwmode, chan, + BW40PLUS) != ALLOWED)) + continue; + if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) { + params->freq = cand; + wpa_printf(MSG_DEBUG, + "P2P: Use freq %d MHz common with the peer, allowing HT40, and maintaining same band", + params->freq); + goto success; + } + } + } + + /* Try using one of the group common freqs on the same band so that CSA + * can be used */ + if (wpa_s->current_ssid && wpa_s->p2p_group_common_freqs) { + for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) { + cand = wpa_s->p2p_group_common_freqs[i]; + if (!wpas_same_band(wpa_s->current_ssid->frequency, + cand)) + continue; + if (wpas_p2p_supported_freq_go(wpa_s, channels, cand)) { + params->freq = cand; + wpa_printf(MSG_DEBUG, + "P2P: Use freq %d MHz common with the peer and maintaining same band", + params->freq); + goto success; + } + } + } + /* Try using one of the group common freqs */ if (wpa_s->p2p_group_common_freqs) { for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++) { @@ -6022,6 +6165,12 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated, return NULL; } + if (go && wpa_s->p2p_go_do_acs) { + group_wpa_s->p2p_go_do_acs = wpa_s->p2p_go_do_acs; + group_wpa_s->p2p_go_acs_band = wpa_s->p2p_go_acs_band; + wpa_s->p2p_go_do_acs = 0; + } + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use separate group interface %s", group_wpa_s->ifname); group_wpa_s->p2p_first_connection_timeout = 0; @@ -6059,31 +6208,16 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, wpa_printf(MSG_DEBUG, "P2P: Stop any on-going P2P FIND"); wpas_p2p_stop_find_oper(wpa_s); - freq = wpas_p2p_select_go_freq(wpa_s, freq); - if (freq < 0) - return -1; + if (!wpa_s->p2p_go_do_acs) { + freq = wpas_p2p_select_go_freq(wpa_s, freq); + if (freq < 0) + return -1; + } if (wpas_p2p_init_go_params(wpa_s, ¶ms, freq, vht_center_freq2, ht40, vht, max_oper_chwidth, NULL)) return -1; - if (params.freq && - !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; @@ -6559,8 +6693,14 @@ int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout, wpa_s->p2p_long_listen = 0; if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL || - wpa_s->p2p_in_provisioning) + wpa_s->p2p_in_provisioning) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Reject p2p_find operation%s%s", + (wpa_s->global->p2p_disabled || !wpa_s->global->p2p) ? + " (P2P disabled)" : "", + wpa_s->p2p_in_provisioning ? + " (p2p_in_provisioning)" : ""); return -1; + } wpa_supplicant_cancel_sched_scan(wpa_s); @@ -7005,7 +7145,7 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s) wpas_p2p_store_persistent_group(wpa_s->p2pdev, ssid, go_dev_addr); - wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 1); + wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 1, ip); } @@ -9041,16 +9181,20 @@ static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s, unsigned int i, invalid_freq = 0, policy_move = 0, flags = 0; unsigned int timeout; int freq; + int dfs_offload; wpas_p2p_go_update_common_freqs(wpa_s); freq = wpa_s->current_ssid->frequency; + dfs_offload = (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) && + ieee80211_is_dfs(freq, wpa_s->hw.modes, wpa_s->hw.num_modes); for (i = 0, invalid_freq = 0; i < num; i++) { if (freqs[i].freq == freq) { flags = freqs[i].flags; /* The channel is invalid, must change it */ - if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) { + if (!p2p_supported_freq_go(wpa_s->global->p2p, freq) && + !dfs_offload) { wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Freq=%d MHz no longer valid for GO", freq); @@ -9060,7 +9204,7 @@ static void wpas_p2p_consider_moving_one_go(struct wpa_supplicant *wpa_s, /* Freq is not used by any other station interface */ continue; } else if (!p2p_supported_freq(wpa_s->global->p2p, - freqs[i].freq)) { + freqs[i].freq) && !dfs_offload) { /* Freq is not valid for P2P use cases */ continue; } else if (wpa_s->conf->p2p_go_freq_change_policy == diff --git a/contrib/wpa/wpa_supplicant/preauth_test.c b/contrib/wpa/wpa_supplicant/preauth_test.c index f4bba98e2a82..f2fff550aa81 100644 --- a/contrib/wpa/wpa_supplicant/preauth_test.c +++ b/contrib/wpa/wpa_supplicant/preauth_test.c @@ -143,16 +143,19 @@ static int wpa_supplicant_mlme_setprotection(void *wpa_s, const u8 *addr, } -static int wpa_supplicant_add_pmkid(void *wpa_s, - const u8 *bssid, const u8 *pmkid) +static int wpa_supplicant_add_pmkid(void *wpa_s, void *network_ctx, + const u8 *bssid, const u8 *pmkid, + const u8 *fils_cache_id, + const u8 *pmk, size_t pmk_len) { printf("%s - not implemented\n", __func__); return -1; } -static int wpa_supplicant_remove_pmkid(void *wpa_s, - const u8 *bssid, const u8 *pmkid) +static int wpa_supplicant_remove_pmkid(void *wpa_s, void *network_ctx, + const u8 *bssid, const u8 *pmkid, + const u8 *fils_cache_id) { printf("%s - not implemented\n", __func__); return -1; @@ -344,8 +347,8 @@ int main(int argc, char *argv[]) if (preauth_test.auth_timed_out) ret = -2; else { - ret = pmksa_cache_set_current(wpa_s.wpa, NULL, bssid, NULL, 0) - ? 0 : -3; + ret = pmksa_cache_set_current(wpa_s.wpa, NULL, bssid, NULL, 0, + NULL, 0) ? 0 : -3; } test_eapol_clean(&wpa_s); diff --git a/contrib/wpa/wpa_supplicant/rrm.c b/contrib/wpa/wpa_supplicant/rrm.c new file mode 100644 index 000000000000..f4fbfa719352 --- /dev/null +++ b/contrib/wpa/wpa_supplicant/rrm.c @@ -0,0 +1,1460 @@ +/* + * wpa_supplicant - Radio Measurements + * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> + * + * 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/eloop.h" +#include "common/ieee802_11_common.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "bss.h" +#include "scan.h" +#include "p2p_supplicant.h" + + +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_clear_beacon_rep_data(wpa_s); +} + + +/* + * 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) { + wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, 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 + +/* Measurement Request element + Location Subject + Maximum Age subelement */ +#define MEASURE_REQUEST_LCI_LEN (3 + 1 + 4) +/* Measurement Request element + Location Civic Request */ +#define MEASURE_REQUEST_CIVIC_LEN (3 + 5) + + +/** + * 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. + * @lci: if set, neighbor request will include LCI request + * @civic: if set, neighbor request will include civic location 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_value *ssid, + int lci, int civic, + 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; + } + + /* 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) + + (lci ? 2 + MEASURE_REQUEST_LCI_LEN : 0) + + (civic ? 2 + MEASURE_REQUEST_CIVIC_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); + } + + if (lci) { + /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */ + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + wpabuf_put_u8(buf, MEASURE_REQUEST_LCI_LEN); + + /* + * Measurement token; nonzero number that is unique among the + * Measurement Request elements in a particular frame. + */ + wpabuf_put_u8(buf, 1); /* Measurement Token */ + + /* + * Parallel, Enable, Request, and Report bits are 0, Duration is + * reserved. + */ + wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ + wpabuf_put_u8(buf, MEASURE_TYPE_LCI); /* Measurement Type */ + + /* IEEE P802.11-REVmc/D5.0 9.4.2.21.10 - LCI request */ + /* Location Subject */ + wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); + + /* Optional Subelements */ + /* + * IEEE P802.11-REVmc/D5.0 Figure 9-170 + * The Maximum Age subelement is required, otherwise the AP can + * send only data that was determined after receiving the + * request. Setting it here to unlimited age. + */ + wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE); + wpabuf_put_u8(buf, 2); + wpabuf_put_le16(buf, 0xffff); + } + + if (civic) { + /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */ + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + wpabuf_put_u8(buf, MEASURE_REQUEST_CIVIC_LEN); + + /* + * Measurement token; nonzero number that is unique among the + * Measurement Request elements in a particular frame. + */ + wpabuf_put_u8(buf, 2); /* Measurement Token */ + + /* + * Parallel, Enable, Request, and Report bits are 0, Duration is + * reserved. + */ + wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ + /* Measurement Type */ + wpabuf_put_u8(buf, MEASURE_TYPE_LOCATION_CIVIC); + + /* IEEE P802.11-REVmc/D5.0 9.4.2.21.14: + * Location Civic request */ + /* Location Subject */ + wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); + wpabuf_put_u8(buf, 0); /* Civic Location Type: IETF RFC 4776 */ + /* Location Service Interval Units: Seconds */ + wpabuf_put_u8(buf, 0); + /* Location Service Interval: 0 - Only one report is requested + */ + wpabuf_put_le16(buf, 0); + /* No optional subelements */ + } + + 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; +} + + +static int wpas_rrm_report_elem(struct wpabuf **buf, u8 token, u8 mode, u8 type, + const u8 *data, size_t data_len) +{ + if (wpabuf_resize(buf, 5 + data_len)) + return -1; + + wpabuf_put_u8(*buf, WLAN_EID_MEASURE_REPORT); + wpabuf_put_u8(*buf, 3 + data_len); + wpabuf_put_u8(*buf, token); + wpabuf_put_u8(*buf, mode); + wpabuf_put_u8(*buf, type); + + if (data_len) + wpabuf_put_data(*buf, data, data_len); + + return 0; +} + + +static int +wpas_rrm_build_lci_report(struct wpa_supplicant *wpa_s, + const struct rrm_measurement_request_element *req, + struct wpabuf **buf) +{ + u8 subject; + u16 max_age = 0; + struct os_reltime t, diff; + unsigned long diff_l; + const u8 *subelem; + const u8 *request = req->variable; + size_t len = req->len - 3; + + if (len < 1) + return -1; + + if (!wpa_s->lci) + goto reject; + + subject = *request++; + len--; + + wpa_printf(MSG_DEBUG, "Measurement request location subject=%u", + subject); + + if (subject != LOCATION_SUBJECT_REMOTE) { + wpa_printf(MSG_INFO, + "Not building LCI report - bad location subject"); + return 0; + } + + /* Subelements are formatted exactly like elements */ + wpa_hexdump(MSG_DEBUG, "LCI request subelements", request, len); + subelem = get_ie(request, len, LCI_REQ_SUBELEM_MAX_AGE); + if (subelem && subelem[1] == 2) + max_age = WPA_GET_LE16(subelem + 2); + + if (os_get_reltime(&t)) + goto reject; + + os_reltime_sub(&t, &wpa_s->lci_time, &diff); + /* LCI age is calculated in 10th of a second units. */ + diff_l = diff.sec * 10 + diff.usec / 100000; + + if (max_age != 0xffff && max_age < diff_l) + goto reject; + + if (wpas_rrm_report_elem(buf, req->token, + MEASUREMENT_REPORT_MODE_ACCEPT, req->type, + wpabuf_head_u8(wpa_s->lci), + wpabuf_len(wpa_s->lci)) < 0) { + wpa_printf(MSG_DEBUG, "Failed to add LCI report element"); + return -1; + } + + return 0; + +reject: + if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr) && + wpas_rrm_report_elem(buf, req->token, + MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE, + req->type, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "RRM: Failed to add report element"); + return -1; + } + + return 0; +} + + +static void wpas_rrm_send_msr_report_mpdu(struct wpa_supplicant *wpa_s, + const u8 *data, size_t len) +{ + struct wpabuf *report = wpabuf_alloc(len + 3); + + if (!report) + return; + + wpabuf_put_u8(report, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(report, WLAN_RRM_RADIO_MEASUREMENT_REPORT); + wpabuf_put_u8(report, wpa_s->rrm.token); + + wpabuf_put_data(report, data, len); + + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(report), wpabuf_len(report), 0)) { + wpa_printf(MSG_ERROR, + "RRM: Radio measurement report failed: Sending Action frame failed"); + } + + wpabuf_free(report); +} + + +static void wpas_rrm_send_msr_report(struct wpa_supplicant *wpa_s, + struct wpabuf *buf) +{ + int len = wpabuf_len(buf); + const u8 *pos = wpabuf_head_u8(buf), *next = pos; + +#define MPDU_REPORT_LEN (int) (IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN - 3) + + while (len) { + int send_len = (len > MPDU_REPORT_LEN) ? next - pos : len; + + if (send_len == len || + (send_len + next[1] + 2) > MPDU_REPORT_LEN) { + wpas_rrm_send_msr_report_mpdu(wpa_s, pos, send_len); + len -= send_len; + pos = next; + } + + if (len) + next += next[1] + 2; + } +#undef MPDU_REPORT_LEN +} + + +static int wpas_add_channel(u8 op_class, u8 chan, u8 num_primary_channels, + int *freqs) +{ + size_t i; + + for (i = 0; i < num_primary_channels; i++) { + u8 primary_chan = chan - (2 * num_primary_channels - 2) + i * 4; + + freqs[i] = ieee80211_chan_to_freq(NULL, op_class, primary_chan); + /* ieee80211_chan_to_freq() is not really meant for this + * conversion of 20 MHz primary channel numbers for wider VHT + * channels, so handle those as special cases here for now. */ + if (freqs[i] < 0 && + (op_class == 128 || op_class == 129 || op_class == 130)) + freqs[i] = 5000 + 5 * primary_chan; + if (freqs[i] < 0) { + wpa_printf(MSG_DEBUG, + "Beacon Report: Invalid channel %u", + chan); + return -1; + } + } + + return 0; +} + + +static int * wpas_add_channels(const struct oper_class_map *op, + struct hostapd_hw_modes *mode, int active, + const u8 *channels, const u8 size) +{ + int *freqs, *next_freq; + u8 num_primary_channels, i; + u8 num_chans; + + num_chans = channels ? size : + (op->max_chan - op->min_chan) / op->inc + 1; + + if (op->bw == BW80 || op->bw == BW80P80) + num_primary_channels = 4; + else if (op->bw == BW160) + num_primary_channels = 8; + else + num_primary_channels = 1; + + /* one extra place for the zero-terminator */ + freqs = os_calloc(num_chans * num_primary_channels + 1, sizeof(*freqs)); + if (!freqs) { + wpa_printf(MSG_ERROR, + "Beacon Report: Failed to allocate freqs array"); + return NULL; + } + + next_freq = freqs; + for (i = 0; i < num_chans; i++) { + u8 chan = channels ? channels[i] : op->min_chan + i * op->inc; + enum chan_allowed res = verify_channel(mode, chan, op->bw); + + if (res == NOT_ALLOWED || (res == NO_IR && active)) + continue; + + if (wpas_add_channel(op->op_class, chan, num_primary_channels, + next_freq) < 0) { + os_free(freqs); + return NULL; + } + + next_freq += num_primary_channels; + } + + if (!freqs[0]) { + os_free(freqs); + return NULL; + } + + return freqs; +} + + +static int * wpas_op_class_freqs(const struct oper_class_map *op, + struct hostapd_hw_modes *mode, int active) +{ + u8 channels_80mhz[] = { 42, 58, 106, 122, 138, 155 }; + u8 channels_160mhz[] = { 50, 114 }; + + /* + * When adding all channels in the operating class, 80 + 80 MHz + * operating classes are like 80 MHz channels because we add all valid + * channels anyway. + */ + if (op->bw == BW80 || op->bw == BW80P80) + return wpas_add_channels(op, mode, active, channels_80mhz, + ARRAY_SIZE(channels_80mhz)); + + if (op->bw == BW160) + return wpas_add_channels(op, mode, active, channels_160mhz, + ARRAY_SIZE(channels_160mhz)); + + return wpas_add_channels(op, mode, active, NULL, 0); +} + + +static int * wpas_channel_report_freqs(struct wpa_supplicant *wpa_s, int active, + const char *country, const u8 *subelems, + size_t len) +{ + int *freqs = NULL, *new_freqs; + const u8 *end = subelems + len; + + while (end - subelems > 2) { + const struct oper_class_map *op; + const u8 *ap_chan_elem, *pos; + u8 left; + struct hostapd_hw_modes *mode; + + ap_chan_elem = get_ie(subelems, end - subelems, + WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL); + if (!ap_chan_elem) + break; + pos = ap_chan_elem + 2; + left = ap_chan_elem[1]; + if (left < 1) + break; + subelems = ap_chan_elem + 2 + left; + + op = get_oper_class(country, *pos); + if (!op) { + wpa_printf(MSG_DEBUG, + "Beacon request: unknown operating class in AP Channel Report subelement %u", + *pos); + goto out; + } + pos++; + left--; + + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op->mode); + if (!mode) + continue; + + /* + * For 80 + 80 MHz operating classes, this AP Channel Report + * element should be followed by another element specifying + * the second 80 MHz channel. For now just add this 80 MHz + * channel, the second 80 MHz channel will be added when the + * next element is parsed. + * TODO: Verify that this AP Channel Report element is followed + * by a corresponding AP Channel Report element as specified in + * IEEE Std 802.11-2016, 11.11.9.1. + */ + new_freqs = wpas_add_channels(op, mode, active, pos, left); + if (new_freqs) + int_array_concat(&freqs, new_freqs); + + os_free(new_freqs); + } + + return freqs; +out: + os_free(freqs); + return NULL; +} + + +static int * wpas_beacon_request_freqs(struct wpa_supplicant *wpa_s, + u8 op_class, u8 chan, int active, + const u8 *subelems, size_t len) +{ + int *freqs = NULL, *ext_freqs = NULL; + struct hostapd_hw_modes *mode; + const char *country = NULL; + const struct oper_class_map *op; + const u8 *elem; + + if (!wpa_s->current_bss) + return NULL; + elem = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_COUNTRY); + if (elem && elem[1] >= 2) + country = (const char *) (elem + 2); + + op = get_oper_class(country, op_class); + if (!op) { + wpa_printf(MSG_DEBUG, + "Beacon request: invalid operating class %d", + op_class); + return NULL; + } + + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op->mode); + if (!mode) + return NULL; + + switch (chan) { + case 0: + freqs = wpas_op_class_freqs(op, mode, active); + if (!freqs) + return NULL; + break; + case 255: + /* freqs will be added from AP channel subelements */ + break; + default: + freqs = wpas_add_channels(op, mode, active, &chan, 1); + if (!freqs) + return NULL; + break; + } + + ext_freqs = wpas_channel_report_freqs(wpa_s, active, country, subelems, + len); + if (ext_freqs) { + int_array_concat(&freqs, ext_freqs); + os_free(ext_freqs); + int_array_sort_unique(freqs); + } + + return freqs; +} + + +static int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len, + u8 *op_class, u8 *chan, u8 *phy_type) +{ + const u8 *ie; + int sec_chan = 0, vht = 0; + struct ieee80211_ht_operation *ht_oper = NULL; + struct ieee80211_vht_operation *vht_oper = NULL; + u8 seg0, seg1; + + ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION); + if (ie && ie[1] >= sizeof(struct ieee80211_ht_operation)) { + u8 sec_chan_offset; + + ht_oper = (struct ieee80211_ht_operation *) (ie + 2); + sec_chan_offset = ht_oper->ht_param & + HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; + if (sec_chan_offset == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) + sec_chan = 1; + else if (sec_chan_offset == + HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) + sec_chan = -1; + } + + ie = get_ie(ies, ies_len, WLAN_EID_VHT_OPERATION); + if (ie && ie[1] >= sizeof(struct ieee80211_vht_operation)) { + vht_oper = (struct ieee80211_vht_operation *) (ie + 2); + + switch (vht_oper->vht_op_info_chwidth) { + case 1: + seg0 = vht_oper->vht_op_info_chan_center_freq_seg0_idx; + seg1 = vht_oper->vht_op_info_chan_center_freq_seg1_idx; + if (seg1 && abs(seg1 - seg0) == 8) + vht = VHT_CHANWIDTH_160MHZ; + else if (seg1) + vht = VHT_CHANWIDTH_80P80MHZ; + else + vht = VHT_CHANWIDTH_80MHZ; + break; + case 2: + vht = VHT_CHANWIDTH_160MHZ; + break; + case 3: + vht = VHT_CHANWIDTH_80P80MHZ; + break; + default: + vht = VHT_CHANWIDTH_USE_HT; + break; + } + } + + if (ieee80211_freq_to_channel_ext(freq, sec_chan, vht, op_class, + chan) == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_DEBUG, + "Cannot determine operating class and channel"); + return -1; + } + + *phy_type = ieee80211_get_phy_type(freq, ht_oper != NULL, + vht_oper != NULL); + if (*phy_type == PHY_TYPE_UNSPECIFIED) { + wpa_printf(MSG_DEBUG, "Cannot determine phy type"); + return -1; + } + + return 0; +} + + +static int wpas_beacon_rep_add_frame_body(struct bitfield *eids, + enum beacon_report_detail detail, + struct wpa_bss *bss, u8 *buf, + size_t buf_len) +{ + u8 *ies = (u8 *) (bss + 1); + size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len; + u8 *pos = buf; + int rem_len; + + rem_len = 255 - sizeof(struct rrm_measurement_beacon_report) - + sizeof(struct rrm_measurement_report_element) - 2; + + if (detail > BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS) { + wpa_printf(MSG_DEBUG, + "Beacon Request: Invalid reporting detail: %d", + detail); + return -1; + } + + if (detail == BEACON_REPORT_DETAIL_NONE) + return 0; + + /* + * Minimal frame body subelement size: EID(1) + length(1) + TSF(8) + + * beacon interval(2) + capabilities(2) = 14 bytes + */ + if (buf_len < 14) + return 0; + + *pos++ = WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY; + /* The length will be filled later */ + pos++; + WPA_PUT_LE64(pos, bss->tsf); + pos += sizeof(bss->tsf); + WPA_PUT_LE16(pos, bss->beacon_int); + pos += 2; + WPA_PUT_LE16(pos, bss->caps); + pos += 2; + + rem_len -= pos - buf; + + /* + * According to IEEE Std 802.11-2016, 9.4.2.22.7, if the reported frame + * body subelement causes the element to exceed the maximum element + * size, the subelement is truncated so that the last IE is a complete + * IE. So even when required to report all IEs, add elements one after + * the other and stop once there is no more room in the measurement + * element. + */ + while (ies_len > 2 && 2U + ies[1] <= ies_len && rem_len > 0) { + if (detail == BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS || + (eids && bitfield_is_set(eids, ies[0]))) { + u8 eid = ies[0], elen = ies[1]; + + if ((eid == WLAN_EID_TIM || eid == WLAN_EID_RSN) && + elen > 4) + elen = 4; + /* + * TODO: Truncate IBSS DFS element as described in + * IEEE Std 802.11-2016, 9.4.2.22.7. + */ + + if (2 + elen > buf + buf_len - pos || + 2 + elen > rem_len) + break; + + *pos++ = ies[0]; + *pos++ = elen; + os_memcpy(pos, ies + 2, elen); + pos += elen; + rem_len -= 2 + elen; + } + + ies_len -= 2 + ies[1]; + ies += 2 + ies[1]; + } + + /* Now the length is known */ + buf[1] = pos - buf - 2; + return pos - buf; +} + + +static int wpas_add_beacon_rep(struct wpa_supplicant *wpa_s, + struct wpabuf **wpa_buf, struct wpa_bss *bss, + u64 start, u64 parent_tsf) +{ + struct beacon_rep_data *data = &wpa_s->beacon_rep_data; + u8 *ie = (u8 *) (bss + 1); + size_t ie_len = bss->ie_len + bss->beacon_ie_len; + int ret; + u8 *buf; + struct rrm_measurement_beacon_report *rep; + + if (os_memcmp(data->bssid, broadcast_ether_addr, ETH_ALEN) != 0 && + os_memcmp(data->bssid, bss->bssid, ETH_ALEN) != 0) + return 0; + + if (data->ssid_len && + (data->ssid_len != bss->ssid_len || + os_memcmp(data->ssid, bss->ssid, bss->ssid_len) != 0)) + return 0; + + /* Maximum element length: beacon report element + reported frame body + * subelement + all IEs of the reported beacon */ + buf = os_malloc(sizeof(*rep) + 14 + ie_len); + if (!buf) + return -1; + + rep = (struct rrm_measurement_beacon_report *) buf; + if (wpas_get_op_chan_phy(bss->freq, ie, ie_len, &rep->op_class, + &rep->channel, &rep->report_info) < 0) { + ret = 0; + goto out; + } + + rep->start_time = host_to_le64(start); + rep->duration = host_to_le16(data->scan_params.duration); + rep->rcpi = rssi_to_rcpi(bss->level); + rep->rsni = 255; /* 255 indicates that RSNI is not available */ + os_memcpy(rep->bssid, bss->bssid, ETH_ALEN); + rep->antenna_id = 0; /* unknown */ + rep->parent_tsf = host_to_le32(parent_tsf); + + ret = wpas_beacon_rep_add_frame_body(data->eids, data->report_detail, + bss, rep->variable, 14 + ie_len); + if (ret < 0) + goto out; + + ret = wpas_rrm_report_elem(wpa_buf, wpa_s->beacon_rep_data.token, + MEASUREMENT_REPORT_MODE_ACCEPT, + MEASURE_TYPE_BEACON, buf, + ret + sizeof(*rep)); +out: + os_free(buf); + return ret; +} + + +static int wpas_beacon_rep_no_results(struct wpa_supplicant *wpa_s, + struct wpabuf **buf) +{ + return wpas_rrm_report_elem(buf, wpa_s->beacon_rep_data.token, + MEASUREMENT_REPORT_MODE_ACCEPT, + MEASURE_TYPE_BEACON, NULL, 0); +} + + +static void wpas_beacon_rep_table(struct wpa_supplicant *wpa_s, + struct wpabuf **buf) +{ + size_t i; + + for (i = 0; i < wpa_s->last_scan_res_used; i++) { + if (wpas_add_beacon_rep(wpa_s, buf, wpa_s->last_scan_res[i], + 0, 0) < 0) + break; + } + + if (!(*buf)) + wpas_beacon_rep_no_results(wpa_s, buf); + + wpa_hexdump_buf(MSG_DEBUG, "RRM: Radio Measurement report", *buf); +} + + +void wpas_rrm_refuse_request(struct wpa_supplicant *wpa_s) +{ + if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr)) { + struct wpabuf *buf = NULL; + + if (wpas_rrm_report_elem(&buf, wpa_s->beacon_rep_data.token, + MEASUREMENT_REPORT_MODE_REJECT_REFUSED, + MEASURE_TYPE_BEACON, NULL, 0)) { + wpa_printf(MSG_ERROR, "RRM: Memory allocation failed"); + wpabuf_free(buf); + return; + } + + wpas_rrm_send_msr_report(wpa_s, buf); + wpabuf_free(buf); + } + + wpas_clear_beacon_rep_data(wpa_s); +} + + +static void wpas_rrm_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_driver_scan_params *params = + &wpa_s->beacon_rep_data.scan_params; + u16 prev_duration = params->duration; + + if (!wpa_s->current_bss) + return; + + if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL) && + params->duration) { + wpa_printf(MSG_DEBUG, + "RRM: Cannot set scan duration due to missing driver support"); + params->duration = 0; + } + os_get_reltime(&wpa_s->beacon_rep_scan); + if (wpa_s->scanning || wpas_p2p_in_progress(wpa_s) || + wpa_supplicant_trigger_scan(wpa_s, params)) + wpas_rrm_refuse_request(wpa_s); + params->duration = prev_duration; +} + + +static int wpas_rm_handle_beacon_req_subelem(struct wpa_supplicant *wpa_s, + struct beacon_rep_data *data, + u8 sid, u8 slen, const u8 *subelem) +{ + u8 report_info, i; + + switch (sid) { + case WLAN_BEACON_REQUEST_SUBELEM_SSID: + if (!slen) { + wpa_printf(MSG_DEBUG, + "SSID subelement with zero length - wildcard SSID"); + break; + } + + if (slen > SSID_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "Invalid SSID subelement length: %u", slen); + return -1; + } + + data->ssid_len = slen; + os_memcpy(data->ssid, subelem, data->ssid_len); + break; + case WLAN_BEACON_REQUEST_SUBELEM_INFO: + if (slen != 2) { + wpa_printf(MSG_DEBUG, + "Invalid reporting information subelement length: %u", + slen); + return -1; + } + + report_info = subelem[0]; + if (report_info != 0) { + wpa_printf(MSG_DEBUG, + "reporting information=%u is not supported", + report_info); + return 0; + } + break; + case WLAN_BEACON_REQUEST_SUBELEM_DETAIL: + if (slen != 1) { + wpa_printf(MSG_DEBUG, + "Invalid reporting detail subelement length: %u", + slen); + return -1; + } + + data->report_detail = subelem[0]; + if (data->report_detail > + BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS) { + wpa_printf(MSG_DEBUG, "Invalid reporting detail: %u", + subelem[0]); + return -1; + } + + break; + case WLAN_BEACON_REQUEST_SUBELEM_REQUEST: + if (data->report_detail != + BEACON_REPORT_DETAIL_REQUESTED_ONLY) { + wpa_printf(MSG_DEBUG, + "Beacon request: request subelement is present but report detail is %u", + data->report_detail); + return -1; + } + + if (!slen) { + wpa_printf(MSG_DEBUG, + "Invalid request subelement length: %u", + slen); + return -1; + } + + if (data->eids) { + wpa_printf(MSG_DEBUG, + "Beacon Request: Request subelement appears more than once"); + return -1; + } + + data->eids = bitfield_alloc(255); + if (!data->eids) { + wpa_printf(MSG_DEBUG, "Failed to allocate EIDs bitmap"); + return -1; + } + + for (i = 0; i < slen; i++) + bitfield_set(data->eids, subelem[i]); + break; + case WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL: + /* Skip - it will be processed when freqs are added */ + break; + default: + wpa_printf(MSG_DEBUG, + "Beacon request: Unknown subelement id %u", sid); + break; + } + + return 1; +} + + +/** + * Returns 0 if the next element can be processed, 1 if some operation was + * triggered, and -1 if processing failed (i.e., the element is in invalid + * format or an internal error occurred). + */ +static int +wpas_rm_handle_beacon_req(struct wpa_supplicant *wpa_s, + u8 elem_token, int duration_mandatory, + const struct rrm_measurement_beacon_request *req, + size_t len, struct wpabuf **buf) +{ + struct beacon_rep_data *data = &wpa_s->beacon_rep_data; + struct wpa_driver_scan_params *params = &data->scan_params; + const u8 *subelems; + size_t elems_len; + u16 rand_interval; + u32 interval_usec; + u32 _rand; + int ret = 0, res; + u8 reject_mode; + + if (len < sizeof(*req)) + return -1; + + if (req->mode != BEACON_REPORT_MODE_PASSIVE && + req->mode != BEACON_REPORT_MODE_ACTIVE && + req->mode != BEACON_REPORT_MODE_TABLE) + return 0; + + subelems = req->variable; + elems_len = len - sizeof(*req); + rand_interval = le_to_host16(req->rand_interval); + + os_free(params->freqs); + os_memset(params, 0, sizeof(*params)); + + data->token = elem_token; + + /* default reporting detail is all fixed length fields and all + * elements */ + data->report_detail = BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS; + os_memcpy(data->bssid, req->bssid, ETH_ALEN); + + while (elems_len >= 2) { + if (subelems[1] > elems_len - 2) { + wpa_printf(MSG_DEBUG, + "Beacon Request: Truncated subelement"); + ret = -1; + goto out; + } + + res = wpas_rm_handle_beacon_req_subelem( + wpa_s, data, subelems[0], subelems[1], &subelems[2]); + if (res < 0) { + ret = res; + goto out; + } else if (!res) { + reject_mode = MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE; + goto out_reject; + } + + elems_len -= 2 + subelems[1]; + subelems += 2 + subelems[1]; + } + + if (req->mode == BEACON_REPORT_MODE_TABLE) { + wpas_beacon_rep_table(wpa_s, buf); + goto out; + } + + params->freqs = wpas_beacon_request_freqs( + wpa_s, req->oper_class, req->channel, + req->mode == BEACON_REPORT_MODE_ACTIVE, + req->variable, len - sizeof(*req)); + if (!params->freqs) { + wpa_printf(MSG_DEBUG, "Beacon request: No valid channels"); + reject_mode = MEASUREMENT_REPORT_MODE_REJECT_REFUSED; + goto out_reject; + } + + params->duration = le_to_host16(req->duration); + params->duration_mandatory = duration_mandatory; + if (!params->duration) { + wpa_printf(MSG_DEBUG, "Beacon request: Duration is 0"); + ret = -1; + goto out; + } + + params->only_new_results = 1; + + if (req->mode == BEACON_REPORT_MODE_ACTIVE) { + params->ssids[params->num_ssids].ssid = data->ssid; + params->ssids[params->num_ssids++].ssid_len = data->ssid_len; + } + + if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0) + _rand = os_random(); + interval_usec = (_rand % (rand_interval + 1)) * 1024; + eloop_register_timeout(0, interval_usec, wpas_rrm_scan_timeout, wpa_s, + NULL); + return 1; +out_reject: + if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr) && + wpas_rrm_report_elem(buf, elem_token, reject_mode, + MEASURE_TYPE_BEACON, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "RRM: Failed to add report element"); + ret = -1; + } +out: + wpas_clear_beacon_rep_data(wpa_s); + return ret; +} + + +static int +wpas_rrm_handle_msr_req_element( + struct wpa_supplicant *wpa_s, + const struct rrm_measurement_request_element *req, + struct wpabuf **buf) +{ + int duration_mandatory; + + wpa_printf(MSG_DEBUG, "Measurement request type %d token %d", + req->type, req->token); + + if (req->mode & MEASUREMENT_REQUEST_MODE_ENABLE) { + /* Enable bit is not supported for now */ + wpa_printf(MSG_DEBUG, "RRM: Enable bit not supported, ignore"); + return 0; + } + + if ((req->mode & MEASUREMENT_REQUEST_MODE_PARALLEL) && + req->type > MEASURE_TYPE_RPI_HIST) { + /* Parallel measurements are not supported for now */ + wpa_printf(MSG_DEBUG, + "RRM: Parallel measurements are not supported, reject"); + goto reject; + } + + duration_mandatory = + !!(req->mode & MEASUREMENT_REQUEST_MODE_DURATION_MANDATORY); + + switch (req->type) { + case MEASURE_TYPE_LCI: + return wpas_rrm_build_lci_report(wpa_s, req, buf); + case MEASURE_TYPE_BEACON: + if (duration_mandatory && + !(wpa_s->drv_rrm_flags & + WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL)) { + wpa_printf(MSG_DEBUG, + "RRM: Driver does not support dwell time configuration - reject beacon report with mandatory duration"); + goto reject; + } + return wpas_rm_handle_beacon_req(wpa_s, req->token, + duration_mandatory, + (const void *) req->variable, + req->len - 3, buf); + default: + wpa_printf(MSG_INFO, + "RRM: Unsupported radio measurement type %u", + req->type); + break; + } + +reject: + if (!is_multicast_ether_addr(wpa_s->rrm.dst_addr) && + wpas_rrm_report_elem(buf, req->token, + MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE, + req->type, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "RRM: Failed to add report element"); + return -1; + } + + return 0; +} + + +static struct wpabuf * +wpas_rrm_process_msr_req_elems(struct wpa_supplicant *wpa_s, const u8 *pos, + size_t len) +{ + struct wpabuf *buf = NULL; + + while (len) { + const struct rrm_measurement_request_element *req; + int res; + + if (len < 2) { + wpa_printf(MSG_DEBUG, "RRM: Truncated element"); + goto out; + } + + req = (const struct rrm_measurement_request_element *) pos; + if (req->eid != WLAN_EID_MEASURE_REQUEST) { + wpa_printf(MSG_DEBUG, + "RRM: Expected Measurement Request element, but EID is %u", + req->eid); + goto out; + } + + if (req->len < 3) { + wpa_printf(MSG_DEBUG, "RRM: Element length too short"); + goto out; + } + + if (req->len > len - 2) { + wpa_printf(MSG_DEBUG, "RRM: Element length too long"); + goto out; + } + + res = wpas_rrm_handle_msr_req_element(wpa_s, req, &buf); + if (res < 0) + goto out; + + pos += req->len + 2; + len -= req->len + 2; + } + + return buf; + +out: + wpabuf_free(buf); + return NULL; +} + + +void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s, + const u8 *src, const u8 *dst, + const u8 *frame, size_t len) +{ + struct wpabuf *report; + + if (wpa_s->wpa_state != WPA_COMPLETED) { + wpa_printf(MSG_INFO, + "RRM: Ignoring radio measurement request: Not associated"); + return; + } + + if (!wpa_s->rrm.rrm_used) { + wpa_printf(MSG_INFO, + "RRM: Ignoring radio measurement request: Not RRM network"); + return; + } + + if (len < 3) { + wpa_printf(MSG_INFO, + "RRM: Ignoring too short radio measurement request"); + return; + } + + wpa_s->rrm.token = *frame; + os_memcpy(wpa_s->rrm.dst_addr, dst, ETH_ALEN); + + /* Number of repetitions is not supported */ + + report = wpas_rrm_process_msr_req_elems(wpa_s, frame + 3, len - 3); + if (!report) + return; + + wpas_rrm_send_msr_report(wpa_s, report); + wpabuf_free(report); +} + + +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.dialog_token = req->dialog_token; + report.tpc.eid = WLAN_EID_TPC_REPORT; + report.tpc.len = 2; + /* Note: The driver is expected to update report.tpc.tx_power and + * report.tpc.link_margin subfields when sending out this frame. + * Similarly, the driver would need to update report.rx_ant_id and + * report.tx_ant_id subfields. */ + report.rsni = 255; /* 255 indicates that RSNI is not available */ + report.rcpi = rssi_to_rcpi(rssi); + + /* 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_buf(MSG_DEBUG, "RRM: Link measurement report", 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); +} + + +int wpas_beacon_rep_scan_process(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res, + struct scan_info *info) +{ + size_t i = 0; + struct wpabuf *buf = NULL; + + if (!wpa_s->beacon_rep_data.token) + return 0; + + if (!wpa_s->current_bss) + goto out; + + /* If the measurement was aborted, don't report partial results */ + if (info->aborted) + goto out; + + wpa_printf(MSG_DEBUG, "RRM: TSF BSSID: " MACSTR " current BSS: " MACSTR, + MAC2STR(info->scan_start_tsf_bssid), + MAC2STR(wpa_s->current_bss->bssid)); + if ((wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT) && + os_memcmp(info->scan_start_tsf_bssid, wpa_s->current_bss->bssid, + ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, + "RRM: Ignore scan results due to mismatching TSF BSSID"); + goto out; + } + + for (i = 0; i < scan_res->num; i++) { + struct wpa_bss *bss = + wpa_bss_get_bssid(wpa_s, scan_res->res[i]->bssid); + + if (!bss) + continue; + + if ((wpa_s->drv_rrm_flags & + WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT) && + os_memcmp(scan_res->res[i]->tsf_bssid, + wpa_s->current_bss->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, + "RRM: Ignore scan result for " MACSTR + " due to mismatching TSF BSSID" MACSTR, + MAC2STR(scan_res->res[i]->bssid), + MAC2STR(scan_res->res[i]->tsf_bssid)); + continue; + } + + /* + * Don't report results that were not received during the + * current measurement. + */ + if (!(wpa_s->drv_rrm_flags & + WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT)) { + struct os_reltime update_time, diff; + + /* For now, allow 8 ms older results due to some + * unknown issue with cfg80211 BSS table updates during + * a scan with the current BSS. + * TODO: Fix this more properly to avoid having to have + * this type of hacks in place. */ + calculate_update_time(&scan_res->fetch_time, + scan_res->res[i]->age, + &update_time); + os_reltime_sub(&wpa_s->beacon_rep_scan, + &update_time, &diff); + if (os_reltime_before(&update_time, + &wpa_s->beacon_rep_scan) && + (diff.sec || diff.usec >= 8000)) { + wpa_printf(MSG_DEBUG, + "RRM: Ignore scan result for " MACSTR + " due to old update (age(ms) %u, calculated age %u.%06u seconds)", + MAC2STR(scan_res->res[i]->bssid), + scan_res->res[i]->age, + (unsigned int) diff.sec, + (unsigned int) diff.usec); + continue; + } + } else if (info->scan_start_tsf > + scan_res->res[i]->parent_tsf) { + continue; + } + + if (wpas_add_beacon_rep(wpa_s, &buf, bss, info->scan_start_tsf, + scan_res->res[i]->parent_tsf) < 0) + break; + } + + if (!buf && wpas_beacon_rep_no_results(wpa_s, &buf)) + goto out; + + wpa_hexdump_buf(MSG_DEBUG, "RRM: Radio Measurement report", buf); + + wpas_rrm_send_msr_report(wpa_s, buf); + wpabuf_free(buf); + +out: + wpas_clear_beacon_rep_data(wpa_s); + return 1; +} + + +void wpas_clear_beacon_rep_data(struct wpa_supplicant *wpa_s) +{ + struct beacon_rep_data *data = &wpa_s->beacon_rep_data; + + eloop_cancel_timeout(wpas_rrm_scan_timeout, wpa_s, NULL); + bitfield_free(data->eids); + os_free(data->scan_params.freqs); + os_memset(data, 0, sizeof(*data)); +} diff --git a/contrib/wpa/wpa_supplicant/scan.c b/contrib/wpa/wpa_supplicant/scan.c index fb8ebdf2ecc1..ee39e0c9228d 100644 --- a/contrib/wpa/wpa_supplicant/scan.c +++ b/contrib/wpa/wpa_supplicant/scan.c @@ -117,9 +117,19 @@ int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s) static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { + int min_temp_disabled = 0; + while (ssid) { - if (!wpas_network_disabled(wpa_s, ssid)) - break; + if (!wpas_network_disabled(wpa_s, ssid)) { + int temp_disabled = wpas_temp_disabled(wpa_s, ssid); + + if (temp_disabled <= 0) + break; + + if (!min_temp_disabled || + temp_disabled < min_temp_disabled) + min_temp_disabled = temp_disabled; + } ssid = ssid->next; } @@ -128,7 +138,7 @@ static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_DEBUG, "wpa_supplicant_assoc_try: Reached " "end of scan list - go back to beginning"); wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN; - wpa_supplicant_req_scan(wpa_s, 0, 0); + wpa_supplicant_req_scan(wpa_s, min_temp_disabled, 0); return; } if (ssid->next) { @@ -176,10 +186,22 @@ static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit) params->only_new_results = 1; } ret = wpa_drv_scan(wpa_s, params); + /* + * Store the obtained vendor scan cookie (if any) in wpa_s context. + * The current design is to allow only one scan request on each + * interface, hence having this scan cookie stored in wpa_s context is + * fine for now. + * + * Revisit this logic if concurrent scan operations per interface + * is supported. + */ + if (ret == 0) + wpa_s->curr_scan_cookie = params->scan_cookie; wpa_scan_free_params(params); work->ctx = NULL; if (ret) { - int retry = wpa_s->last_scan_req != MANUAL_SCAN_REQ; + int retry = wpa_s->last_scan_req != MANUAL_SCAN_REQ && + !wpa_s->beacon_rep_data.token; if (wpa_s->disconnected) retry = 0; @@ -197,7 +219,14 @@ static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit) /* 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); + } else if (wpa_s->scan_res_handler) { + /* Clear the scan_res_handler */ + wpa_s->scan_res_handler = NULL; } + + if (wpa_s->beacon_rep_data.token) + wpas_rrm_refuse_request(wpa_s); + return; } @@ -426,6 +455,33 @@ static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s, #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_MBO +static void wpas_fils_req_param_add_max_channel(struct wpa_supplicant *wpa_s, + struct wpabuf **ie) +{ + if (wpabuf_resize(ie, 5)) { + wpa_printf(MSG_DEBUG, + "Failed to allocate space for FILS Request Parameters element"); + return; + } + + /* FILS Request Parameters element */ + wpabuf_put_u8(*ie, WLAN_EID_EXTENSION); + wpabuf_put_u8(*ie, 3); /* FILS Request attribute length */ + wpabuf_put_u8(*ie, WLAN_EID_EXT_FILS_REQ_PARAMS); + /* Parameter control bitmap */ + wpabuf_put_u8(*ie, 0); + /* Max Channel Time field - contains the value of MaxChannelTime + * parameter of the MLME-SCAN.request primitive represented in units of + * TUs, as an unsigned integer. A Max Channel Time field value of 255 + * is used to indicate any duration of more than 254 TUs, or an + * unspecified or unknown duration. (IEEE Std 802.11ai-2016, 9.4.2.178) + */ + wpabuf_put_u8(*ie, 255); +} +#endif /* CONFIG_MBO */ + + void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s) { struct wpabuf *default_ies = NULL; @@ -447,8 +503,10 @@ void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s) wpabuf_put_data(default_ies, ext_capab, ext_capab_len); #ifdef CONFIG_MBO - /* Send cellular capabilities for potential MBO STAs */ - if (wpabuf_resize(&default_ies, 9) == 0) + if (wpa_s->enable_oce & OCE_STA) + wpas_fils_req_param_add_max_channel(wpa_s, &default_ies); + /* Send MBO and OCE capabilities */ + if (wpabuf_resize(&default_ies, 12) == 0) wpas_mbo_scan_ie(wpa_s, default_ies); #endif /* CONFIG_MBO */ @@ -488,6 +546,11 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) wpas_add_interworking_elements(wpa_s, extra_ie); #endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_MBO + if (wpa_s->enable_oce & OCE_STA) + wpas_fils_req_param_add_max_channel(wpa_s, &extra_ie); +#endif /* CONFIG_MBO */ + #ifdef CONFIG_WPS wps = wpas_wps_in_use(wpa_s, &req_type); @@ -529,8 +592,8 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s) #endif /* CONFIG_FST */ #ifdef CONFIG_MBO - /* Send cellular capabilities for potential MBO STAs */ - if (wpabuf_resize(&extra_ie, 9) == 0) + /* Send MBO and OCE capabilities */ + if (wpabuf_resize(&extra_ie, 12) == 0) wpas_mbo_scan_ie(wpa_s, extra_ie); #endif /* CONFIG_MBO */ @@ -614,6 +677,87 @@ static void wpa_setband_scan_freqs(struct wpa_supplicant *wpa_s, } +static void wpa_add_scan_ssid(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params, + size_t max_ssids, const u8 *ssid, size_t ssid_len) +{ + unsigned int j; + + for (j = 0; j < params->num_ssids; j++) { + if (params->ssids[j].ssid_len == ssid_len && + params->ssids[j].ssid && + os_memcmp(params->ssids[j].ssid, ssid, ssid_len) == 0) + return; /* already in the list */ + } + + if (params->num_ssids + 1 > max_ssids) { + wpa_printf(MSG_DEBUG, "Over max scan SSIDs for manual request"); + return; + } + + wpa_printf(MSG_DEBUG, "Scan SSID (manual request): %s", + wpa_ssid_txt(ssid, ssid_len)); + + params->ssids[params->num_ssids].ssid = ssid; + params->ssids[params->num_ssids].ssid_len = ssid_len; + params->num_ssids++; +} + + +static void wpa_add_owe_scan_ssid(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params, + struct wpa_ssid *ssid, size_t max_ssids) +{ +#ifdef CONFIG_OWE + struct wpa_bss *bss; + + if (!(ssid->key_mgmt & WPA_KEY_MGMT_OWE)) + return; + + wpa_printf(MSG_DEBUG, "OWE: Look for transition mode AP. ssid=%s", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + const u8 *owe, *pos, *end; + const u8 *owe_ssid; + size_t owe_ssid_len; + + if (bss->ssid_len != ssid->ssid_len || + os_memcmp(bss->ssid, ssid->ssid, ssid->ssid_len) != 0) + continue; + + owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE); + if (!owe || owe[1] < 4) + continue; + + pos = owe + 6; + end = owe + 2 + owe[1]; + + /* Must include BSSID and ssid_len */ + if (end - pos < ETH_ALEN + 1) + return; + + /* Skip BSSID */ + pos += ETH_ALEN; + owe_ssid_len = *pos++; + owe_ssid = pos; + + if ((size_t) (end - pos) < owe_ssid_len || + owe_ssid_len > SSID_MAX_LEN) + return; + + wpa_printf(MSG_DEBUG, + "OWE: scan_ssids: transition mode OWE ssid=%s", + wpa_ssid_txt(owe_ssid, owe_ssid_len)); + + wpa_add_scan_ssid(wpa_s, params, max_ssids, + owe_ssid, owe_ssid_len); + return; + } +#endif /* CONFIG_OWE */ +} + + static void wpa_set_scan_ssids(struct wpa_supplicant *wpa_s, struct wpa_driver_scan_params *params, size_t max_ssids) @@ -628,33 +772,17 @@ static void wpa_set_scan_ssids(struct wpa_supplicant *wpa_s, max_ssids = max_ssids > 1 ? max_ssids - 1 : max_ssids; 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) + if (!ssid) continue; - - 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++; + if (ssid->scan_ssid) + wpa_add_scan_ssid(wpa_s, params, max_ssids, + ssid->ssid, ssid->ssid_len); + /* + * Also add the SSID of the OWE BSS, to allow discovery of + * transition mode APs more quickly. + */ + wpa_add_owe_scan_ssid(wpa_s, params, ssid, max_ssids); } wpa_s->scan_id_count = 0; @@ -703,10 +831,7 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) size_t max_ssids; 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; - } + wpa_s->ignore_post_flush_scan_res = 0; if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) { wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled"); @@ -768,6 +893,21 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) return; } + /* + * Don't cancel the scan based on ongoing PNO; defer it. Some scans are + * used for changing modes inside wpa_supplicant (roaming, + * auto-reconnect, etc). Discarding the scan might hurt these processes. + * The normal use case for PNO is to suspend the host immediately after + * starting PNO, so the periodic 100 ms attempts to run the scan do not + * normally happen in practice multiple times, i.e., this is simply + * restarting scanning once the host is woken up and PNO stopped. + */ + if (wpa_s->pno || wpa_s->pno_sched_pending) { + wpa_dbg(wpa_s, MSG_DEBUG, "Defer scan - PNO is in progress"); + wpa_supplicant_req_scan(wpa_s, 0, 100000); + return; + } + if (wpa_s->conf->ap_scan == 2) max_ssids = 1; else { @@ -909,6 +1049,17 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx) if (params.num_ssids + 1 >= max_ssids) break; } + + if (!wpas_network_disabled(wpa_s, ssid)) { + /* + * Also add the SSID of the OWE BSS, to allow + * discovery of transition mode APs more + * quickly. + */ + wpa_add_owe_scan_ssid(wpa_s, ¶ms, ssid, + max_ssids); + } + ssid = ssid->next; if (ssid == start) break; @@ -995,6 +1146,13 @@ ssid_list_set: wpa_s->manual_scan_freqs = NULL; } + if (params.freqs == NULL && wpa_s->select_network_scan_freqs) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Limit select_network scan to specified channels"); + params.freqs = wpa_s->select_network_scan_freqs; + wpa_s->select_network_scan_freqs = NULL; + } + if (params.freqs == NULL && wpa_s->next_scan_freqs) { wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously " "generated frequency list"); @@ -1029,6 +1187,11 @@ ssid_list_set: } } +#ifdef CONFIG_MBO + if (wpa_s->enable_oce & OCE_STA) + params.oce_scan = 1; +#endif /* CONFIG_MBO */ + params.filter_ssids = wpa_supplicant_build_filter_ssids( wpa_s->conf, ¶ms.num_filter_ssids); if (extra_ie) { @@ -1047,7 +1210,8 @@ ssid_list_set: } #endif /* CONFIG_P2P */ - if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) { + if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) && + wpa_s->wpa_state <= WPA_SCANNING) { params.mac_addr_rand = 1; if (wpa_s->mac_addr_scan) { params.mac_addr = wpa_s->mac_addr_scan; @@ -1225,6 +1389,26 @@ int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s, } +static void +wpa_scan_set_relative_rssi_params(struct wpa_supplicant *wpa_s, + struct wpa_driver_scan_params *params) +{ + if (wpa_s->wpa_state != WPA_COMPLETED || + !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI) || + wpa_s->srp.relative_rssi_set == 0) + return; + + params->relative_rssi_set = 1; + params->relative_rssi = wpa_s->srp.relative_rssi; + + if (wpa_s->srp.relative_adjust_rssi == 0) + return; + + params->relative_adjust_band = wpa_s->srp.relative_adjust_band; + params->relative_adjust_rssi = wpa_s->srp.relative_adjust_rssi; +} + + /** * wpa_supplicant_req_sched_scan - Start a periodic scheduled scan * @wpa_s: Pointer to wpa_supplicant data @@ -1417,6 +1601,11 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s) int_array_concat(¶ms.freqs, wpa_s->conf->freq_list); } +#ifdef CONFIG_MBO + if (wpa_s->enable_oce & OCE_STA) + params.oce_scan = 1; +#endif /* CONFIG_MBO */ + scan_params = ¶ms; scan: @@ -1458,18 +1647,24 @@ scan: params.sched_scan_plans_num = 1; } + params.sched_scan_start_delay = wpa_s->conf->sched_scan_start_delay; + if (ssid || !wpa_s->first_sched_scan) { wpa_dbg(wpa_s, MSG_DEBUG, - "Starting sched scan: interval %u timeout %d", + "Starting sched scan after %u seconds: interval %u timeout %d", + params.sched_scan_start_delay, params.sched_scan_plans[0].interval, wpa_s->sched_scan_timeout); } else { - wpa_dbg(wpa_s, MSG_DEBUG, "Starting sched scan (no timeout)"); + wpa_dbg(wpa_s, MSG_DEBUG, + "Starting sched scan after %u seconds (no timeout)", + params.sched_scan_start_delay); } wpa_setband_scan_freqs(wpa_s, scan_params); - if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) { + if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) && + wpa_s->wpa_state <= WPA_SCANNING) { params.mac_addr_rand = 1; if (wpa_s->mac_addr_sched_scan) { params.mac_addr = wpa_s->mac_addr_sched_scan; @@ -1478,6 +1673,8 @@ scan: } } + wpa_scan_set_relative_rssi_params(wpa_s, scan_params); + ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params); wpabuf_free(extra_ie); os_free(params.filter_ssids); @@ -1618,7 +1815,13 @@ static int wpa_scan_get_max_rate(const struct wpa_scan_res *res) */ const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) { - return get_ie((const u8 *) (res + 1), res->ie_len, ie); + size_t ie_len = res->ie_len; + + /* Use the Beacon frame IEs if res->ie_len is not available */ + if (!ie_len) + ie_len = res->beacon_ie_len; + + return get_ie((const u8 *) (res + 1), ie_len, ie); } @@ -1735,10 +1938,12 @@ struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, * This doc https://supportforums.cisco.com/docs/DOC-12954 says, "the general * rule of thumb is that any SNR above 20 is good." This one * http://www.cisco.com/en/US/tech/tk722/tk809/technologies_q_and_a_item09186a00805e9a96.shtml#qa23 - * recommends 25 as a minimum SNR for 54 Mbps data rate. 30 is chosen here as a - * conservative value. + * recommends 25 as a minimum SNR for 54 Mbps data rate. The estimates used in + * scan_est_throughput() allow even smaller SNR values for the maximum rates + * (21 for 54 Mbps, 22 for VHT80 MCS9, 24 for HT40 and HT20 MCS7). Use 25 as a + * somewhat conservative value here. */ -#define GREAT_SNR 30 +#define GREAT_SNR 25 #define IS_5GHZ(n) (n > 4000) @@ -1786,10 +1991,12 @@ static int wpa_scan_result_compar(const void *a, const void *b) } /* 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)) { + if (snr_a && snr_b && abs(snr_b - snr_a) < 7) { if (wa->est_throughput != wb->est_throughput) return wb->est_throughput - wa->est_throughput; + } + if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) || + (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) { if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq)) return IS_5GHZ(wa->freq) ? -1 : 1; } @@ -2177,10 +2384,22 @@ wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_WPS */ - qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *), - compar); + if (scan_res->res) { + qsort(scan_res->res, scan_res->num, + sizeof(struct wpa_scan_res *), compar); + } dump_scan_res(scan_res); + if (wpa_s->ignore_post_flush_scan_res) { + /* FLUSH command aborted an ongoing scan and these are the + * results from the aborted scan. Do not process the results to + * maintain flushed state. */ + wpa_dbg(wpa_s, MSG_DEBUG, + "Do not update BSS table based on pending post-FLUSH scan results"); + wpa_s->ignore_post_flush_scan_res = 0; + return scan_res; + } + 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], @@ -2262,11 +2481,10 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src) for (i = 0; i < src->num_ssids; i++) { if (src->ssids[i].ssid) { - n = os_malloc(src->ssids[i].ssid_len); + n = os_memdup(src->ssids[i].ssid, + 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; } @@ -2274,30 +2492,26 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src) params->num_ssids = src->num_ssids; if (src->extra_ies) { - n = os_malloc(src->extra_ies_len); + n = os_memdup(src->extra_ies, 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)); + params->freqs = os_memdup(src->freqs, (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) * + params->filter_ssids = os_memdup(src->filter_ssids, + 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; } @@ -2305,17 +2519,18 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src) params->p2p_probe = src->p2p_probe; params->only_new_results = src->only_new_results; params->low_priority = src->low_priority; + params->duration = src->duration; + params->duration_mandatory = src->duration_mandatory; + params->oce_scan = src->oce_scan; if (src->sched_scan_plans_num > 0) { params->sched_scan_plans = - os_malloc(sizeof(*src->sched_scan_plans) * + os_memdup(src->sched_scan_plans, + sizeof(*src->sched_scan_plans) * src->sched_scan_plans_num); if (!params->sched_scan_plans) goto failed; - os_memcpy(params->sched_scan_plans, src->sched_scan_plans, - sizeof(*src->sched_scan_plans) * - src->sched_scan_plans_num); params->sched_scan_plans_num = src->sched_scan_plans_num; } @@ -2340,13 +2555,16 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src) if (src->bssid) { u8 *bssid; - bssid = os_malloc(ETH_ALEN); + bssid = os_memdup(src->bssid, ETH_ALEN); if (!bssid) goto failed; - os_memcpy(bssid, src->bssid, ETH_ALEN); params->bssid = bssid; } + params->relative_rssi_set = src->relative_rssi_set; + params->relative_rssi = src->relative_rssi; + params->relative_adjust_band = src->relative_adjust_band; + params->relative_adjust_rssi = src->relative_adjust_rssi; return params; failed: @@ -2404,7 +2622,7 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s) return 0; if ((wpa_s->wpa_state > WPA_SCANNING) && - (wpa_s->wpa_state <= WPA_COMPLETED)) { + (wpa_s->wpa_state < WPA_COMPLETED)) { wpa_printf(MSG_ERROR, "PNO: In assoc process"); return -EAGAIN; } @@ -2511,12 +2729,15 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s) params.sched_scan_plans_num = 1; } + params.sched_scan_start_delay = wpa_s->conf->sched_scan_start_delay; + 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) { + if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) && + wpa_s->wpa_state <= WPA_SCANNING) { params.mac_addr_rand = 1; if (wpa_s->mac_addr_pno) { params.mac_addr = wpa_s->mac_addr_pno; @@ -2524,6 +2745,8 @@ int wpas_start_pno(struct wpa_supplicant *wpa_s) } } + wpa_scan_set_relative_rssi_params(wpa_s, ¶ms); + ret = wpa_supplicant_start_sched_scan(wpa_s, ¶ms); os_free(params.filter_ssids); if (ret == 0) @@ -2614,18 +2837,20 @@ int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s, int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s) { - int scan_work = !!wpa_s->scan_work; - -#ifdef CONFIG_P2P - scan_work |= !!wpa_s->p2p_scan_work; -#endif /* CONFIG_P2P */ + struct wpa_radio_work *work; + struct wpa_radio *radio = wpa_s->radio; - if (scan_work && wpa_s->own_scan_running) { + dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) { + if (work->wpa_s != wpa_s || !work->started || + (os_strcmp(work->type, "scan") != 0 && + os_strcmp(work->type, "p2p-scan") != 0)) + continue; wpa_dbg(wpa_s, MSG_DEBUG, "Abort an ongoing scan"); - return wpa_drv_abort_scan(wpa_s); + return wpa_drv_abort_scan(wpa_s, wpa_s->curr_scan_cookie); } - return 0; + wpa_dbg(wpa_s, MSG_DEBUG, "No ongoing scan/p2p-scan found to abort"); + return -1; } diff --git a/contrib/wpa/wpa_supplicant/sme.c b/contrib/wpa/wpa_supplicant/sme.c index 61fd3b24549c..39c80696a94c 100644 --- a/contrib/wpa/wpa_supplicant/sme.c +++ b/contrib/wpa/wpa_supplicant/sme.c @@ -72,7 +72,7 @@ static int sme_set_sae_group(struct wpa_supplicant *wpa_s) 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; + return 0; } wpa_s->sme.sae_group_index++; } @@ -83,12 +83,29 @@ static int sme_set_sae_group(struct wpa_supplicant *wpa_s) static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, - const u8 *bssid) + const u8 *bssid, int external) { struct wpabuf *buf; size_t len; - - if (ssid->passphrase == NULL) { + const char *password; + +#ifdef CONFIG_TESTING_OPTIONS + if (wpa_s->sae_commit_override) { + wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override"); + buf = wpabuf_alloc(4 + wpabuf_len(wpa_s->sae_commit_override)); + if (!buf) + return NULL; + wpabuf_put_le16(buf, 1); /* Transaction seq# */ + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + wpabuf_put_buf(buf, wpa_s->sae_commit_override); + return buf; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + password = ssid->sae_password; + if (!password) + password = ssid->passphrase; + if (!password) { wpa_printf(MSG_DEBUG, "SAE: No password available"); return NULL; } @@ -99,27 +116,32 @@ static struct wpabuf * sme_auth_build_sae_commit(struct wpa_supplicant *wpa_s, } if (sae_prepare_commit(wpa_s->own_addr, bssid, - (u8 *) ssid->passphrase, - os_strlen(ssid->passphrase), + (u8 *) password, os_strlen(password), + ssid->sae_password_id, &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; + if (ssid->sae_password_id) + len += 4 + os_strlen(ssid->sae_password_id); 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); - sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token); + if (!external) { + wpabuf_put_le16(buf, 1); /* Transaction seq# */ + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + } + sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token, + ssid->sae_password_id); return buf; } -static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s) +static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s, + int external) { struct wpabuf *buf; @@ -127,8 +149,10 @@ static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s) if (buf == NULL) return NULL; - wpabuf_put_le16(buf, 2); /* Transaction seq# */ - wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + if (!external) { + wpabuf_put_le16(buf, 2); /* Transaction seq# */ + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + } sae_write_confirm(&wpa_s->sme.sae, buf); return buf; @@ -187,6 +211,10 @@ static void sme_auth_handle_rrm(struct wpa_supplicant *wpa_s, if (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION) *pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT; + *pos |= WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE | + WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE | + WLAN_RRM_CAPS_BEACON_REPORT_TABLE; + if (wpa_s->lci) pos[1] |= WLAN_RRM_CAPS_LCI_MEASUREMENT; @@ -204,16 +232,18 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IEEE80211R const u8 *ie; #endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_IEEE80211R +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS) const u8 *md = NULL; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R || CONFIG_FILS */ int i, bssid_changed; struct wpabuf *resp = NULL; u8 ext_capab[18]; int ext_capab_len; int skip_auth; + u8 *wpa_ie; + size_t wpa_ie_len; #ifdef CONFIG_MBO - const u8 *mbo; + const u8 *mbo_ie; #endif /* CONFIG_MBO */ if (bss == NULL) { @@ -300,13 +330,20 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, wpa_bss_get_ie(bss, WLAN_EID_RSN)) && wpa_key_mgmt_wpa(ssid->key_mgmt)) { int try_opportunistic; + const u8 *cache_id = NULL; + try_opportunistic = (ssid->proactive_key_caching < 0 ? wpa_s->conf->okc : ssid->proactive_key_caching) && (ssid->proto & WPA_PROTO_RSN); +#ifdef CONFIG_FILS + if (wpa_key_mgmt_fils(ssid->key_mgmt)) + cache_id = wpa_bss_get_fils_cache_id(bss); +#endif /* CONFIG_FILS */ if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, wpa_s->current_ssid, - try_opportunistic) == 0) + try_opportunistic, cache_id, + 0) == 0) 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, @@ -317,6 +354,20 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, wpas_connect_work_done(wpa_s); return; } +#ifdef CONFIG_HS20 + } else if (wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE) && + (ssid->key_mgmt & WPA_KEY_MGMT_OSEN)) { + /* No PMKSA caching, but otherwise similar to RSN/WPA */ + 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; + } +#endif /* CONFIG_HS20 */ } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) { /* @@ -356,6 +407,28 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, wpa_s->sme.assoc_req_ie_len = 0; } + /* In case the WPA vendor IE is used, it should be placed after all the + * non-vendor IEs, as the lower layer expects the IEs to be ordered as + * defined in the standard. Store the WPA IE so it can later be + * inserted at the correct location. + */ + wpa_ie = NULL; + wpa_ie_len = 0; + if (wpa_s->wpa_proto == WPA_PROTO_WPA) { + wpa_ie = os_memdup(wpa_s->sme.assoc_req_ie, + wpa_s->sme.assoc_req_ie_len); + if (wpa_ie) { + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Storing WPA IE"); + + wpa_ie_len = wpa_s->sme.assoc_req_ie_len; + wpa_s->sme.assoc_req_ie_len = 0; + } else { + wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed copy WPA IE"); + wpas_connect_work_done(wpa_s); + return; + } + } + #ifdef CONFIG_IEEE80211R ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN); if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN) @@ -366,7 +439,12 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, wpa_ft_prepare_auth_request(wpa_s->wpa, ie); } - if (md && wpa_key_mgmt_ft(ssid->key_mgmt)) { + if (md && !wpa_key_mgmt_ft(ssid->key_mgmt)) + md = NULL; + if (md) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: FT mobility domain %02x%02x", + md[0], md[1]); + if (wpa_s->sme.assoc_req_ie_len + 5 < sizeof(wpa_s->sme.assoc_req_ie)) { struct rsn_mdie *mdie; @@ -440,20 +518,10 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, sme_auth_handle_rrm(wpa_s, bss); -#ifdef CONFIG_MBO - mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE); - if (mbo) { - int len; - - len = wpas_mbo_supp_op_class_ie( - wpa_s, bss->freq, - wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, - sizeof(wpa_s->sme.assoc_req_ie) - - wpa_s->sme.assoc_req_ie_len); - if (len > 0) - wpa_s->sme.assoc_req_ie_len += len; - } -#endif /* CONFIG_MBO */ + wpa_s->sme.assoc_req_ie_len += wpas_supp_op_class_ie( + wpa_s, bss->freq, + wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, + sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len); if (params.p2p) wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT); @@ -477,12 +545,13 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, if (is_hs20_network(wpa_s, ssid, bss)) { struct wpabuf *hs20; - hs20 = wpabuf_alloc(20); + hs20 = wpabuf_alloc(20 + MAX_ROAMING_CONS_OI_LEN); if (hs20) { int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid); size_t len; wpas_hs20_add_indication(hs20, pps_mo_id); + wpas_hs20_add_roam_cons_sel(hs20, ssid); len = sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len; if (wpabuf_len(hs20) <= len) { @@ -496,6 +565,26 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_HS20 */ + if (wpa_ie) { + size_t len; + + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Reinsert WPA IE"); + + len = sizeof(wpa_s->sme.assoc_req_ie) - + wpa_s->sme.assoc_req_ie_len; + + if (len > wpa_ie_len) { + os_memcpy(wpa_s->sme.assoc_req_ie + + wpa_s->sme.assoc_req_ie_len, + wpa_ie, wpa_ie_len); + wpa_s->sme.assoc_req_ie_len += wpa_ie_len; + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Failed to add WPA IE"); + } + + os_free(wpa_ie); + } + if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) { struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]; size_t len; @@ -511,13 +600,16 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, } #ifdef CONFIG_MBO - if (mbo) { + mbo_ie = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE); + if (mbo_ie) { int len; len = wpas_mbo_ie(wpa_s, wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, sizeof(wpa_s->sme.assoc_req_ie) - - wpa_s->sme.assoc_req_ie_len); + wpa_s->sme.assoc_req_ie_len, + !!mbo_attr_from_mbo_ie(mbo_ie, + OCE_ATTR_ID_CAPA_IND)); if (len >= 0) wpa_s->sme.assoc_req_ie_len += len; } @@ -525,10 +617,11 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, #ifdef CONFIG_SAE if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE && - pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0) == 0) - { + pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0, + NULL, WPA_KEY_MGMT_SAE) == 0) { wpa_dbg(wpa_s, MSG_DEBUG, "PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication"); + wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); params.auth_alg = WPA_AUTH_ALG_OPEN; wpa_s->sme.sae_pmksa_caching = 1; } @@ -536,19 +629,105 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE) { if (start) resp = sme_auth_build_sae_commit(wpa_s, ssid, - bss->bssid); + bss->bssid, 0); else - resp = sme_auth_build_sae_confirm(wpa_s); + resp = sme_auth_build_sae_confirm(wpa_s, 0); if (resp == NULL) { wpas_connection_failed(wpa_s, bss->bssid); return; } - params.sae_data = wpabuf_head(resp); - params.sae_data_len = wpabuf_len(resp); + params.auth_data = wpabuf_head(resp); + params.auth_data_len = wpabuf_len(resp); wpa_s->sme.sae.state = start ? SAE_COMMITTED : SAE_CONFIRMED; } #endif /* CONFIG_SAE */ + old_ssid = wpa_s->current_ssid; + wpa_s->current_ssid = ssid; + wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid); + wpa_supplicant_initiate_eapol(wpa_s); + +#ifdef CONFIG_FILS + /* TODO: FILS operations can in some cases be done between different + * network_ctx (i.e., same credentials can be used with multiple + * networks). */ + if (params.auth_alg == WPA_AUTH_ALG_OPEN && + wpa_key_mgmt_fils(ssid->key_mgmt)) { + const u8 *indic; + u16 fils_info; + const u8 *realm, *username, *rrk; + size_t realm_len, username_len, rrk_len; + u16 next_seq_num; + + /* + * Check FILS Indication element (FILS Information field) bits + * indicating supported authentication algorithms against local + * configuration (ssid->fils_dh_group). Try to use FILS + * authentication only if the AP supports the combination in the + * network profile. */ + indic = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION); + if (!indic || indic[1] < 2) { + wpa_printf(MSG_DEBUG, "SME: " MACSTR + " does not include FILS Indication element - cannot use FILS authentication with it", + MAC2STR(bss->bssid)); + goto no_fils; + } + + fils_info = WPA_GET_LE16(indic + 2); + if (ssid->fils_dh_group == 0 && !(fils_info & BIT(9))) { + wpa_printf(MSG_DEBUG, "SME: " MACSTR + " does not support FILS SK without PFS - cannot use FILS authentication with it", + MAC2STR(bss->bssid)); + goto no_fils; + } + if (ssid->fils_dh_group != 0 && !(fils_info & BIT(10))) { + wpa_printf(MSG_DEBUG, "SME: " MACSTR + " does not support FILS SK with PFS - cannot use FILS authentication with it", + MAC2STR(bss->bssid)); + goto no_fils; + } + + if (wpa_s->last_con_fail_realm && + eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap, + &username, &username_len, + &realm, &realm_len, &next_seq_num, + &rrk, &rrk_len) == 0 && + realm && realm_len == wpa_s->last_con_fail_realm_len && + os_memcmp(realm, wpa_s->last_con_fail_realm, + realm_len) == 0) { + wpa_printf(MSG_DEBUG, + "SME: FILS authentication for this realm failed last time - try to regenerate ERP key hierarchy"); + goto no_fils; + } + + if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, + ssid, 0, + wpa_bss_get_fils_cache_id(bss), + 0) == 0) + wpa_printf(MSG_DEBUG, + "SME: Try to use FILS with PMKSA caching"); + resp = fils_build_auth(wpa_s->wpa, ssid->fils_dh_group, md); + if (resp) { + int auth_alg; + + if (ssid->fils_dh_group) + wpa_printf(MSG_DEBUG, + "SME: Try to use FILS SK authentication with PFS (DH Group %u)", + ssid->fils_dh_group); + else + wpa_printf(MSG_DEBUG, + "SME: Try to use FILS SK authentication without PFS"); + auth_alg = ssid->fils_dh_group ? + WPA_AUTH_ALG_FILS_SK_PFS : WPA_AUTH_ALG_FILS; + params.auth_alg = auth_alg; + params.auth_data = wpabuf_head(resp); + params.auth_data_len = wpabuf_len(resp); + wpa_s->sme.auth_alg = auth_alg; + } + } +no_fils: +#endif /* CONFIG_FILS */ + wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_cancel_scan(wpa_s); @@ -556,12 +735,9 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, " (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid), wpa_ssid_txt(params.ssid, params.ssid_len), params.freq); + eapol_sm_notify_portValid(wpa_s->eapol, FALSE); wpa_clear_keys(wpa_s, bss->bssid); wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING); - old_ssid = wpa_s->current_ssid; - wpa_s->current_ssid = ssid; - 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) wpas_notify_network_changed(wpa_s); @@ -650,6 +826,10 @@ static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit) return; } + /* Starting new connection, so clear the possibly used WPA IE from the + * previous association. */ + wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); + sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1); } @@ -700,8 +880,151 @@ void sme_authenticate(struct wpa_supplicant *wpa_s, #ifdef CONFIG_SAE +static int sme_external_auth_build_buf(struct wpabuf *buf, + struct wpabuf *params, + const u8 *sa, const u8 *da, + u16 auth_transaction, u16 seq_num) +{ + struct ieee80211_mgmt *resp; + + resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt, + u.auth.variable)); + + resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) | + (WLAN_FC_STYPE_AUTH << 4)); + os_memcpy(resp->da, da, ETH_ALEN); + os_memcpy(resp->sa, sa, ETH_ALEN); + os_memcpy(resp->bssid, da, ETH_ALEN); + resp->u.auth.auth_alg = host_to_le16(WLAN_AUTH_SAE); + resp->seq_ctrl = host_to_le16(seq_num << 4); + resp->u.auth.auth_transaction = host_to_le16(auth_transaction); + resp->u.auth.status_code = host_to_le16(WLAN_STATUS_SUCCESS); + if (params) + wpabuf_put_buf(buf, params); + + return 0; +} + + +static void sme_external_auth_send_sae_commit(struct wpa_supplicant *wpa_s, + const u8 *bssid, + struct wpa_ssid *ssid) +{ + struct wpabuf *resp, *buf; + + resp = sme_auth_build_sae_commit(wpa_s, ssid, bssid, 1); + if (!resp) + return; + + wpa_s->sme.sae.state = SAE_COMMITTED; + buf = wpabuf_alloc(4 + SAE_COMMIT_MAX_LEN + wpabuf_len(resp)); + if (!buf) { + wpabuf_free(resp); + return; + } + + wpa_s->sme.seq_num++; + sme_external_auth_build_buf(buf, resp, wpa_s->own_addr, + bssid, 1, wpa_s->sme.seq_num); + wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0); + wpabuf_free(resp); + wpabuf_free(buf); +} + + +static void sme_send_external_auth_status(struct wpa_supplicant *wpa_s, + u16 status) +{ + struct external_auth params; + + os_memset(¶ms, 0, sizeof(params)); + params.status = status; + os_memcpy(params.ssid, wpa_s->sme.ext_auth.ssid, + wpa_s->sme.ext_auth.ssid_len); + params.ssid_len = wpa_s->sme.ext_auth.ssid_len; + os_memcpy(params.bssid, wpa_s->sme.ext_auth.bssid, ETH_ALEN); + wpa_drv_send_external_auth_status(wpa_s, ¶ms); +} + + +static void sme_handle_external_auth_start(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + struct wpa_ssid *ssid; + size_t ssid_str_len = data->external_auth.ssid_len; + u8 *ssid_str = data->external_auth.ssid; + + /* Get the SSID conf from the ssid string obtained */ + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (!wpas_network_disabled(wpa_s, ssid) && + ssid_str_len == ssid->ssid_len && + os_memcmp(ssid_str, ssid->ssid, ssid_str_len) == 0 && + (ssid->key_mgmt & WPA_KEY_MGMT_SAE)) + break; + } + if (ssid) + sme_external_auth_send_sae_commit(wpa_s, + data->external_auth.bssid, + ssid); + else + sme_send_external_auth_status(wpa_s, + WLAN_STATUS_UNSPECIFIED_FAILURE); +} + + +static void sme_external_auth_send_sae_confirm(struct wpa_supplicant *wpa_s, + const u8 *da) +{ + struct wpabuf *resp, *buf; + + resp = sme_auth_build_sae_confirm(wpa_s, 1); + if (!resp) { + wpa_printf(MSG_DEBUG, "SAE: Confirm message buf alloc failure"); + return; + } + + wpa_s->sme.sae.state = SAE_CONFIRMED; + buf = wpabuf_alloc(4 + SAE_CONFIRM_MAX_LEN + wpabuf_len(resp)); + if (!buf) { + wpa_printf(MSG_DEBUG, "SAE: Auth Confirm buf alloc failure"); + wpabuf_free(resp); + return; + } + wpa_s->sme.seq_num++; + sme_external_auth_build_buf(buf, resp, wpa_s->own_addr, + da, 2, wpa_s->sme.seq_num); + wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1, 0); + wpabuf_free(resp); + wpabuf_free(buf); +} + + +void sme_external_auth_trigger(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ + if (RSN_SELECTOR_GET(&data->external_auth.key_mgmt_suite) != + RSN_AUTH_KEY_MGMT_SAE) + return; + + if (data->external_auth.action == EXT_AUTH_START) { + os_memcpy(&wpa_s->sme.ext_auth, data, + sizeof(struct external_auth)); + wpa_s->sme.seq_num = 0; + wpa_s->sme.sae.state = SAE_NOTHING; + wpa_s->sme.sae.send_confirm = 0; + wpa_s->sme.sae_group_index = 0; + sme_handle_external_auth_start(wpa_s, data); + } else if (data->external_auth.action == EXT_AUTH_ABORT) { + /* Report failure to driver for the wrong trigger */ + sme_send_external_auth_status(wpa_s, + WLAN_STATUS_UNSPECIFIED_FAILURE); + } +} + + static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, - u16 status_code, const u8 *data, size_t len) + u16 status_code, const u8 *data, size_t len, + int external, const u8 *sa) { int *groups; @@ -711,7 +1034,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, 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) { + (external || wpa_s->current_bss) && wpa_s->current_ssid) { int default_groups[] = { 19, 20, 21, 25, 26, 0 }; u16 group; @@ -738,25 +1061,45 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, 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); + if (!external) + sme_send_authentication(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, 1); + else + sme_external_auth_send_sae_commit( + wpa_s, wpa_s->sme.ext_auth.bssid, + wpa_s->current_ssid); 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) { + (external || 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); + if (!external) + sme_send_authentication(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, 1); + else + sme_external_auth_send_sae_commit( + wpa_s, wpa_s->sme.ext_auth.bssid, + wpa_s->current_ssid); return 0; } + if (auth_transaction == 1 && + status_code == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) { + const u8 *bssid = sa ? sa : wpa_s->pending_bssid; + + wpa_msg(wpa_s, MSG_INFO, + WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER MACSTR, + MAC2STR(bssid)); + return -1; + } + if (status_code != WLAN_STATUS_SUCCESS) return -1; @@ -766,7 +1109,7 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, groups = wpa_s->conf->sae_groups; wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit"); - if (wpa_s->current_bss == NULL || + if ((!external && wpa_s->current_bss == NULL) || wpa_s->current_ssid == NULL) return -1; if (wpa_s->sme.sae.state != SAE_COMMITTED) @@ -791,8 +1134,11 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, 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); + if (!external) + sme_send_authentication(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, 0); + else + sme_external_auth_send_sae_confirm(wpa_s, sa); return 0; } else if (auth_transaction == 2) { wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE confirm"); @@ -802,11 +1148,60 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction, return -1; wpa_s->sme.sae.state = SAE_ACCEPTED; sae_clear_temp_data(&wpa_s->sme.sae); + + if (external) { + /* Report success to driver */ + sme_send_external_auth_status(wpa_s, + WLAN_STATUS_SUCCESS); + } + return 1; } return -1; } + + +void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s, + const u8 *auth_frame, size_t len) +{ + const struct ieee80211_mgmt *header; + size_t auth_length; + + header = (const struct ieee80211_mgmt *) auth_frame; + auth_length = IEEE80211_HDRLEN + sizeof(header->u.auth); + + if (len < auth_length) { + /* Notify failure to the driver */ + sme_send_external_auth_status(wpa_s, + WLAN_STATUS_UNSPECIFIED_FAILURE); + return; + } + + if (le_to_host16(header->u.auth.auth_alg) == WLAN_AUTH_SAE) { + int res; + + res = sme_sae_auth( + wpa_s, le_to_host16(header->u.auth.auth_transaction), + le_to_host16(header->u.auth.status_code), + header->u.auth.variable, + len - auth_length, 1, header->sa); + if (res < 0) { + /* Notify failure to the driver */ + sme_send_external_auth_status( + wpa_s, WLAN_STATUS_UNSPECIFIED_FAILURE); + return; + } + 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->sme.sae.pmkid, wpa_s->pending_bssid); + } +} + #endif /* CONFIG_SAE */ @@ -847,7 +1242,7 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) int res; res = sme_sae_auth(wpa_s, data->auth.auth_transaction, data->auth.status_code, data->auth.ies, - data->auth.ies_len); + data->auth.ies_len, 0, NULL); if (res < 0) { wpas_connection_failed(wpa_s, wpa_s->pending_bssid); wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED); @@ -875,12 +1270,19 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) } } wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AUTH_REJECT MACSTR - " auth_type=%u auth_transaction=%u status_code=%u ie=%s", + " auth_type=%u auth_transaction=%u status_code=%u%s%s", MAC2STR(data->auth.peer), data->auth.auth_type, data->auth.auth_transaction, data->auth.status_code, - ie_txt); + ie_txt ? " ie=" : "", + ie_txt ? ie_txt : ""); os_free(ie_txt); +#ifdef CONFIG_FILS + if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS || + wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS_SK_PFS) + fils_connection_failure(wpa_s); +#endif /* CONFIG_FILS */ + if (data->auth.status_code != WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG || wpa_s->sme.auth_alg == data->auth.auth_type || @@ -916,9 +1318,17 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) #ifdef CONFIG_IEEE80211R if (data->auth.auth_type == WLAN_AUTH_FT) { + const u8 *ric_ies = NULL; + size_t ric_ies_len = 0; + + if (wpa_s->ric_ies) { + ric_ies = wpabuf_head(wpa_s->ric_ies); + ric_ies_len = wpabuf_len(wpa_s->ric_ies); + } if (wpa_ft_process_response(wpa_s->wpa, data->auth.ies, data->auth.ies_len, 0, - data->auth.peer, NULL, 0) < 0) { + data->auth.peer, + ric_ies, ric_ies_len) < 0) { wpa_dbg(wpa_s, MSG_DEBUG, "SME: FT Authentication response processing failed"); wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" @@ -933,16 +1343,75 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data) } #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_FILS + if (data->auth.auth_type == WLAN_AUTH_FILS_SK || + data->auth.auth_type == WLAN_AUTH_FILS_SK_PFS) { + u16 expect_auth_type; + + expect_auth_type = wpa_s->sme.auth_alg == + WPA_AUTH_ALG_FILS_SK_PFS ? WLAN_AUTH_FILS_SK_PFS : + WLAN_AUTH_FILS_SK; + if (data->auth.auth_type != expect_auth_type) { + wpa_dbg(wpa_s, MSG_DEBUG, + "SME: FILS Authentication response used different auth alg (%u; expected %u)", + data->auth.auth_type, expect_auth_type); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" + MACSTR + " reason=%d locally_generated=1", + MAC2STR(wpa_s->pending_bssid), + WLAN_REASON_DEAUTH_LEAVING); + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + wpa_supplicant_mark_disassoc(wpa_s); + return; + } + + if (fils_process_auth(wpa_s->wpa, wpa_s->pending_bssid, + data->auth.ies, data->auth.ies_len) < 0) { + wpa_dbg(wpa_s, MSG_DEBUG, + "SME: FILS Authentication response processing failed"); + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" + MACSTR + " reason=%d locally_generated=1", + MAC2STR(wpa_s->pending_bssid), + WLAN_REASON_DEAUTH_LEAVING); + wpas_connection_failed(wpa_s, wpa_s->pending_bssid); + wpa_supplicant_mark_disassoc(wpa_s); + return; + } + } +#endif /* CONFIG_FILS */ + sme_associate(wpa_s, ssid->mode, data->auth.peer, data->auth.auth_type); } +#ifdef CONFIG_FILS +#ifdef CONFIG_IEEE80211R +static void remove_ie(u8 *buf, size_t *len, u8 eid) +{ + u8 *pos, *next, *end; + + pos = (u8 *) get_ie(buf, *len, eid); + if (pos) { + next = pos + 2 + pos[1]; + end = buf + *len; + *len -= 2 + pos[1]; + os_memmove(pos, next, end - next); + } +} +#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_FILS */ + + void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, const u8 *bssid, u16 auth_type) { struct wpa_driver_associate_params params; struct ieee802_11_elems elems; +#ifdef CONFIG_FILS + u8 nonces[2 * FILS_NONCE_LEN]; +#endif /* CONFIG_FILS */ #ifdef CONFIG_HT_OVERRIDES struct ieee80211_ht_capabilities htcaps; struct ieee80211_ht_capabilities htcaps_mask; @@ -953,6 +1422,138 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, #endif /* CONFIG_VHT_OVERRIDES */ os_memset(¶ms, 0, sizeof(params)); + +#ifdef CONFIG_FILS + if (auth_type == WLAN_AUTH_FILS_SK || + auth_type == WLAN_AUTH_FILS_SK_PFS) { + struct wpabuf *buf; + const u8 *snonce, *anonce; + const unsigned int max_hlp = 20; + struct wpabuf *hlp[max_hlp]; + unsigned int i, num_hlp = 0; + struct fils_hlp_req *req; + + dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req, + list) { + hlp[num_hlp] = wpabuf_alloc(2 * ETH_ALEN + 6 + + wpabuf_len(req->pkt)); + if (!hlp[num_hlp]) + break; + wpabuf_put_data(hlp[num_hlp], req->dst, ETH_ALEN); + wpabuf_put_data(hlp[num_hlp], wpa_s->own_addr, + ETH_ALEN); + wpabuf_put_data(hlp[num_hlp], + "\xaa\xaa\x03\x00\x00\x00", 6); + wpabuf_put_buf(hlp[num_hlp], req->pkt); + num_hlp++; + if (num_hlp >= max_hlp) + break; + } + + buf = fils_build_assoc_req(wpa_s->wpa, ¶ms.fils_kek, + ¶ms.fils_kek_len, &snonce, + &anonce, + (const struct wpabuf **) hlp, + num_hlp); + for (i = 0; i < num_hlp; i++) + wpabuf_free(hlp[i]); + if (!buf) + return; + wpa_hexdump(MSG_DEBUG, "FILS: assoc_req before FILS elements", + wpa_s->sme.assoc_req_ie, + wpa_s->sme.assoc_req_ie_len); +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(wpa_s->key_mgmt)) { + /* Remove RSNE and MDE to allow them to be overridden + * with FILS+FT specific values from + * fils_build_assoc_req(). */ + remove_ie(wpa_s->sme.assoc_req_ie, + &wpa_s->sme.assoc_req_ie_len, + WLAN_EID_RSN); + wpa_hexdump(MSG_DEBUG, + "FILS: assoc_req after RSNE removal", + wpa_s->sme.assoc_req_ie, + wpa_s->sme.assoc_req_ie_len); + remove_ie(wpa_s->sme.assoc_req_ie, + &wpa_s->sme.assoc_req_ie_len, + WLAN_EID_MOBILITY_DOMAIN); + wpa_hexdump(MSG_DEBUG, + "FILS: assoc_req after MDE removal", + wpa_s->sme.assoc_req_ie, + wpa_s->sme.assoc_req_ie_len); + } +#endif /* CONFIG_IEEE80211R */ + /* TODO: Make wpa_s->sme.assoc_req_ie use dynamic allocation */ + if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(buf) > + sizeof(wpa_s->sme.assoc_req_ie)) { + wpa_printf(MSG_ERROR, + "FILS: Not enough buffer room for own AssocReq elements"); + wpabuf_free(buf); + return; + } + 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); + wpabuf_free(buf); + wpa_hexdump(MSG_DEBUG, "FILS: assoc_req after FILS elements", + wpa_s->sme.assoc_req_ie, + wpa_s->sme.assoc_req_ie_len); + + os_memcpy(nonces, snonce, FILS_NONCE_LEN); + os_memcpy(nonces + FILS_NONCE_LEN, anonce, FILS_NONCE_LEN); + params.fils_nonces = nonces; + params.fils_nonces_len = sizeof(nonces); + } +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE +#ifdef CONFIG_TESTING_OPTIONS + if (get_ie_ext(wpa_s->sme.assoc_req_ie, wpa_s->sme.assoc_req_ie_len, + WLAN_EID_EXT_OWE_DH_PARAM)) { + wpa_printf(MSG_INFO, "TESTING: Override OWE DH element"); + } else +#endif /* CONFIG_TESTING_OPTIONS */ + if (auth_type == WLAN_AUTH_OPEN && + wpa_s->key_mgmt == WPA_KEY_MGMT_OWE) { + struct wpabuf *owe_ie; + u16 group; + + if (wpa_s->current_ssid && wpa_s->current_ssid->owe_group) { + group = wpa_s->current_ssid->owe_group; + } else if (wpa_s->assoc_status_code == + WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) { + if (wpa_s->last_owe_group == 19) + group = 20; + else if (wpa_s->last_owe_group == 20) + group = 21; + else + group = OWE_DH_GROUP; + } else { + group = OWE_DH_GROUP; + } + + wpa_s->last_owe_group = group; + wpa_printf(MSG_DEBUG, "OWE: Try to use group %u", group); + owe_ie = owe_build_assoc_req(wpa_s->wpa, group); + if (!owe_ie) { + wpa_printf(MSG_ERROR, + "OWE: Failed to build IE for Association Request frame"); + return; + } + if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(owe_ie) > + sizeof(wpa_s->sme.assoc_req_ie)) { + wpa_printf(MSG_ERROR, + "OWE: Not enough buffer room for own Association Request frame elements"); + wpabuf_free(owe_ie); + return; + } + os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len, + wpabuf_head(owe_ie), wpabuf_len(owe_ie)); + wpa_s->sme.assoc_req_ie_len += wpabuf_len(owe_ie); + wpabuf_free(owe_ie); + } +#endif /* CONFIG_OWE */ + params.bssid = bssid; params.ssid = wpa_s->sme.ssid; params.ssid_len = wpa_s->sme.ssid_len; @@ -962,8 +1563,11 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, params.wpa_ie = wpa_s->sme.assoc_req_ie_len ? wpa_s->sme.assoc_req_ie : NULL; params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len; + wpa_hexdump(MSG_DEBUG, "SME: Association Request IEs", + params.wpa_ie, params.wpa_ie_len); params.pairwise_suite = wpa_s->pairwise_cipher; params.group_suite = wpa_s->group_cipher; + params.mgmt_group_suite = wpa_s->mgmt_group_cipher; params.key_mgmt_suite = wpa_s->key_mgmt; params.wpa_proto = wpa_s->wpa_proto; #ifdef CONFIG_HT_OVERRIDES @@ -981,9 +1585,85 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, 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) { + if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies && + get_ie(wpa_s->sme.ft_ies, wpa_s->sme.ft_ies_len, + WLAN_EID_RIC_DATA)) { + /* There seems to be a pretty inconvenient bug in the Linux + * kernel IE splitting functionality when RIC is used. For now, + * skip correct behavior in IE construction here (i.e., drop the + * additional non-FT-specific IEs) to avoid kernel issues. This + * is fine since RIC is used only for testing purposes in the + * current implementation. */ + wpa_printf(MSG_INFO, + "SME: Linux kernel workaround - do not try to include additional IEs with RIC"); params.wpa_ie = wpa_s->sme.ft_ies; params.wpa_ie_len = wpa_s->sme.ft_ies_len; + } else if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) { + const u8 *rm_en, *pos, *end; + size_t rm_en_len = 0; + u8 *rm_en_dup = NULL, *wpos; + + /* Remove RSNE, MDE, FTE to allow them to be overridden with + * FT specific values */ + remove_ie(wpa_s->sme.assoc_req_ie, + &wpa_s->sme.assoc_req_ie_len, + WLAN_EID_RSN); + remove_ie(wpa_s->sme.assoc_req_ie, + &wpa_s->sme.assoc_req_ie_len, + WLAN_EID_MOBILITY_DOMAIN); + remove_ie(wpa_s->sme.assoc_req_ie, + &wpa_s->sme.assoc_req_ie_len, + WLAN_EID_FAST_BSS_TRANSITION); + rm_en = get_ie(wpa_s->sme.assoc_req_ie, + wpa_s->sme.assoc_req_ie_len, + WLAN_EID_RRM_ENABLED_CAPABILITIES); + if (rm_en) { + /* Need to remove RM Enabled Capabilities element as + * well temporarily, so that it can be placed between + * RSNE and MDE. */ + rm_en_len = 2 + rm_en[1]; + rm_en_dup = os_memdup(rm_en, rm_en_len); + remove_ie(wpa_s->sme.assoc_req_ie, + &wpa_s->sme.assoc_req_ie_len, + WLAN_EID_RRM_ENABLED_CAPABILITIES); + } + wpa_hexdump(MSG_DEBUG, + "SME: Association Request IEs after FT IE removal", + wpa_s->sme.assoc_req_ie, + wpa_s->sme.assoc_req_ie_len); + if (wpa_s->sme.assoc_req_ie_len + wpa_s->sme.ft_ies_len + + rm_en_len > sizeof(wpa_s->sme.assoc_req_ie)) { + wpa_printf(MSG_ERROR, + "SME: Not enough buffer room for FT IEs in Association Request frame"); + os_free(rm_en_dup); + return; + } + + os_memmove(wpa_s->sme.assoc_req_ie + wpa_s->sme.ft_ies_len + + rm_en_len, + wpa_s->sme.assoc_req_ie, + wpa_s->sme.assoc_req_ie_len); + pos = wpa_s->sme.ft_ies; + end = pos + wpa_s->sme.ft_ies_len; + wpos = wpa_s->sme.assoc_req_ie; + if (*pos == WLAN_EID_RSN) { + os_memcpy(wpos, pos, 2 + pos[1]); + wpos += 2 + pos[1]; + pos += 2 + pos[1]; + } + if (rm_en_dup) { + os_memcpy(wpos, rm_en_dup, rm_en_len); + wpos += rm_en_len; + os_free(rm_en_dup); + } + os_memcpy(wpos, pos, end - pos); + wpa_s->sme.assoc_req_ie_len += wpa_s->sme.ft_ies_len + + rm_en_len; + params.wpa_ie = wpa_s->sme.assoc_req_ie; + params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len; + wpa_hexdump(MSG_DEBUG, + "SME: Association Request IEs after FT override", + params.wpa_ie, params.wpa_ie_len); } #endif /* CONFIG_IEEE80211R */ params.mode = mode; @@ -1038,6 +1718,14 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode, eloop_register_timeout(SME_ASSOC_TIMEOUT, 0, sme_assoc_timer, wpa_s, NULL); + +#ifdef CONFIG_TESTING_OPTIONS + wpabuf_free(wpa_s->last_assoc_req_wpa_ie); + wpa_s->last_assoc_req_wpa_ie = NULL; + if (params.wpa_ie) + wpa_s->last_assoc_req_wpa_ie = + wpabuf_alloc_copy(params.wpa_ie, params.wpa_ie_len); +#endif /* CONFIG_TESTING_OPTIONS */ } @@ -1056,10 +1744,9 @@ int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md, os_memcpy(wpa_s->sme.mobility_domain, md, MOBILITY_DOMAIN_ID_LEN); wpa_hexdump(MSG_DEBUG, "SME: FT IEs", ies, ies_len); os_free(wpa_s->sme.ft_ies); - wpa_s->sme.ft_ies = os_malloc(ies_len); + wpa_s->sme.ft_ies = os_memdup(ies, ies_len); if (wpa_s->sme.ft_ies == NULL) return -1; - os_memcpy(wpa_s->sme.ft_ies, ies, ies_len); wpa_s->sme.ft_ies_len = ies_len; return 0; } @@ -1226,7 +1913,7 @@ void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s) sae_clear_data(&wpa_s->sme.sae); #endif /* CONFIG_SAE */ #ifdef CONFIG_IEEE80211R - if (wpa_s->sme.ft_ies) + if (wpa_s->sme.ft_ies || wpa_s->sme.ft_used) sme_update_ft_ies(wpa_s, NULL, NULL, 0); #endif /* CONFIG_IEEE80211R */ } diff --git a/contrib/wpa/wpa_supplicant/sme.h b/contrib/wpa/wpa_supplicant/sme.h index fd5c3b4e1ed8..f3c822025574 100644 --- a/contrib/wpa/wpa_supplicant/sme.h +++ b/contrib/wpa/wpa_supplicant/sme.h @@ -38,6 +38,10 @@ void sme_deinit(struct wpa_supplicant *wpa_s); int sme_proc_obss_scan(struct wpa_supplicant *wpa_s); void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable); +void sme_external_auth_trigger(struct wpa_supplicant *wpa_s, + union wpa_event_data *data); +void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s, + const u8 *auth_frame, size_t len); #else /* CONFIG_SME */ @@ -113,6 +117,16 @@ static inline void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, { } +static inline void sme_external_auth_trigger(struct wpa_supplicant *wpa_s, + union wpa_event_data *data) +{ +} + +static inline void sme_external_auth_mgmt_rx(struct wpa_supplicant *wpa_s, + const u8 *auth_frame, size_t len) +{ +} + #endif /* CONFIG_SME */ #endif /* SME_H */ diff --git a/contrib/wpa/wpa_supplicant/wifi_display.c b/contrib/wpa/wpa_supplicant/wifi_display.c index c363b21b92b1..c94e4610893a 100644 --- a/contrib/wpa/wpa_supplicant/wifi_display.c +++ b/contrib/wpa/wpa_supplicant/wifi_display.c @@ -86,6 +86,7 @@ static int wifi_display_update_wfd_ie(struct wpa_global *global) p2p_set_wfd_ie_prov_disc_resp(global->p2p, NULL); p2p_set_wfd_ie_go_neg(global->p2p, NULL); p2p_set_wfd_dev_info(global->p2p, NULL); + p2p_set_wfd_r2_dev_info(global->p2p, NULL); p2p_set_wfd_assoc_bssid(global->p2p, NULL); p2p_set_wfd_coupled_sink_info(global->p2p, NULL); return 0; @@ -93,6 +94,8 @@ static int wifi_display_update_wfd_ie(struct wpa_global *global) p2p_set_wfd_dev_info(global->p2p, global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]); + p2p_set_wfd_r2_dev_info( + global->p2p, global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO]); p2p_set_wfd_assoc_bssid( global->p2p, global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]); @@ -133,6 +136,11 @@ static int wifi_display_update_wfd_ie(struct wpa_global *global) if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]) len += wpabuf_len(global->wfd_subelem[ WFD_SUBELEM_DEVICE_INFO]); + + if (global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO]) + len += wpabuf_len(global->wfd_subelem[ + WFD_SUBELEM_R2_DEVICE_INFO]); + if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]) len += wpabuf_len(global->wfd_subelem[ WFD_SUBELEM_ASSOCIATED_BSSID]); @@ -151,6 +159,11 @@ static int wifi_display_update_wfd_ie(struct wpa_global *global) if (global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]) wpabuf_put_buf(buf, global->wfd_subelem[WFD_SUBELEM_DEVICE_INFO]); + + if (global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO]) + wpabuf_put_buf(buf, + global->wfd_subelem[WFD_SUBELEM_R2_DEVICE_INFO]); + if (global->wfd_subelem[WFD_SUBELEM_ASSOCIATED_BSSID]) wpabuf_put_buf(buf, global->wfd_subelem[ WFD_SUBELEM_ASSOCIATED_BSSID]); diff --git a/contrib/wpa/wpa_supplicant/wmm_ac.c b/contrib/wpa/wpa_supplicant/wmm_ac.c index 5625d36638b5..a88cc46f3956 100644 --- a/contrib/wpa/wpa_supplicant/wmm_ac.c +++ b/contrib/wpa/wpa_supplicant/wmm_ac.c @@ -87,13 +87,10 @@ static int wmm_ac_add_ts(struct wpa_supplicant *wpa_s, const u8 *addr, } /* copy tspec */ - _tspec = os_malloc(sizeof(*_tspec)); + _tspec = os_memdup(tspec, 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, diff --git a/contrib/wpa/wpa_supplicant/wnm_sta.c b/contrib/wpa/wpa_supplicant/wnm_sta.c index 67a07ff7b1e7..6b68fc9e3772 100644 --- a/contrib/wpa/wpa_supplicant/wnm_sta.c +++ b/contrib/wpa/wpa_supplicant/wnm_sta.c @@ -13,6 +13,7 @@ #include "common/ieee802_11_common.h" #include "common/wpa_ctrl.h" #include "rsn_supp/wpa.h" +#include "config.h" #include "wpa_supplicant_i.h" #include "driver_i.h" #include "scan.h" @@ -84,12 +85,11 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, /* TFS IE(s) */ if (tfs_req) { wnmtfs_ie_len = wpabuf_len(tfs_req); - wnmtfs_ie = os_malloc(wnmtfs_ie_len); + wnmtfs_ie = os_memdup(wpabuf_head(tfs_req), wnmtfs_ie_len); if (wnmtfs_ie == NULL) { os_free(wnmsleep_ie); return -1; } - os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len); } else { wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); if (wnmtfs_ie == NULL) { @@ -338,6 +338,9 @@ void wnm_deallocate_memory(struct wpa_supplicant *wpa_s) wpa_s->wnm_num_neighbor_report = 0; os_free(wpa_s->wnm_neighbor_report_elements); wpa_s->wnm_neighbor_report_elements = NULL; + + wpabuf_free(wpa_s->coloc_intf_elems); + wpa_s->coloc_intf_elems = NULL; } @@ -501,10 +504,128 @@ static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s, } +static void wnm_clear_acceptable(struct wpa_supplicant *wpa_s) +{ + unsigned int i; + + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) + wpa_s->wnm_neighbor_report_elements[i].acceptable = 0; +} + + +static struct wpa_bss * get_first_acceptable(struct wpa_supplicant *wpa_s) +{ + unsigned int i; + struct neighbor_report *nei; + + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { + nei = &wpa_s->wnm_neighbor_report_elements[i]; + if (nei->acceptable) + return wpa_bss_get_bssid(wpa_s, nei->bssid); + } + + return NULL; +} + + +#ifdef CONFIG_MBO static struct wpa_bss * -compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs) +get_mbo_transition_candidate(struct wpa_supplicant *wpa_s, + enum mbo_transition_reject_reason *reason) { + struct wpa_bss *target = NULL; + struct wpa_bss_trans_info params; + struct wpa_bss_candidate_info *info = NULL; + struct neighbor_report *nei = wpa_s->wnm_neighbor_report_elements; + u8 *first_candidate_bssid = NULL, *pos; + unsigned int i; + + params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason; + params.n_candidates = 0; + params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN); + if (!params.bssid) + return NULL; + + pos = params.bssid; + for (i = 0; i < wpa_s->wnm_num_neighbor_report; nei++, i++) { + if (nei->is_first) + first_candidate_bssid = nei->bssid; + if (!nei->acceptable) + continue; + os_memcpy(pos, nei->bssid, ETH_ALEN); + pos += ETH_ALEN; + params.n_candidates++; + } + + if (!params.n_candidates) + goto end; + + info = wpa_drv_get_bss_trans_status(wpa_s, ¶ms); + if (!info) { + /* If failed to get candidate BSS transition status from driver, + * get the first acceptable candidate from wpa_supplicant. + */ + target = wpa_bss_get_bssid(wpa_s, params.bssid); + goto end; + } + + /* Get the first acceptable candidate from driver */ + for (i = 0; i < info->num; i++) { + if (info->candidates[i].is_accept) { + target = wpa_bss_get_bssid(wpa_s, + info->candidates[i].bssid); + goto end; + } + } + + /* If Disassociation Imminent is set and driver rejects all the + * candidate select first acceptable candidate which has + * rssi > disassoc_imminent_rssi_threshold + */ + if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { + for (i = 0; i < info->num; i++) { + target = wpa_bss_get_bssid(wpa_s, + info->candidates[i].bssid); + if (target && + (target->level < + wpa_s->conf->disassoc_imminent_rssi_threshold)) + continue; + goto end; + } + } + + /* While sending BTM reject use reason code of the first candidate + * received in BTM request frame + */ + if (reason) { + for (i = 0; i < info->num; i++) { + if (first_candidate_bssid && + os_memcmp(first_candidate_bssid, + info->candidates[i].bssid, ETH_ALEN) == 0) + { + *reason = info->candidates[i].reject_reason; + break; + } + } + } + + target = NULL; + +end: + os_free(params.bssid); + if (info) { + os_free(info->candidates); + os_free(info); + } + return target; +} +#endif /* CONFIG_MBO */ + +static struct wpa_bss * +compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs, + enum mbo_transition_reject_reason *reason) +{ u8 i; struct wpa_bss *bss = wpa_s->current_bss; struct wpa_bss *target; @@ -515,6 +636,8 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs) wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d", MAC2STR(wpa_s->bssid), bss->level); + wnm_clear_acceptable(wpa_s); + for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { struct neighbor_report *nei; @@ -564,7 +687,7 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs) if (wpa_s->current_ssid && !wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid, - 1)) { + 1, 0)) { wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR " (pref %d) does not match the current network profile", MAC2STR(nei->bssid), @@ -591,14 +714,26 @@ compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs) continue; } + nei->acceptable = 1; + } + +#ifdef CONFIG_MBO + if (wpa_s->wnm_mbo_trans_reason_present) + target = get_mbo_transition_candidate(wpa_s, reason); + else + target = get_first_acceptable(wpa_s); +#else /* CONFIG_MBO */ + target = get_first_acceptable(wpa_s); +#endif /* CONFIG_MBO */ + + if (target) { wpa_printf(MSG_DEBUG, "WNM: Found an acceptable preferred transition candidate BSS " MACSTR " (RSSI %d)", - MAC2STR(nei->bssid), target->level); - return target; + MAC2STR(target->bssid), target->level); } - return NULL; + return target; } @@ -651,36 +786,40 @@ static u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) } -static int wnm_add_nei_rep(u8 *buf, size_t len, const u8 *bssid, u32 bss_info, - u8 op_class, u8 chan, u8 phy_type, u8 pref) +static int wnm_add_nei_rep(struct wpabuf **buf, const u8 *bssid, + u32 bss_info, u8 op_class, u8 chan, u8 phy_type, + u8 pref) { - u8 *pos = buf; + if (wpabuf_len(*buf) + 18 > + IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN) { + wpa_printf(MSG_DEBUG, + "WNM: No room in frame for Neighbor Report element"); + return -1; + } - if (len < 18) { + if (wpabuf_resize(buf, 18) < 0) { wpa_printf(MSG_DEBUG, - "WNM: Not enough room for Neighbor Report element"); + "WNM: Failed to allocate memory for Neighbor Report element"); return -1; } - *pos++ = WLAN_EID_NEIGHBOR_REPORT; + wpabuf_put_u8(*buf, WLAN_EID_NEIGHBOR_REPORT); /* length: 13 for basic neighbor report + 3 for preference subelement */ - *pos++ = 16; - os_memcpy(pos, bssid, ETH_ALEN); - pos += ETH_ALEN; - WPA_PUT_LE32(pos, bss_info); - pos += 4; - *pos++ = op_class; - *pos++ = chan; - *pos++ = phy_type; - *pos++ = WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE; - *pos++ = 1; - *pos++ = pref; - return pos - buf; + wpabuf_put_u8(*buf, 16); + wpabuf_put_data(*buf, bssid, ETH_ALEN); + wpabuf_put_le32(*buf, bss_info); + wpabuf_put_u8(*buf, op_class); + wpabuf_put_u8(*buf, chan); + wpabuf_put_u8(*buf, phy_type); + wpabuf_put_u8(*buf, WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE); + wpabuf_put_u8(*buf, 1); + wpabuf_put_u8(*buf, pref); + return 0; } static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss, u8 *buf, size_t len, + struct wpa_bss *bss, struct wpabuf **buf, u8 pref) { const u8 *ie; @@ -729,20 +868,19 @@ static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s, info = wnm_get_bss_info(wpa_s, bss); - return wnm_add_nei_rep(buf, len, bss->bssid, info, op_class, chan, - phy_type, pref); + return wnm_add_nei_rep(buf, bss->bssid, info, op_class, chan, phy_type, + pref); } -static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len) +static void wnm_add_cand_list(struct wpa_supplicant *wpa_s, struct wpabuf **buf) { - u8 *pos = buf; unsigned int i, pref = 255; struct os_reltime now; struct wpa_ssid *ssid = wpa_s->current_ssid; if (!ssid) - return 0; + return; /* * TODO: Define when scan results are no longer valid for the candidate @@ -750,7 +888,7 @@ static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len) */ os_get_reltime(&now); if (os_reltime_expired(&now, &wpa_s->last_scan, 10)) - return 0; + return; wpa_printf(MSG_DEBUG, "WNM: Add candidate list to BSS Transition Management Response frame"); @@ -758,93 +896,100 @@ static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len) struct wpa_bss *bss = wpa_s->last_scan_res[i]; int res; - if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1)) { - res = wnm_nei_rep_add_bss(wpa_s, bss, pos, len, pref--); + if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0)) { + res = wnm_nei_rep_add_bss(wpa_s, bss, buf, pref--); if (res == -2) continue; /* could not build entry for BSS */ if (res < 0) break; /* no more room for candidates */ if (pref == 1) break; - - pos += res; - len -= res; } } - wpa_hexdump(MSG_DEBUG, - "WNM: BSS Transition Management Response candidate list", - buf, pos - buf); - - return pos - buf; + wpa_hexdump_buf(MSG_DEBUG, + "WNM: BSS Transition Management Response candidate list", + *buf); } +#define BTM_RESP_MIN_SIZE 5 + ETH_ALEN + 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) + enum bss_trans_mgmt_status_code status, + enum mbo_transition_reject_reason reason, + u8 delay, const u8 *target_bssid) { - u8 buf[2000], *pos; - struct ieee80211_mgmt *mgmt; - size_t len; + struct wpabuf *buf; 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); + wpa_printf(MSG_DEBUG, + "WNM: Send BSS Transition Management Response to " MACSTR + " dialog_token=%u status=%u reason=%u delay=%d", + MAC2STR(wpa_s->bssid), dialog_token, status, reason, 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)); - 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_resp.action = WNM_BSS_TRANS_MGMT_RESP; - mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token; - mgmt->u.action.u.bss_tm_resp.status_code = status; - mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay; - pos = mgmt->u.action.u.bss_tm_resp.variable; + buf = wpabuf_alloc(BTM_RESP_MIN_SIZE); + if (!buf) { + wpa_printf(MSG_DEBUG, + "WNM: Failed to allocate memory for BTM response"); + return; + } + + wpabuf_put_u8(buf, WLAN_ACTION_WNM); + wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP); + wpabuf_put_u8(buf, dialog_token); + wpabuf_put_u8(buf, status); + wpabuf_put_u8(buf, delay); if (target_bssid) { - os_memcpy(pos, target_bssid, ETH_ALEN); - pos += ETH_ALEN; + wpabuf_put_data(buf, target_bssid, 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; + wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN); } if (status == WNM_BSS_TM_ACCEPT) - pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos); + wnm_add_cand_list(wpa_s, &buf); #ifdef CONFIG_MBO - if (status != WNM_BSS_TM_ACCEPT) { - pos += wpas_mbo_ie_bss_trans_reject( - wpa_s, pos, buf + sizeof(buf) - pos, - MBO_TRANSITION_REJECT_REASON_UNSPECIFIED); + if (status != WNM_BSS_TM_ACCEPT && + wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) { + u8 mbo[10]; + size_t ret; + + ret = wpas_mbo_ie_bss_trans_reject(wpa_s, mbo, sizeof(mbo), + reason); + if (ret) { + if (wpabuf_resize(&buf, ret) < 0) { + wpabuf_free(buf); + wpa_printf(MSG_DEBUG, + "WNM: Failed to allocate memory for MBO IE"); + return; + } + + wpabuf_put_data(buf, mbo, ret); + } } #endif /* CONFIG_MBO */ - len = pos - (u8 *) &mgmt->u.action.category; - 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); + wpabuf_head_u8(buf), wpabuf_len(buf), 0); if (res < 0) { wpa_printf(MSG_DEBUG, "WNM: Failed to send BSS Transition Management Response"); } + + wpabuf_free(buf); } @@ -863,10 +1008,10 @@ static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s, wpa_s->wnm_reply = 0; wpa_printf(MSG_DEBUG, "WNM: Sending successful BSS Transition Management Response"); - wnm_send_bss_transition_mgmt_resp(wpa_s, - wpa_s->wnm_dialog_token, - WNM_BSS_TM_ACCEPT, - 0, bss->bssid); + wnm_send_bss_transition_mgmt_resp( + wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT, + MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, + bss->bssid); } if (bss == wpa_s->current_bss) { @@ -888,6 +1033,8 @@ 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; + enum mbo_transition_reject_reason reason = + MBO_TRANSITION_REJECT_REASON_UNSPECIFIED; if (!wpa_s->wnm_neighbor_report_elements) return 0; @@ -909,7 +1056,7 @@ int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail) } /* Compare the Neighbor Report and scan results */ - bss = compare_scan_neighbor_results(wpa_s, 0); + bss = compare_scan_neighbor_results(wpa_s, 0, &reason); if (!bss) { wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found"); status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES; @@ -930,7 +1077,7 @@ send_bss_resp_fail: wpa_s->wnm_reply = 0; wnm_send_bss_transition_mgmt_resp(wpa_s, wpa_s->wnm_dialog_token, - status, 0, NULL); + status, reason, 0, NULL); } wnm_deallocate_memory(wpa_s); @@ -1118,7 +1265,7 @@ static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s) return 0; } - bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE); + bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE, NULL); if (!bss) { wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Comparison of scan results against transition candidates did not find matches"); @@ -1144,6 +1291,11 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, if (end - pos < 5) return; +#ifdef CONFIG_MBO + wpa_s->wnm_mbo_trans_reason_present = 0; + wpa_s->wnm_mbo_transition_reason = 0; +#endif /* CONFIG_MBO */ + if (wpa_s->current_bss) beacon_int = wpa_s->current_bss->beacon_int; else @@ -1166,10 +1318,10 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_printf(MSG_INFO, "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d", wpa_s->reject_btm_req_reason); - wnm_send_bss_transition_mgmt_resp(wpa_s, - wpa_s->wnm_dialog_token, - wpa_s->reject_btm_req_reason, - 0, NULL); + wnm_send_bss_transition_mgmt_resp( + wpa_s, wpa_s->wnm_dialog_token, + wpa_s->reject_btm_req_reason, + MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL); return; } #endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */ @@ -1248,6 +1400,15 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wpa_s->wnm_num_neighbor_report]; wnm_parse_neighbor_report(wpa_s, pos, len, rep); wpa_s->wnm_num_neighbor_report++; +#ifdef CONFIG_MBO + if (wpa_s->wnm_mbo_trans_reason_present && + wpa_s->wnm_num_neighbor_report == 1) { + rep->is_first = 1; + wpa_printf(MSG_DEBUG, + "WNM: First transition candidate is " + MACSTR, MAC2STR(rep->bssid)); + } +#endif /* CONFIG_MBO */ } pos += len; @@ -1259,7 +1420,8 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, wnm_send_bss_transition_mgmt_resp( wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES, - 0, NULL); + MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, + NULL); return; } @@ -1322,19 +1484,21 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, 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); + wnm_send_bss_transition_mgmt_resp( + wpa_s, wpa_s->wnm_dialog_token, status, + MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL); } } +#define BTM_QUERY_MIN_SIZE 4 + int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, - u8 query_reason, int cand_list) + u8 query_reason, + const char *btm_candidates, + int cand_list) { - u8 buf[2000], *pos; - struct ieee80211_mgmt *mgmt; - size_t len; + struct wpabuf *buf; int ret; wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to " @@ -1342,28 +1506,43 @@ int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, MAC2STR(wpa_s->bssid), query_reason, cand_list ? " candidate list" : ""); - 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; + buf = wpabuf_alloc(BTM_QUERY_MIN_SIZE); + if (!buf) + return -1; + + wpabuf_put_u8(buf, WLAN_ACTION_WNM); + wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_QUERY); + wpabuf_put_u8(buf, 1); + wpabuf_put_u8(buf, query_reason); if (cand_list) - pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos); + wnm_add_cand_list(wpa_s, &buf); + + if (btm_candidates) { + const size_t max_len = 1000; + + ret = wpabuf_resize(&buf, max_len); + if (ret < 0) { + wpabuf_free(buf); + return ret; + } + + ret = ieee802_11_parse_candidate_list(btm_candidates, + wpabuf_put(buf, 0), + max_len); + if (ret < 0) { + wpabuf_free(buf); + return ret; + } - len = pos - (u8 *) &mgmt->u.action.category; + wpabuf_put(buf, ret); + } 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); + wpabuf_head_u8(buf), wpabuf_len(buf), 0); + wpabuf_free(buf); return ret; } @@ -1468,6 +1647,32 @@ static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s, pos = next; continue; } + + if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 && + WPA_GET_BE24(pos) == OUI_WFA && + pos[3] == HS20_WNM_T_C_ACCEPTANCE) { + const u8 *ie_end; + u8 url_len; + char *url; + + ie_end = pos + ie_len; + pos += 4; + url_len = *pos++; + wpa_printf(MSG_DEBUG, + "WNM: HS 2.0 Terms and Conditions Acceptance (URL Length %u)", + url_len); + if (url_len > ie_end - pos) + break; + url = os_malloc(url_len + 1); + if (!url) + break; + os_memcpy(url, pos, url_len); + url[url_len] = '\0'; + hs20_rx_t_c_acceptance(wpa_s, url); + os_free(url); + pos = next; + continue; + } #endif /* CONFIG_HS20 */ pos = next; @@ -1515,6 +1720,46 @@ static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s, } +static void ieee802_11_rx_wnm_coloc_intf_req(struct wpa_supplicant *wpa_s, + const u8 *sa, const u8 *frm, + int len) +{ + u8 dialog_token, req_info, auto_report, timeout; + + if (!wpa_s->conf->coloc_intf_reporting) + return; + + /* Dialog Token [1] | Request Info [1] */ + + if (len < 2) + return; + dialog_token = frm[0]; + req_info = frm[1]; + auto_report = req_info & 0x03; + timeout = req_info >> 2; + + wpa_dbg(wpa_s, MSG_DEBUG, + "WNM: Received Collocated Interference Request (dialog_token %u auto_report %u timeout %u sa " MACSTR ")", + dialog_token, auto_report, timeout, MAC2STR(sa)); + + if (dialog_token == 0) + return; /* only nonzero values are used for request */ + + if (wpa_s->wpa_state != WPA_COMPLETED || + os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) { + wpa_dbg(wpa_s, MSG_DEBUG, + "WNM: Collocated Interference Request frame not from current AP - ignore it"); + return; + } + + wpa_msg(wpa_s, MSG_INFO, COLOC_INTF_REQ "%u %u %u", + dialog_token, auto_report, timeout); + wpa_s->coloc_intf_dialog_token = dialog_token; + wpa_s->coloc_intf_auto_report = auto_report; + wpa_s->coloc_intf_timeout = timeout; +} + + void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, const struct ieee80211_mgmt *mgmt, size_t len) { @@ -1548,8 +1793,75 @@ void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, case WNM_NOTIFICATION_REQ: ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos); break; + case WNM_COLLOCATED_INTERFERENCE_REQ: + ieee802_11_rx_wnm_coloc_intf_req(wpa_s, mgmt->sa, pos, + end - pos); + break; default: wpa_printf(MSG_ERROR, "WNM: Unknown request"); break; } } + + +int wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token, + const struct wpabuf *elems) +{ + struct wpabuf *buf; + int ret; + + if (wpa_s->wpa_state < WPA_ASSOCIATED || !elems) + return -1; + + wpa_printf(MSG_DEBUG, "WNM: Send Collocated Interference Report to " + MACSTR " (dialog token %u)", + MAC2STR(wpa_s->bssid), dialog_token); + + buf = wpabuf_alloc(3 + wpabuf_len(elems)); + if (!buf) + return -1; + + wpabuf_put_u8(buf, WLAN_ACTION_WNM); + wpabuf_put_u8(buf, WNM_COLLOCATED_INTERFERENCE_REPORT); + wpabuf_put_u8(buf, dialog_token); + wpabuf_put_buf(buf, elems); + + ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head_u8(buf), wpabuf_len(buf), 0); + wpabuf_free(buf); + return ret; +} + + +void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s, + struct wpabuf *elems) +{ + wpabuf_free(wpa_s->coloc_intf_elems); + if (elems && wpabuf_len(elems) == 0) { + wpabuf_free(elems); + elems = NULL; + } + wpa_s->coloc_intf_elems = elems; + + if (wpa_s->conf->coloc_intf_reporting && wpa_s->coloc_intf_elems && + wpa_s->coloc_intf_dialog_token && + (wpa_s->coloc_intf_auto_report == 1 || + wpa_s->coloc_intf_auto_report == 3)) { + /* TODO: Check that there has not been less than + * wpa_s->coloc_intf_timeout * 200 TU from the last report. + */ + wnm_send_coloc_intf_report(wpa_s, + wpa_s->coloc_intf_dialog_token, + wpa_s->coloc_intf_elems); + } +} + + +void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_WNM + wpa_s->coloc_intf_dialog_token = 0; + wpa_s->coloc_intf_auto_report = 0; +#endif /* CONFIG_WNM */ +} diff --git a/contrib/wpa/wpa_supplicant/wnm_sta.h b/contrib/wpa/wpa_supplicant/wnm_sta.h index 81d815359634..29625f8ca943 100644 --- a/contrib/wpa/wpa_supplicant/wnm_sta.h +++ b/contrib/wpa/wpa_supplicant/wnm_sta.h @@ -43,6 +43,10 @@ struct neighbor_report { unsigned int rm_capab_present:1; unsigned int bearing_present:1; unsigned int bss_term_present:1; + unsigned int acceptable:1; +#ifdef CONFIG_MBO + unsigned int is_first:1; +#endif /* CONFIG_MBO */ struct measurement_pilot *meas_pilot; struct multiple_bssid *mul_bssid; int freq; @@ -56,13 +60,21 @@ void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, const struct ieee80211_mgmt *mgmt, size_t len); int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, - u8 query_reason, int cand_list); + u8 query_reason, + const char *btm_candidates, + int cand_list); + void wnm_deallocate_memory(struct wpa_supplicant *wpa_s); +int wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token, + const struct wpabuf *elems); +void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s, + struct wpabuf *elems); #ifdef CONFIG_WNM int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail); +void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s); #else /* CONFIG_WNM */ @@ -72,6 +84,10 @@ static inline int wnm_scan_process(struct wpa_supplicant *wpa_s, return 0; } +static inline void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s) +{ +} + #endif /* CONFIG_WNM */ #endif /* WNM_STA_H */ diff --git a/contrib/wpa/wpa_supplicant/wpa_cli.c b/contrib/wpa/wpa_supplicant/wpa_cli.c index a848b7737db5..779355440a01 100644 --- a/contrib/wpa/wpa_supplicant/wpa_cli.c +++ b/contrib/wpa/wpa_supplicant/wpa_cli.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - command line interface for wpa_supplicant daemon - * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -29,7 +29,7 @@ static const char *const wpa_cli_version = "wpa_cli v" VERSION_STR "\n" -"Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> and contributors"; +"Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi> and contributors"; #define VENDOR_ELEM_FRAME_ID \ " 0: Probe Req (P2P), 1: Probe Resp (P2P) , 2: Probe Resp (GO), " \ @@ -60,6 +60,10 @@ 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 DEFINE_DL_LIST(networks); /* struct cli_txt_entry */ +static DEFINE_DL_LIST(creds); /* struct cli_txt_entry */ +#ifdef CONFIG_AP +static DEFINE_DL_LIST(stations); /* struct cli_txt_entry */ +#endif /* CONFIG_AP */ static void print_help(const char *cmd); @@ -67,7 +71,9 @@ static void wpa_cli_mon_receive(int sock, void *eloop_ctx, void *sock_ctx); static void wpa_cli_close_connection(void); static char * wpa_cli_get_default_ifname(void); static char ** wpa_list_cmd_list(void); +static void update_creds(struct wpa_ctrl *ctrl); static void update_networks(struct wpa_ctrl *ctrl); +static void update_stations(struct wpa_ctrl *ctrl); static void usage(void) @@ -214,7 +220,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) +static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print) { char buf[4096]; size_t len; @@ -250,7 +256,7 @@ static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print) } -static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd) +static int wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd) { return _wpa_ctrl_command(ctrl, cmd, 1); } @@ -331,6 +337,39 @@ static int wpa_cli_cmd_pmksa_flush(struct wpa_ctrl *ctrl, int argc, } +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL + +static int wpa_cli_cmd_pmksa_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "PMKSA_GET", 1, argc, argv); +} + + +static int wpa_cli_cmd_pmksa_add(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_cli_cmd(ctrl, "PMKSA_ADD", 8, argc, argv); +} + + +#ifdef CONFIG_MESH + +static int wpa_cli_mesh_cmd_pmksa_get(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "MESH_PMKSA_GET", 1, argc, argv); +} + + +static int wpa_cli_mesh_cmd_pmksa_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "MESH_PMKSA_ADD", 4, argc, argv); +} + +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ + + static int wpa_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) { print_help(argc > 0 ? argv[0] : NULL); @@ -437,11 +476,13 @@ static char ** wpa_cli_complete_set(const char *str, int pos) #endif /* CONFIG_P2P */ "country", "bss_max_count", "bss_expiration_age", "bss_expiration_scan_count", "filter_ssids", "filter_rssi", - "max_num_sta", "disassoc_low_ack", + "max_num_sta", "disassoc_low_ack", "ap_isolate", #ifdef CONFIG_HS20 "hs20", #endif /* CONFIG_HS20 */ "interworking", "hessid", "access_network_type", "pbc_in_m1", + "go_interworking", "go_access_network_type", "go_internet", + "go_venue_group", "go_venue_type", "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", @@ -455,6 +496,7 @@ static char ** wpa_cli_complete_set(const char *str, int pos) #ifdef CONFIG_TESTING_OPTIONS "ignore_auth_resp", #endif /* CONFIG_TESTING_OPTIONS */ + "relative_rssi", "relative_band_adjust", }; int i, num_fields = ARRAY_SIZE(fields); @@ -531,15 +573,18 @@ static char ** wpa_cli_complete_get(const char *str, int pos) #endif /* CONFIG_P2P */ "bss_max_count", "bss_expiration_age", "bss_expiration_scan_count", "filter_ssids", "filter_rssi", - "max_num_sta", "disassoc_low_ack", + "max_num_sta", "disassoc_low_ack", "ap_isolate", #ifdef CONFIG_HS20 "hs20", #endif /* CONFIG_HS20 */ "interworking", "access_network_type", "pbc_in_m1", "autoscan", + "go_interworking", "go_access_network_type", "go_internet", + "go_venue_group", "go_venue_type", "wps_nfc_dev_pw_id", "ext_password_backend", "p2p_go_max_inactivity", "auto_interworking", "okc", "pmf", "dtim_period", "beacon_int", "ignore_old_scan_res", "scan_cur_freq", "sched_scan_interval", + "sched_scan_start_delay", "tdls_external_control", "osu_dir", "wowlan_triggers", "p2p_search_delay", "mac_addr", "rand_addr_lifetime", "preassoc_mac_addr", "key_mgmt_offload", "passive_scan", @@ -639,13 +684,6 @@ static int wpa_cli_cmd_bss_flush(struct wpa_ctrl *ctrl, int argc, char *argv[]) } -static int wpa_cli_cmd_stkstart(struct wpa_ctrl *ctrl, int argc, - char *argv[]) -{ - return wpa_cli_cmd(ctrl, "STKSTART", 1, argc, argv); -} - - static int wpa_cli_cmd_ft_ds(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_cli_cmd(ctrl, "FT_DS", 1, argc, argv); @@ -1332,7 +1370,8 @@ static const char *network_fields[] = { "ssid", "scan_ssid", "bssid", "bssid_blacklist", "bssid_whitelist", "psk", "proto", "key_mgmt", "bg_scan_period", "pairwise", "group", "auth_alg", "scan_freq", - "freq_list", "max_oper_chwidth", + "freq_list", "max_oper_chwidth", "ht40", "vht", "vht_center_freq1", + "vht_center_freq2", "ht", #ifdef IEEE8021X_EAPOL "eap", "identity", "anonymous_identity", "password", "ca_cert", "ca_path", "client_cert", "private_key", "private_key_passwd", @@ -1352,7 +1391,7 @@ static const char *network_fields[] = { "eap_workaround", "pac_file", "fragment_size", "ocsp", #endif /* IEEE8021X_EAPOL */ #ifdef CONFIG_MESH - "mode", "no_auto_peer", + "mode", "no_auto_peer", "mesh_rssi_threshold", #else /* CONFIG_MESH */ "mode", #endif /* CONFIG_MESH */ @@ -1360,7 +1399,7 @@ static const char *network_fields[] = { #ifdef CONFIG_IEEE80211W "ieee80211w", #endif /* CONFIG_IEEE80211W */ - "peerkey", "mixed_cell", "frequency", "fixed_freq", + "mixed_cell", "frequency", "fixed_freq", #ifdef CONFIG_MESH "mesh_basic_rates", "dot11MeshMaxRetries", "dot11MeshRetryTimeout", "dot11MeshConfirmTimeout", @@ -1386,6 +1425,9 @@ static const char *network_fields[] = { "ap_max_inactivity", "dtim_period", "beacon_int", #ifdef CONFIG_MACSEC "macsec_policy", + "macsec_integ_only", + "macsec_port", + "mka_priority", #endif /* CONFIG_MACSEC */ #ifdef CONFIG_HS20 "update_identifier", @@ -1479,14 +1521,56 @@ static int wpa_cli_cmd_list_creds(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_add_cred(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - return wpa_ctrl_command(ctrl, "ADD_CRED"); + int res = wpa_ctrl_command(ctrl, "ADD_CRED"); + if (interactive) + update_creds(ctrl); + return res; } static int wpa_cli_cmd_remove_cred(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - return wpa_cli_cmd(ctrl, "REMOVE_CRED", 1, argc, argv); + int res = wpa_cli_cmd(ctrl, "REMOVE_CRED", 1, argc, argv); + if (interactive) + update_creds(ctrl); + return res; +} + + +static const char * const cred_fields[] = { + "temporary", "priority", "sp_priority", "pcsc", "eap", + "update_identifier", "min_dl_bandwidth_home", "min_ul_bandwidth_home", + "min_dl_bandwidth_roaming", "min_ul_bandwidth_roaming", "max_bss_load", + "req_conn_capab", "ocsp", "sim_num", "realm", "username", "password", + "ca_cert", "client_cert", "private_key", "private_key_passwd", "imsi", + "milenage", "domain_suffix_match", "domain", "phase1", "phase2", + "roaming_consortium", "required_roaming_consortium", "excluded_ssid", + "roaming_partner", "provisioning_sp" +}; + + +static char ** wpa_cli_complete_cred(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + int i, num_fields = ARRAY_SIZE(cred_fields); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&creds); + break; + case 2: + res = os_calloc(num_fields + 1, sizeof(char *)); + if (res == NULL) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(cred_fields[i]); + if (res[i] == NULL) + break; + } + } + return res; } @@ -1736,8 +1820,23 @@ static int wpa_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) } -static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, - char *addr, size_t addr_len) +static char ** wpa_cli_complete_sta(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&stations); + break; + } + + return res; +} + + +static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd, + char *addr, size_t addr_len, int print) { char buf[4096], *pos; size_t len; @@ -1765,9 +1864,11 @@ static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, } buf[len] = '\0'; - if (os_memcmp(buf, "FAIL", 4) == 0) + if (os_memcmp(buf, "FAIL", 4) == 0 || + os_memcmp(buf, "UNKNOWN COMMAND", 15) == 0) return -1; - printf("%s", buf); + if (print) + printf("%s", buf); pos = buf; while (*pos != '\0' && *pos != '\n') @@ -1782,16 +1883,33 @@ static int wpa_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char addr[32], cmd[64]; - if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr))) + if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 1)) return 0; do { os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); - } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0); + } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 1) == 0); return -1; } +static int wpa_cli_cmd_list_sta(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char addr[32], cmd[64]; + + if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0)) + return 0; + do { + if (os_strcmp(addr, "") != 0) + printf("%s\n", addr); + os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); + } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0); + + return 0; +} + + static int wpa_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1799,12 +1917,43 @@ static int wpa_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc, } +static char ** wpa_cli_complete_deauthenticate(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&stations); + break; + } + + return res; +} + + static int wpa_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc, char *argv[]) { return wpa_cli_cmd(ctrl, "DISASSOCIATE", 1, argc, argv); } + +static char ** wpa_cli_complete_disassociate(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&stations); + break; + } + + return res; +} + + static int wpa_cli_cmd_chanswitch(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -2176,7 +2325,7 @@ static char ** wpa_cli_complete_p2p_peer(const char *str, int pos) } -static int wpa_ctrl_command_p2p_peer(struct wpa_ctrl *ctrl, char *cmd, +static int wpa_ctrl_command_p2p_peer(struct wpa_ctrl *ctrl, const char *cmd, char *addr, size_t addr_len, int discovered) { @@ -2338,6 +2487,8 @@ static int wpa_cli_cmd_p2p_remove_client(struct wpa_ctrl *ctrl, int argc, return wpa_cli_cmd(ctrl, "P2P_REMOVE_CLIENT", 1, argc, argv); } +#endif /* CONFIG_P2P */ + static int wpa_cli_cmd_vendor_elem_add(struct wpa_ctrl *ctrl, int argc, char *argv[]) @@ -2359,7 +2510,6 @@ static int wpa_cli_cmd_vendor_elem_remove(struct wpa_ctrl *ctrl, int argc, return wpa_cli_cmd(ctrl, "VENDOR_ELEM_REMOVE", 2, argc, argv); } -#endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY @@ -2726,6 +2876,101 @@ static int wpa_cli_cmd_p2p_lo_stop(struct wpa_ctrl *ctrl, int argc, } +#ifdef CONFIG_DPP + +static int wpa_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DPP_QR_CODE", 1, argc, argv); +} + + +static int wpa_cli_cmd_dpp_bootstrap_gen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_GEN", 1, argc, argv); +} + + +static int wpa_cli_cmd_dpp_bootstrap_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_REMOVE", 1, argc, argv); +} + + +static int wpa_cli_cmd_dpp_bootstrap_get_uri(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_GET_URI", 1, argc, argv); +} + + +static int wpa_cli_cmd_dpp_bootstrap_info(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DPP_BOOTSTRAP_INFO", 1, argc, argv); +} + + +static int wpa_cli_cmd_dpp_auth_init(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DPP_AUTH_INIT", 1, argc, argv); +} + + +static int wpa_cli_cmd_dpp_listen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DPP_LISTEN", 1, argc, argv); +} + + +static int wpa_cli_cmd_dpp_stop_listen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DPP_STOP_LISTEN"); +} + + +static int wpa_cli_cmd_dpp_configurator_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DPP_CONFIGURATOR_ADD", 0, argc, argv); +} + + +static int wpa_cli_cmd_dpp_configurator_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DPP_CONFIGURATOR_REMOVE", 1, argc, argv); +} + + +static int wpa_cli_cmd_dpp_configurator_get_key(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DPP_CONFIGURATOR_GET_KEY", 1, argc, argv); +} + + +static int wpa_cli_cmd_dpp_pkex_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DPP_PKEX_ADD", 1, argc, argv); +} + + +static int wpa_cli_cmd_dpp_pkex_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv); +} + +#endif /* CONFIG_DPP */ + + enum wpa_cli_cmd_flags { cli_cmd_flag_none = 0x00, cli_cmd_flag_sensitive = 0x01 @@ -2798,6 +3043,22 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "pmksa_flush", wpa_cli_cmd_pmksa_flush, NULL, cli_cmd_flag_none, "= flush PMKSA cache entries" }, +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL + { "pmksa_get", wpa_cli_cmd_pmksa_get, NULL, + cli_cmd_flag_none, + "<network_id> = fetch all stored PMKSA cache entries" }, + { "pmksa_add", wpa_cli_cmd_pmksa_add, NULL, + cli_cmd_flag_sensitive, + "<network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> = store PMKSA cache entry from external storage" }, +#ifdef CONFIG_MESH + { "mesh_pmksa_get", wpa_cli_mesh_cmd_pmksa_get, NULL, + cli_cmd_flag_none, + "<peer MAC address | any> = fetch all stored mesh PMKSA cache entries" }, + { "mesh_pmksa_add", wpa_cli_mesh_cmd_pmksa_add, NULL, + cli_cmd_flag_sensitive, + "<BSSID> <PMKID> <PMK> <expiration in seconds> = store mesh PMKSA cache entry from external storage" }, +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ { "reassociate", wpa_cli_cmd_reassociate, NULL, cli_cmd_flag_none, "= force reassociation" }, @@ -2807,30 +3068,30 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "preauthenticate", wpa_cli_cmd_preauthenticate, wpa_cli_complete_bss, cli_cmd_flag_none, "<BSSID> = force preauthentication" }, - { "identity", wpa_cli_cmd_identity, NULL, + { "identity", wpa_cli_cmd_identity, wpa_cli_complete_network_id, cli_cmd_flag_none, "<network id> <identity> = configure identity for an SSID" }, - { "password", wpa_cli_cmd_password, NULL, + { "password", wpa_cli_cmd_password, wpa_cli_complete_network_id, cli_cmd_flag_sensitive, "<network id> <password> = configure password for an SSID" }, - { "new_password", wpa_cli_cmd_new_password, NULL, - cli_cmd_flag_sensitive, + { "new_password", wpa_cli_cmd_new_password, + wpa_cli_complete_network_id, cli_cmd_flag_sensitive, "<network id> <password> = change password for an SSID" }, - { "pin", wpa_cli_cmd_pin, NULL, + { "pin", wpa_cli_cmd_pin, wpa_cli_complete_network_id, cli_cmd_flag_sensitive, "<network id> <pin> = configure pin for an SSID" }, - { "otp", wpa_cli_cmd_otp, NULL, + { "otp", wpa_cli_cmd_otp, wpa_cli_complete_network_id, cli_cmd_flag_sensitive, "<network id> <password> = configure one-time-password for an SSID" }, - { "passphrase", wpa_cli_cmd_passphrase, NULL, + { "passphrase", wpa_cli_cmd_passphrase, wpa_cli_complete_network_id, cli_cmd_flag_sensitive, "<network id> <passphrase> = configure private key passphrase\n" " for an SSID" }, - { "sim", wpa_cli_cmd_sim, NULL, + { "sim", wpa_cli_cmd_sim, wpa_cli_complete_network_id, cli_cmd_flag_sensitive, "<network id> <pin> = report SIM operation result" }, - { "bssid", wpa_cli_cmd_bssid, NULL, + { "bssid", wpa_cli_cmd_bssid, wpa_cli_complete_network_id, cli_cmd_flag_none, "<network id> <BSSID> = set preferred BSSID for an SSID" }, { "blacklist", wpa_cli_cmd_blacklist, wpa_cli_complete_bss, @@ -2884,10 +3145,10 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "remove_cred", wpa_cli_cmd_remove_cred, NULL, cli_cmd_flag_none, "<cred id> = remove a credential" }, - { "set_cred", wpa_cli_cmd_set_cred, NULL, + { "set_cred", wpa_cli_cmd_set_cred, wpa_cli_complete_cred, cli_cmd_flag_sensitive, "<cred id> <variable> <value> = set credential variables" }, - { "get_cred", wpa_cli_cmd_get_cred, NULL, + { "get_cred", wpa_cli_cmd_get_cred, wpa_cli_complete_cred, cli_cmd_flag_none, "<cred id> <variable> = get credential variables" }, { "save_config", wpa_cli_cmd_save_config, NULL, @@ -2951,9 +3212,6 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "bss_flush", wpa_cli_cmd_bss_flush, NULL, cli_cmd_flag_none, "<value> = set BSS flush age (0 by default)" }, - { "stkstart", wpa_cli_cmd_stkstart, NULL, - cli_cmd_flag_none, - "<addr> = request STK negotiation with <addr>" }, { "ft_ds", wpa_cli_cmd_ft_ds, wpa_cli_complete_bss, cli_cmd_flag_none, "<addr> = request over-the-DS FT with <addr>" }, @@ -3029,17 +3287,20 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { cli_cmd_flag_none, "<addr> = request RSN authentication with <addr> in IBSS" }, #ifdef CONFIG_AP - { "sta", wpa_cli_cmd_sta, NULL, + { "sta", wpa_cli_cmd_sta, wpa_cli_complete_sta, cli_cmd_flag_none, "<addr> = get information about an associated station (AP)" }, { "all_sta", wpa_cli_cmd_all_sta, NULL, cli_cmd_flag_none, "= get information about all associated stations (AP)" }, - { "deauthenticate", wpa_cli_cmd_deauthenticate, NULL, + { "list_sta", wpa_cli_cmd_list_sta, NULL, cli_cmd_flag_none, + "= list all stations (AP)" }, + { "deauthenticate", wpa_cli_cmd_deauthenticate, + wpa_cli_complete_deauthenticate, cli_cmd_flag_none, "<addr> = deauthenticate a station" }, - { "disassociate", wpa_cli_cmd_disassociate, NULL, - cli_cmd_flag_none, + { "disassociate", wpa_cli_cmd_disassociate, + wpa_cli_complete_disassociate, cli_cmd_flag_none, "<addr> = disassociate a station" }, { "chan_switch", wpa_cli_cmd_chanswitch, NULL, cli_cmd_flag_none, @@ -3168,6 +3429,7 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "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 */ { "vendor_elem_add", wpa_cli_cmd_vendor_elem_add, NULL, cli_cmd_flag_none, "<frame id> <hexdump of elem(s)> = add vendor specific IEs to frame(s)\n" @@ -3180,7 +3442,6 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { cli_cmd_flag_none, "<frame id> <hexdump of elem(s)> = remove vendor specific IE(s) in frame(s)\n" VENDOR_ELEM_FRAME_ID }, -#endif /* CONFIG_P2P */ #ifdef CONFIG_WIFI_DISPLAY { "wfd_subelem_set", wpa_cli_cmd_wfd_subelem_set, NULL, cli_cmd_flag_none, @@ -3285,7 +3546,9 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "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> [list] = Send BSS Transition Management Query" }, + "<query reason> [list]" + " [neighbor=<BSSID>,<BSSID information>,<operating class>,<channel number>,<PHY type>[,<hexdump of optional subelements>]" + " = Send BSS Transition Management Query" }, #endif /* CONFIG_WNM */ { "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive, "<params..> = Sent unprocessed command" }, @@ -3320,6 +3583,44 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = { { "p2p_lo_stop", wpa_cli_cmd_p2p_lo_stop, NULL, cli_cmd_flag_none, "= stop P2P listen offload" }, +#ifdef CONFIG_DPP + { "dpp_qr_code", wpa_cli_cmd_dpp_qr_code, NULL, cli_cmd_flag_none, + "report a scanned DPP URI from a QR Code" }, + { "dpp_bootstrap_gen", wpa_cli_cmd_dpp_bootstrap_gen, NULL, + cli_cmd_flag_sensitive, + "type=<qrcode> [chan=..] [mac=..] [info=..] [curve=..] [key=..] = generate DPP bootstrap information" }, + { "dpp_bootstrap_remove", wpa_cli_cmd_dpp_bootstrap_remove, NULL, + cli_cmd_flag_none, + "*|<id> = remove DPP bootstrap information" }, + { "dpp_bootstrap_get_uri", wpa_cli_cmd_dpp_bootstrap_get_uri, NULL, + cli_cmd_flag_none, + "<id> = get DPP bootstrap URI" }, + { "dpp_bootstrap_info", wpa_cli_cmd_dpp_bootstrap_info, NULL, + cli_cmd_flag_none, + "<id> = show DPP bootstrap information" }, + { "dpp_auth_init", wpa_cli_cmd_dpp_auth_init, NULL, cli_cmd_flag_none, + "peer=<id> [own=<id>] = initiate DPP bootstrapping" }, + { "dpp_listen", wpa_cli_cmd_dpp_listen, NULL, cli_cmd_flag_none, + "<freq in MHz> = start DPP listen" }, + { "dpp_stop_listen", wpa_cli_cmd_dpp_stop_listen, NULL, + cli_cmd_flag_none, + "= stop DPP listen" }, + { "dpp_configurator_add", wpa_cli_cmd_dpp_configurator_add, NULL, + cli_cmd_flag_sensitive, + "[curve=..] [key=..] = add DPP configurator" }, + { "dpp_configurator_remove", wpa_cli_cmd_dpp_configurator_remove, NULL, + cli_cmd_flag_none, + "*|<id> = remove DPP configurator" }, + { "dpp_configurator_get_key", wpa_cli_cmd_dpp_configurator_get_key, + NULL, cli_cmd_flag_none, + "<id> = Get DPP configurator's private key" }, + { "dpp_pkex_add", wpa_cli_cmd_dpp_pkex_add, NULL, + cli_cmd_flag_sensitive, + "add PKEX code" }, + { "dpp_pkex_remove", wpa_cli_cmd_dpp_pkex_remove, NULL, + cli_cmd_flag_none, + "*|<id> = remove DPP pkex information" }, +#endif /* CONFIG_DPP */ { NULL, NULL, NULL, cli_cmd_flag_none, NULL } }; @@ -3638,6 +3939,10 @@ static void wpa_cli_action_process(const char *msg) wpa_cli_exec(action_file, ifname, pos); } else if (str_starts(pos, WPS_EVENT_SUCCESS)) { wpa_cli_exec(action_file, ifname, pos); + } else if (str_starts(pos, WPS_EVENT_ACTIVE)) { + wpa_cli_exec(action_file, ifname, pos); + } else if (str_starts(pos, WPS_EVENT_TIMEOUT)) { + wpa_cli_exec(action_file, ifname, pos); } else if (str_starts(pos, WPS_EVENT_FAIL)) { wpa_cli_exec(action_file, ifname, pos); } else if (str_starts(pos, AP_STA_CONNECTED)) { @@ -3650,6 +3955,8 @@ static void wpa_cli_action_process(const char *msg) wpa_cli_exec(action_file, ifname, pos); } else if (str_starts(pos, HS20_DEAUTH_IMMINENT_NOTICE)) { wpa_cli_exec(action_file, ifname, pos); + } else if (str_starts(pos, HS20_T_C_ACCEPTANCE)) { + wpa_cli_exec(action_file, ifname, pos); } else if (str_starts(pos, WPA_EVENT_TERMINATING)) { printf("wpa_supplicant is terminating - stop monitoring\n"); wpa_cli_quit = 1; @@ -3675,6 +3982,7 @@ static void wpa_cli_reconnect(void) edit_clear_line(); printf("\rConnection to wpa_supplicant re-established\n"); edit_redraw(); + update_stations(ctrl_conn); } } @@ -3897,7 +4205,7 @@ 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"; + const char *cmd = "BSS RANGE=ALL MASK=0x2"; char *pos, *end; if (ctrl == NULL) @@ -3928,7 +4236,7 @@ static void update_ifnames(struct wpa_ctrl *ctrl) char buf[4096]; size_t len = sizeof(buf); int ret; - char *cmd = "INTERFACES"; + const char *cmd = "INTERFACES"; char *pos, *end; char txt[200]; @@ -3955,12 +4263,44 @@ static void update_ifnames(struct wpa_ctrl *ctrl) } +static void update_creds(struct wpa_ctrl *ctrl) +{ + char buf[4096]; + size_t len = sizeof(buf); + int ret; + const char *cmd = "LIST_CREDS"; + char *pos, *end; + int header = 1; + + cli_txt_list_flush(&creds); + + 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'; + if (!header) + cli_txt_list_add_word(&creds, pos, '\t'); + header = 0; + pos = end + 1; + } +} + + static void update_networks(struct wpa_ctrl *ctrl) { char buf[4096]; size_t len = sizeof(buf); int ret; - char *cmd = "LIST_NETWORKS"; + const char *cmd = "LIST_NETWORKS"; char *pos, *end; int header = 1; @@ -3987,6 +4327,27 @@ static void update_networks(struct wpa_ctrl *ctrl) } +static void update_stations(struct wpa_ctrl *ctrl) +{ +#ifdef CONFIG_AP + char addr[32], cmd[64]; + + if (!ctrl || !interactive) + return; + + cli_txt_list_flush(&stations); + + if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0)) + return; + do { + if (os_strcmp(addr, "") != 0) + cli_txt_list_add(&stations, addr); + os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); + } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0); +#endif /* CONFIG_AP */ +} + + static void try_connection(void *eloop_ctx, void *timeout_ctx) { if (ctrl_conn) @@ -4007,7 +4368,9 @@ static void try_connection(void *eloop_ctx, void *timeout_ctx) } update_bssid_list(ctrl_conn); + update_creds(ctrl_conn); update_networks(ctrl_conn); + update_stations(ctrl_conn); if (warning_displayed) printf("Connection established.\n"); @@ -4029,6 +4392,7 @@ static void wpa_cli_interactive(void) cli_txt_list_flush(&p2p_groups); cli_txt_list_flush(&bsses); cli_txt_list_flush(&ifnames); + cli_txt_list_flush(&creds); cli_txt_list_flush(&networks); if (edit_started) edit_deinit(hfile, wpa_cli_edit_filter_history_cb); @@ -4254,6 +4618,7 @@ int main(int argc, char *argv[]) "control interface\n"); } } + update_stations(ctrl_conn); } } diff --git a/contrib/wpa/wpa_supplicant/wpa_passphrase.c b/contrib/wpa/wpa_supplicant/wpa_passphrase.c index 9b568f0f7c67..adca1cce13ee 100644 --- a/contrib/wpa/wpa_supplicant/wpa_passphrase.c +++ b/contrib/wpa/wpa_supplicant/wpa_passphrase.c @@ -17,6 +17,7 @@ int main(int argc, char *argv[]) unsigned char psk[32]; int i; char *ssid, *passphrase, buf[64], *pos; + size_t len; if (argc < 2) { printf("usage: wpa_passphrase <ssid> [passphrase]\n" @@ -47,10 +48,15 @@ int main(int argc, char *argv[]) passphrase = buf; } - if (os_strlen(passphrase) < 8 || os_strlen(passphrase) > 63) { + len = os_strlen(passphrase); + if (len < 8 || len > 63) { printf("Passphrase must be 8..63 characters\n"); return 1; } + if (has_ctrl_char((u8 *) passphrase, len)) { + printf("Invalid passphrase character\n"); + return 1; + } pbkdf2_sha1(passphrase, (u8 *) ssid, os_strlen(ssid), 4096, psk, 32); diff --git a/contrib/wpa/wpa_supplicant/wpa_priv.c b/contrib/wpa/wpa_supplicant/wpa_priv.c index 511df4f18148..b3ad45eca516 100644 --- a/contrib/wpa/wpa_supplicant/wpa_priv.c +++ b/contrib/wpa/wpa_supplicant/wpa_priv.c @@ -21,6 +21,7 @@ #include "common/privsep_commands.h" #include "common/ieee802_11_defs.h" +#define WPA_PRIV_MAX_L2 3 struct wpa_priv_interface { struct wpa_priv_interface *next; @@ -35,11 +36,16 @@ struct wpa_priv_interface { void *drv_priv; void *drv_global_priv; struct sockaddr_un drv_addr; + socklen_t drv_addr_len; int wpas_registered; - /* TODO: add support for multiple l2 connections */ - struct l2_packet_data *l2; - struct sockaddr_un l2_addr; + struct l2_packet_data *l2[WPA_PRIV_MAX_L2]; + struct sockaddr_un l2_addr[WPA_PRIV_MAX_L2]; + socklen_t l2_addr_len[WPA_PRIV_MAX_L2]; + struct wpa_priv_l2 { + struct wpa_priv_interface *parent; + int idx; + } l2_ctx[WPA_PRIV_MAX_L2]; }; struct wpa_priv_global { @@ -48,8 +54,10 @@ struct wpa_priv_global { static void wpa_priv_cmd_register(struct wpa_priv_interface *iface, - struct sockaddr_un *from) + struct sockaddr_un *from, socklen_t fromlen) { + int i; + if (iface->drv_priv) { wpa_printf(MSG_DEBUG, "Cleaning up forgotten driver instance"); if (iface->driver->deinit) @@ -62,11 +70,13 @@ static void wpa_priv_cmd_register(struct wpa_priv_interface *iface, iface->wpas_registered = 0; } - if (iface->l2) { - wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet " - "instance"); - l2_packet_deinit(iface->l2); - iface->l2 = NULL; + for (i = 0; i < WPA_PRIV_MAX_L2; i++) { + if (iface->l2[i]) { + wpa_printf(MSG_DEBUG, + "Cleaning up forgotten l2_packet instance"); + l2_packet_deinit(iface->l2[i]); + iface->l2[i] = NULL; + } } if (iface->driver->init2) { @@ -96,7 +106,8 @@ static void wpa_priv_cmd_register(struct wpa_priv_interface *iface, wpa_printf(MSG_DEBUG, "Driver wrapper '%s' initialized for interface " "'%s'", iface->driver_name, iface->ifname); - os_memcpy(&iface->drv_addr, from, sizeof(iface->drv_addr)); + os_memcpy(&iface->drv_addr, from, fromlen); + iface->drv_addr_len = fromlen; iface->wpas_registered = 1; if (iface->driver->set_param && @@ -123,18 +134,43 @@ static void wpa_priv_cmd_unregister(struct wpa_priv_interface *iface, static void wpa_priv_cmd_scan(struct wpa_priv_interface *iface, - char *buf, size_t len) + void *buf, size_t len) { struct wpa_driver_scan_params params; + struct privsep_cmd_scan *scan; + unsigned int i; + int freqs[PRIVSEP_MAX_SCAN_FREQS + 1]; if (iface->drv_priv == NULL) return; + if (len < sizeof(*scan)) { + wpa_printf(MSG_DEBUG, "Invalid scan request"); + return; + } + + scan = buf; + os_memset(¶ms, 0, sizeof(params)); - if (len) { - params.ssids[0].ssid = (u8 *) buf; - params.ssids[0].ssid_len = len; - params.num_ssids = 1; + if (scan->num_ssids > WPAS_MAX_SCAN_SSIDS) { + wpa_printf(MSG_DEBUG, "Invalid scan request (num_ssids)"); + return; + } + params.num_ssids = scan->num_ssids; + for (i = 0; i < scan->num_ssids; i++) { + params.ssids[i].ssid = scan->ssids[i]; + params.ssids[i].ssid_len = scan->ssid_lens[i]; + } + + if (scan->num_freqs > PRIVSEP_MAX_SCAN_FREQS) { + wpa_printf(MSG_DEBUG, "Invalid scan request (num_freqs)"); + return; + } + if (scan->num_freqs) { + for (i = 0; i < scan->num_freqs; i++) + freqs[i] = scan->freqs[i]; + freqs[i] = 0; + params.freqs = freqs; } if (iface->driver->scan2) @@ -143,7 +179,8 @@ static void wpa_priv_cmd_scan(struct wpa_priv_interface *iface, static void wpa_priv_get_scan_results2(struct wpa_priv_interface *iface, - struct sockaddr_un *from) + struct sockaddr_un *from, + socklen_t fromlen) { struct wpa_scan_results *res; u8 *buf = NULL, *pos, *end; @@ -165,7 +202,7 @@ static void wpa_priv_get_scan_results2(struct wpa_priv_interface *iface, for (i = 0; i < res->num; i++) { struct wpa_scan_res *r = res->res[i]; - val = sizeof(*r) + r->ie_len; + val = sizeof(*r) + r->ie_len + r->beacon_ie_len; if (end - pos < (int) sizeof(int) + val) break; os_memcpy(pos, &val, sizeof(int)); @@ -174,8 +211,7 @@ static void wpa_priv_get_scan_results2(struct wpa_priv_interface *iface, pos += val; } - sendto(iface->fd, buf, pos - buf, 0, (struct sockaddr *) from, - sizeof(*from)); + sendto(iface->fd, buf, pos - buf, 0, (struct sockaddr *) from, fromlen); os_free(buf); wpa_scan_results_free(res); @@ -184,21 +220,21 @@ static void wpa_priv_get_scan_results2(struct wpa_priv_interface *iface, fail: os_free(buf); wpa_scan_results_free(res); - sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from)); + sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, fromlen); } static void wpa_priv_cmd_get_scan_results(struct wpa_priv_interface *iface, - struct sockaddr_un *from) + struct sockaddr_un *from, + socklen_t fromlen) { if (iface->drv_priv == NULL) return; if (iface->driver->get_scan_results2) - wpa_priv_get_scan_results2(iface, from); + wpa_priv_get_scan_results2(iface, from, fromlen); else - sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, - sizeof(*from)); + sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, fromlen); } @@ -218,7 +254,7 @@ static void wpa_priv_cmd_authenticate(struct wpa_priv_interface *iface, } auth = buf; - if (sizeof(*auth) + auth->ie_len + auth->sae_data_len > len) { + if (sizeof(*auth) + auth->ie_len + auth->auth_data_len > len) { wpa_printf(MSG_DEBUG, "Authentication request overflow"); return; } @@ -244,9 +280,9 @@ static void wpa_priv_cmd_authenticate(struct wpa_priv_interface *iface, params.ie = (u8 *) (auth + 1); params.ie_len = auth->ie_len; } - if (auth->sae_data_len) { - params.sae_data = ((u8 *) (auth + 1)) + auth->ie_len; - params.sae_data_len = auth->sae_data_len; + if (auth->auth_data_len) { + params.auth_data = ((u8 *) (auth + 1)) + auth->ie_len; + params.auth_data_len = auth->auth_data_len; } res = iface->driver->authenticate(iface->drv_priv, ¶ms); @@ -303,7 +339,7 @@ static void wpa_priv_cmd_associate(struct wpa_priv_interface *iface, static void wpa_priv_cmd_get_bssid(struct wpa_priv_interface *iface, - struct sockaddr_un *from) + struct sockaddr_un *from, socklen_t fromlen) { u8 bssid[ETH_ALEN]; @@ -315,16 +351,16 @@ static void wpa_priv_cmd_get_bssid(struct wpa_priv_interface *iface, goto fail; sendto(iface->fd, bssid, ETH_ALEN, 0, (struct sockaddr *) from, - sizeof(*from)); + fromlen); return; fail: - sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from)); + sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, fromlen); } static void wpa_priv_cmd_get_ssid(struct wpa_priv_interface *iface, - struct sockaddr_un *from) + struct sockaddr_un *from, socklen_t fromlen) { u8 ssid[sizeof(int) + SSID_MAX_LEN]; int res; @@ -335,17 +371,18 @@ static void wpa_priv_cmd_get_ssid(struct wpa_priv_interface *iface, if (iface->driver->get_ssid == NULL) goto fail; + os_memset(ssid, 0, sizeof(ssid)); res = iface->driver->get_ssid(iface->drv_priv, &ssid[sizeof(int)]); if (res < 0 || res > SSID_MAX_LEN) goto fail; os_memcpy(ssid, &res, sizeof(int)); sendto(iface->fd, ssid, sizeof(ssid), 0, (struct sockaddr *) from, - sizeof(*from)); + fromlen); return; fail: - sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from)); + sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, fromlen); } @@ -378,7 +415,7 @@ static void wpa_priv_cmd_set_key(struct wpa_priv_interface *iface, static void wpa_priv_cmd_get_capa(struct wpa_priv_interface *iface, - struct sockaddr_un *from) + struct sockaddr_un *from, socklen_t fromlen) { struct wpa_driver_capa capa; @@ -394,18 +431,19 @@ static void wpa_priv_cmd_get_capa(struct wpa_priv_interface *iface, capa.extended_capa_mask = NULL; capa.extended_capa_len = 0; sendto(iface->fd, &capa, sizeof(capa), 0, (struct sockaddr *) from, - sizeof(*from)); + fromlen); return; fail: - sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from)); + sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, fromlen); } static void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { - struct wpa_priv_interface *iface = ctx; + struct wpa_priv_l2 *l2_ctx = ctx; + struct wpa_priv_interface *iface = l2_ctx->parent; struct msghdr msg; struct iovec io[2]; @@ -417,8 +455,8 @@ static void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf, os_memset(&msg, 0, sizeof(msg)); msg.msg_iov = io; msg.msg_iovlen = 2; - msg.msg_name = &iface->l2_addr; - msg.msg_namelen = sizeof(iface->l2_addr); + msg.msg_name = &iface->l2_addr[l2_ctx->idx]; + msg.msg_namelen = iface->l2_addr_len[l2_ctx->idx]; if (sendmsg(iface->fd, &msg, 0) < 0) { wpa_printf(MSG_ERROR, "sendmsg(l2 rx): %s", strerror(errno)); @@ -426,14 +464,23 @@ static void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf, } +static int wpa_priv_allowed_l2_proto(u16 proto) +{ + return proto == ETH_P_EAPOL || proto == ETH_P_RSN_PREAUTH || + proto == ETH_P_80211_ENCAP; +} + + static void wpa_priv_cmd_l2_register(struct wpa_priv_interface *iface, struct sockaddr_un *from, + socklen_t fromlen, void *buf, size_t len) { int *reg_cmd = buf; u8 own_addr[ETH_ALEN]; int res; u16 proto; + int idx; if (len != 2 * sizeof(int)) { wpa_printf(MSG_DEBUG, "Invalid l2_register length %lu", @@ -442,50 +489,69 @@ static void wpa_priv_cmd_l2_register(struct wpa_priv_interface *iface, } proto = reg_cmd[0]; - if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH && - proto != ETH_P_80211_ENCAP) { + if (!wpa_priv_allowed_l2_proto(proto)) { wpa_printf(MSG_DEBUG, "Refused l2_packet connection for " "ethertype 0x%x", proto); return; } - if (iface->l2) { - wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet " - "instance"); - l2_packet_deinit(iface->l2); - iface->l2 = NULL; + for (idx = 0; idx < WPA_PRIV_MAX_L2; idx++) { + if (!iface->l2[idx]) + break; + } + if (idx == WPA_PRIV_MAX_L2) { + wpa_printf(MSG_DEBUG, "No free l2_packet connection found"); + return; } - os_memcpy(&iface->l2_addr, from, sizeof(iface->l2_addr)); + os_memcpy(&iface->l2_addr[idx], from, fromlen); + iface->l2_addr_len[idx] = fromlen; - iface->l2 = l2_packet_init(iface->ifname, NULL, proto, - wpa_priv_l2_rx, iface, reg_cmd[1]); - if (iface->l2 == NULL) { + iface->l2_ctx[idx].idx = idx; + iface->l2_ctx[idx].parent = iface; + iface->l2[idx] = l2_packet_init(iface->ifname, NULL, proto, + wpa_priv_l2_rx, &iface->l2_ctx[idx], + reg_cmd[1]); + if (!iface->l2[idx]) { wpa_printf(MSG_DEBUG, "Failed to initialize l2_packet " "instance for protocol %d", proto); return; } - if (l2_packet_get_own_addr(iface->l2, own_addr) < 0) { + if (l2_packet_get_own_addr(iface->l2[idx], own_addr) < 0) { wpa_printf(MSG_DEBUG, "Failed to get own address from " "l2_packet"); - l2_packet_deinit(iface->l2); - iface->l2 = NULL; + l2_packet_deinit(iface->l2[idx]); + iface->l2[idx] = NULL; return; } res = sendto(iface->fd, own_addr, ETH_ALEN, 0, - (struct sockaddr *) from, sizeof(*from)); - wpa_printf(MSG_DEBUG, "L2 registration: res=%d", res); + (struct sockaddr *) from, fromlen); + wpa_printf(MSG_DEBUG, "L2 registration[idx=%d]: res=%d", idx, res); } static void wpa_priv_cmd_l2_unregister(struct wpa_priv_interface *iface, - struct sockaddr_un *from) + struct sockaddr_un *from, + socklen_t fromlen) { - if (iface->l2) { - l2_packet_deinit(iface->l2); - iface->l2 = NULL; + int idx; + + for (idx = 0; idx < WPA_PRIV_MAX_L2; idx++) { + if (iface->l2_addr_len[idx] == fromlen && + os_memcmp(&iface->l2_addr[idx], from, fromlen) == 0) + break; + } + if (idx == WPA_PRIV_MAX_L2) { + wpa_printf(MSG_DEBUG, + "No registered l2_packet socket found for unregister request"); + return; + } + + if (iface->l2[idx]) { + l2_packet_deinit(iface->l2[idx]); + iface->l2[idx] = NULL; } } @@ -493,20 +559,36 @@ static void wpa_priv_cmd_l2_unregister(struct wpa_priv_interface *iface, static void wpa_priv_cmd_l2_notify_auth_start(struct wpa_priv_interface *iface, struct sockaddr_un *from) { - if (iface->l2) - l2_packet_notify_auth_start(iface->l2); + int idx; + + for (idx = 0; idx < WPA_PRIV_MAX_L2; idx++) { + if (iface->l2[idx]) + l2_packet_notify_auth_start(iface->l2[idx]); + } } static void wpa_priv_cmd_l2_send(struct wpa_priv_interface *iface, - struct sockaddr_un *from, + struct sockaddr_un *from, socklen_t fromlen, void *buf, size_t len) { u8 *dst_addr; u16 proto; int res; + int idx; - if (iface->l2 == NULL) + for (idx = 0; idx < WPA_PRIV_MAX_L2; idx++) { + if (iface->l2_addr_len[idx] == fromlen && + os_memcmp(&iface->l2_addr[idx], from, fromlen) == 0) + break; + } + if (idx == WPA_PRIV_MAX_L2) { + wpa_printf(MSG_DEBUG, + "No registered l2_packet socket found for send request"); + return; + } + + if (iface->l2[idx] == NULL) return; if (len < ETH_ALEN + 2) { @@ -518,15 +600,15 @@ static void wpa_priv_cmd_l2_send(struct wpa_priv_interface *iface, dst_addr = buf; os_memcpy(&proto, buf + ETH_ALEN, 2); - if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) { + if (!wpa_priv_allowed_l2_proto(proto)) { wpa_printf(MSG_DEBUG, "Refused l2_packet send for ethertype " "0x%x", proto); return; } - res = l2_packet_send(iface->l2, dst_addr, proto, buf + ETH_ALEN + 2, - len - ETH_ALEN - 2); - wpa_printf(MSG_DEBUG, "L2 send: res=%d", res); + res = l2_packet_send(iface->l2[idx], dst_addr, proto, + buf + ETH_ALEN + 2, len - ETH_ALEN - 2); + wpa_printf(MSG_DEBUG, "L2 send[idx=%d]: res=%d", idx, res); } @@ -571,7 +653,7 @@ static void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx) switch (cmd) { case PRIVSEP_CMD_REGISTER: - wpa_priv_cmd_register(iface, &from); + wpa_priv_cmd_register(iface, &from, fromlen); break; case PRIVSEP_CMD_UNREGISTER: wpa_priv_cmd_unregister(iface, &from); @@ -580,34 +662,35 @@ static void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx) wpa_priv_cmd_scan(iface, cmd_buf, cmd_len); break; case PRIVSEP_CMD_GET_SCAN_RESULTS: - wpa_priv_cmd_get_scan_results(iface, &from); + wpa_priv_cmd_get_scan_results(iface, &from, fromlen); break; case PRIVSEP_CMD_ASSOCIATE: wpa_priv_cmd_associate(iface, cmd_buf, cmd_len); break; case PRIVSEP_CMD_GET_BSSID: - wpa_priv_cmd_get_bssid(iface, &from); + wpa_priv_cmd_get_bssid(iface, &from, fromlen); break; case PRIVSEP_CMD_GET_SSID: - wpa_priv_cmd_get_ssid(iface, &from); + wpa_priv_cmd_get_ssid(iface, &from, fromlen); break; case PRIVSEP_CMD_SET_KEY: wpa_priv_cmd_set_key(iface, cmd_buf, cmd_len); break; case PRIVSEP_CMD_GET_CAPA: - wpa_priv_cmd_get_capa(iface, &from); + wpa_priv_cmd_get_capa(iface, &from, fromlen); break; case PRIVSEP_CMD_L2_REGISTER: - wpa_priv_cmd_l2_register(iface, &from, cmd_buf, cmd_len); + wpa_priv_cmd_l2_register(iface, &from, fromlen, + cmd_buf, cmd_len); break; case PRIVSEP_CMD_L2_UNREGISTER: - wpa_priv_cmd_l2_unregister(iface, &from); + wpa_priv_cmd_l2_unregister(iface, &from, fromlen); break; case PRIVSEP_CMD_L2_NOTIFY_AUTH_START: wpa_priv_cmd_l2_notify_auth_start(iface, &from); break; case PRIVSEP_CMD_L2_SEND: - wpa_priv_cmd_l2_send(iface, &from, cmd_buf, cmd_len); + wpa_priv_cmd_l2_send(iface, &from, fromlen, cmd_buf, cmd_len); break; case PRIVSEP_CMD_SET_COUNTRY: pos = cmd_buf; @@ -625,8 +708,14 @@ static void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx) static void wpa_priv_interface_deinit(struct wpa_priv_interface *iface) { - if (iface->drv_priv && iface->driver->deinit) - iface->driver->deinit(iface->drv_priv); + int i; + + if (iface->drv_priv) { + if (iface->driver->deinit) + iface->driver->deinit(iface->drv_priv); + if (iface->drv_global_priv) + iface->driver->global_deinit(iface->drv_global_priv); + } if (iface->fd >= 0) { eloop_unregister_read_sock(iface->fd); @@ -634,8 +723,10 @@ static void wpa_priv_interface_deinit(struct wpa_priv_interface *iface) unlink(iface->sock_name); } - if (iface->l2) - l2_packet_deinit(iface->l2); + for (i = 0; i < WPA_PRIV_MAX_L2; i++) { + if (iface->l2[i]) + l2_packet_deinit(iface->l2[i]); + } os_free(iface->ifname); os_free(iface->driver_name); @@ -777,7 +868,7 @@ static int wpa_priv_send_event(struct wpa_priv_interface *iface, int event, msg.msg_iov = io; msg.msg_iovlen = data ? 2 : 1; msg.msg_name = &iface->drv_addr; - msg.msg_namelen = sizeof(iface->drv_addr); + msg.msg_namelen = iface->drv_addr_len; if (sendmsg(iface->fd, &msg, 0) < 0) { wpa_printf(MSG_ERROR, "sendmsg(wpas_socket): %s", @@ -796,7 +887,7 @@ static void wpa_priv_send_auth(struct wpa_priv_interface *iface, struct privsep_event_auth *auth; u8 *buf, *pos; - buf = os_malloc(buflen); + buf = os_zalloc(buflen); if (buf == NULL) return; @@ -990,12 +1081,6 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, &data->pmkid_candidate, sizeof(struct pmkid_candidate)); break; - case EVENT_STKSTART: - if (data == NULL) - return; - wpa_priv_send_event(iface, PRIVSEP_EVENT_STKSTART, - &data->stkstart.peer, ETH_ALEN); - break; case EVENT_FT_RESPONSE: wpa_priv_send_ft_response(iface, data); break; @@ -1061,7 +1146,7 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, msg.msg_iov = io; msg.msg_iovlen = 3; msg.msg_name = &iface->drv_addr; - msg.msg_namelen = sizeof(iface->drv_addr); + msg.msg_namelen = iface->drv_addr_len; if (sendmsg(iface->fd, &msg, 0) < 0) wpa_printf(MSG_ERROR, "sendmsg(wpas_socket): %s", @@ -1099,7 +1184,7 @@ static void wpa_priv_fd_workaround(void) static void usage(void) { printf("wpa_priv v" VERSION_STR "\n" - "Copyright (c) 2007-2016, Jouni Malinen <j@w1.fi> and " + "Copyright (c) 2007-2017, Jouni Malinen <j@w1.fi> and " "contributors\n" "\n" "usage:\n" diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.c b/contrib/wpa/wpa_supplicant/wpa_supplicant.c index 7361ee96d1df..e587d7e3cd69 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant.c +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -38,6 +38,7 @@ #include "common/wpa_ctrl.h" #include "common/ieee802_11_defs.h" #include "common/hw_features_common.h" +#include "common/gas_server.h" #include "p2p/p2p.h" #include "fst/fst.h" #include "blacklist.h" @@ -59,10 +60,15 @@ #include "wnm_sta.h" #include "wpas_kay.h" #include "mesh.h" +#include "dpp_supplicant.h" +#ifdef CONFIG_MESH +#include "ap/ap_config.h" +#include "ap/hostapd.h" +#endif /* CONFIG_MESH */ const char *const wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" -"Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> and contributors"; +"Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> and contributors"; const char *const wpa_supplicant_license = "This software may be distributed under the terms of the BSD license.\n" @@ -112,6 +118,13 @@ const char *const wpa_supplicant_full_license5 = "\n"; #endif /* CONFIG_NO_STDOUT_DEBUG */ + +static void wpa_bss_tmp_disallow_timeout(void *eloop_ctx, void *timeout_ctx); +#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL) +static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s); +#endif /* CONFIG_FILS && IEEE8021X_EAPOL */ + + /* Configure default/group WEP keys for static WEP */ int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) { @@ -230,10 +243,30 @@ void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec " "%d usec", sec, usec); eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); + wpa_s->last_auth_timeout_sec = sec; eloop_register_timeout(sec, usec, wpa_supplicant_timeout, wpa_s, NULL); } +/* + * wpas_auth_timeout_restart - Restart and change timeout for authentication + * @wpa_s: Pointer to wpa_supplicant data + * @sec_diff: difference in seconds applied to original timeout value + */ +void wpas_auth_timeout_restart(struct wpa_supplicant *wpa_s, int sec_diff) +{ + int new_sec = wpa_s->last_auth_timeout_sec + sec_diff; + + if (eloop_is_timeout_registered(wpa_supplicant_timeout, wpa_s, NULL)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "Authentication timeout restart: %d sec", new_sec); + eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); + eloop_register_timeout(new_sec, 0, wpa_supplicant_timeout, + wpa_s, NULL); + } +} + + /** * wpa_supplicant_cancel_auth_timeout - Cancel authentication timeout * @wpa_s: Pointer to wpa_supplicant data @@ -247,6 +280,9 @@ void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s) wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout"); eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL); wpa_blacklist_del(wpa_s, wpa_s->bssid); + os_free(wpa_s->last_con_fail_realm); + wpa_s->last_con_fail_realm = NULL; + wpa_s->last_con_fail_realm_len = 0; } @@ -329,7 +365,12 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s) eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf); - ieee802_1x_alloc_kay_sm(wpa_s, ssid); +#ifdef CONFIG_MACSEC + if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE && ssid->mka_psk_set) + ieee802_1x_create_preshared_mka(wpa_s, ssid); + else + ieee802_1x_alloc_kay_sm(wpa_s, ssid); +#endif /* CONFIG_MACSEC */ #endif /* IEEE8021X_EAPOL */ } @@ -409,12 +450,26 @@ static void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s) dl_list_for_each_safe(bss, prev, &wpa_s->bss_tmp_disallowed, struct wpa_bss_tmp_disallowed, list) { + eloop_cancel_timeout(wpa_bss_tmp_disallow_timeout, wpa_s, bss); dl_list_del(&bss->list); os_free(bss); } } +void wpas_flush_fils_hlp_req(struct wpa_supplicant *wpa_s) +{ + struct fils_hlp_req *req; + + while ((req = dl_list_first(&wpa_s->fils_hlp_req, struct fils_hlp_req, + list)) != NULL) { + dl_list_del(&req->list); + wpabuf_free(req->pkt); + os_free(req); + } +} + + static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) { int i; @@ -434,6 +489,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) #ifdef CONFIG_TESTING_OPTIONS l2_packet_deinit(wpa_s->l2_test); wpa_s->l2_test = NULL; + os_free(wpa_s->get_pref_freq_list_override); + wpa_s->get_pref_freq_list_override = NULL; + wpabuf_free(wpa_s->last_assoc_req_wpa_ie); + wpa_s->last_assoc_req_wpa_ie = NULL; #endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->conf != NULL) { @@ -448,6 +507,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) os_free(wpa_s->confanother); wpa_s->confanother = NULL; + os_free(wpa_s->last_con_fail_realm); + wpa_s->last_con_fail_realm = NULL; + wpa_s->last_con_fail_realm_len = 0; + wpa_sm_set_eapol(wpa_s->wpa, NULL); eapol_sm_deinit(wpa_s->eapol); wpa_s->eapol = NULL; @@ -506,6 +569,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) os_free(wpa_s->manual_scan_freqs); wpa_s->manual_scan_freqs = NULL; + os_free(wpa_s->select_network_scan_freqs); + wpa_s->select_network_scan_freqs = NULL; os_free(wpa_s->manual_sched_scan_freqs); wpa_s->manual_sched_scan_freqs = NULL; @@ -524,6 +589,8 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) radio_remove_works(wpa_s, "gas-query", 0); gas_query_deinit(wpa_s->gas); wpa_s->gas = NULL; + gas_server_deinit(wpa_s->gas_server); + wpa_s->gas_server = NULL; free_hw_features(wpa_s); @@ -580,6 +647,32 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpabuf_free(wpa_s->lci); wpa_s->lci = NULL; + wpas_clear_beacon_rep_data(wpa_s); + +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL +#ifdef CONFIG_MESH + { + struct external_pmksa_cache *entry; + + while ((entry = dl_list_last(&wpa_s->mesh_external_pmksa_cache, + struct external_pmksa_cache, + list)) != NULL) { + dl_list_del(&entry->list); + os_free(entry->pmksa_cache); + os_free(entry); + } + } +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ + + wpas_flush_fils_hlp_req(wpa_s); + + wpabuf_free(wpa_s->ric_ies); + wpa_s->ric_ies = NULL; + +#ifdef CONFIG_DPP + wpas_dpp_deinit(wpa_s); +#endif /* CONFIG_DPP */ } @@ -793,12 +886,24 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, if (state == WPA_COMPLETED && wpa_s->new_connection) { struct wpa_ssid *ssid = wpa_s->current_ssid; + int fils_hlp_sent = 0; + +#ifdef CONFIG_SME + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && + wpa_auth_alg_fils(wpa_s->sme.auth_alg)) + fils_hlp_sent = 1; +#endif /* CONFIG_SME */ + if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && + wpa_auth_alg_fils(wpa_s->auth_alg)) + fils_hlp_sent = 1; + #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]", + MACSTR " completed [id=%d id_str=%s%s]", MAC2STR(wpa_s->bssid), ssid ? ssid->id : -1, - ssid && ssid->id_str ? ssid->id_str : ""); + ssid && ssid->id_str ? ssid->id_str : "", + fils_hlp_sent ? " FILS_HLP_SENT" : ""); #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ wpas_clear_temp_disabled(wpa_s, ssid, 1); wpa_blacklist_clear(wpa_s); @@ -813,6 +918,11 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, wpas_p2p_completed(wpa_s); sme_sched_obss_scan(wpa_s, 1); + +#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL) + if (!fils_hlp_sent && ssid && ssid->eap.erp) + wpas_update_fils_connect_params(wpa_s); +#endif /* CONFIG_FILS && IEEE8021X_EAPOL */ } else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING || state == WPA_ASSOCIATED) { wpa_s->new_connection = 1; @@ -927,7 +1037,13 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) "file '%s' - exiting", wpa_s->confname); return -1; } - wpa_config_read(wpa_s->confanother, conf); + if (wpa_s->confanother && + !wpa_config_read(wpa_s->confanother, conf)) { + wpa_msg(wpa_s, MSG_ERROR, + "Failed to parse the configuration file '%s' - exiting", + wpa_s->confanother); + return -1; + } conf->changed_parameters = (unsigned int) -1; @@ -953,7 +1069,9 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s) * TODO: should notify EAPOL SM about changes in opensc_engine_path, * pkcs11_engine_path, pkcs11_module_path, openssl_ciphers. */ - if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { + if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || + wpa_s->key_mgmt == WPA_KEY_MGMT_OWE || + wpa_s->key_mgmt == WPA_KEY_MGMT_DPP) { /* * Clear forced success to clear EAP state for next * authentication. @@ -1098,14 +1216,20 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, 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)) { + } else if (bss_osen && (ssid->proto & WPA_PROTO_OSEN) && + wpa_parse_wpa_ie(bss_osen, 2 + bss_osen[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, "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; + } else if (bss_rsn && (ssid->proto & WPA_PROTO_OSEN) && + wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[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, "RSN: using OSEN (within RSN)"); + proto = WPA_PROTO_RSN; #endif /* CONFIG_HS20 */ } else if (bss) { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN"); @@ -1157,10 +1281,35 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, ie.pairwise_cipher = ssid->pairwise_cipher; ie.key_mgmt = ssid->key_mgmt; #ifdef CONFIG_IEEE80211W - ie.mgmt_group_cipher = - ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION ? - WPA_CIPHER_AES_128_CMAC : 0; + ie.mgmt_group_cipher = 0; + if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + if (ssid->group_mgmt_cipher & + WPA_CIPHER_BIP_GMAC_256) + ie.mgmt_group_cipher = + WPA_CIPHER_BIP_GMAC_256; + else if (ssid->group_mgmt_cipher & + WPA_CIPHER_BIP_CMAC_256) + ie.mgmt_group_cipher = + WPA_CIPHER_BIP_CMAC_256; + else if (ssid->group_mgmt_cipher & + WPA_CIPHER_BIP_GMAC_128) + ie.mgmt_group_cipher = + WPA_CIPHER_BIP_GMAC_128; + else + ie.mgmt_group_cipher = + WPA_CIPHER_AES_128_CMAC; + } #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_OWE + if ((ssid->key_mgmt & WPA_KEY_MGMT_OWE) && + !ssid->owe_only && + !bss_wpa && !bss_rsn && !bss_osen) { + wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); + wpa_s->wpa_proto = 0; + *wpa_ie_len = 0; + return 0; + } +#endif /* CONFIG_OWE */ wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Set cipher suites " "based on configuration"); } else @@ -1233,10 +1382,46 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X with Suite B"); #endif /* CONFIG_SUITEB */ +#ifdef CONFIG_FILS +#ifdef CONFIG_IEEE80211R + } else if (sel & WPA_KEY_MGMT_FT_FILS_SHA384) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA384"); + } else if (sel & WPA_KEY_MGMT_FT_FILS_SHA256) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA256"); +#endif /* CONFIG_IEEE80211R */ + } else if (sel & WPA_KEY_MGMT_FILS_SHA384) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA384; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA384"); + } else if (sel & WPA_KEY_MGMT_FILS_SHA256) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA256; + wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA256"); +#endif /* CONFIG_FILS */ #ifdef CONFIG_IEEE80211R +#ifdef CONFIG_SHA384 + } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { + wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: using KEY_MGMT FT/802.1X-SHA384"); + if (pmksa_cache_get_current(wpa_s->wpa)) { + /* PMKSA caching with FT is not fully functional, so + * disable the case for now. */ + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: Disable PMKSA caching for FT/802.1X connection"); + pmksa_cache_clear_current(wpa_s->wpa); + } +#endif /* CONFIG_SHA384 */ } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X"); + if (pmksa_cache_get_current(wpa_s->wpa)) { + /* PMKSA caching with FT is not fully functional, so + * disable the case for now. */ + wpa_dbg(wpa_s, MSG_DEBUG, + "WPA: Disable PMKSA caching for FT/802.1X connection"); + pmksa_cache_clear_current(wpa_s->wpa); + } } else if (sel & WPA_KEY_MGMT_FT_PSK) { wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK; wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK"); @@ -1273,6 +1458,16 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, wpa_s->key_mgmt = WPA_KEY_MGMT_OSEN; wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using KEY_MGMT OSEN"); #endif /* CONFIG_HS20 */ +#ifdef CONFIG_OWE + } else if (sel & WPA_KEY_MGMT_OWE) { + wpa_s->key_mgmt = WPA_KEY_MGMT_OWE; + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT OWE"); +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + } else if (sel & WPA_KEY_MGMT_DPP) { + wpa_s->key_mgmt = WPA_KEY_MGMT_DPP; + wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using KEY_MGMT DPP"); +#endif /* CONFIG_DPP */ } else { wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select " "authenticated key management type"); @@ -1286,6 +1481,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IEEE80211W sel = ie.mgmt_group_cipher; + if (ssid->group_mgmt_cipher) + sel &= ssid->group_mgmt_cipher; if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION || !(ie.capabilities & WPA_CAPABILITY_MFPC)) sel = 0; @@ -1322,15 +1519,27 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) { int psk_set = 0; + int sae_only; + + sae_only = (ssid->key_mgmt & (WPA_KEY_MGMT_PSK | + WPA_KEY_MGMT_FT_PSK | + WPA_KEY_MGMT_PSK_SHA256)) == 0; - if (ssid->psk_set) { + if (ssid->psk_set && !sae_only) { + wpa_hexdump_key(MSG_MSGDUMP, "PSK (set in config)", + ssid->psk, PMK_LEN); wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL, NULL); psk_set = 1; } + + if (wpa_key_mgmt_sae(ssid->key_mgmt) && + (ssid->sae_password || ssid->passphrase)) + psk_set = 1; + #ifndef CONFIG_NO_PBKDF2 if (bss && ssid->bssid_set && ssid->ssid_len == 0 && - ssid->passphrase) { + ssid->passphrase && !sae_only) { u8 psk[PMK_LEN]; pbkdf2_sha1(ssid->passphrase, bss->ssid, bss->ssid_len, 4096, psk, PMK_LEN); @@ -1342,7 +1551,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_NO_PBKDF2 */ #ifdef CONFIG_EXT_PASSWORD - if (ssid->ext_psk) { + if (ssid->ext_psk && !sae_only) { struct wpabuf *pw = ext_password_get(wpa_s->ext_pw, ssid->ext_psk); char pw_str[64 + 1]; @@ -1388,6 +1597,9 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, ext_password_free(pw); return -1; } + wpa_hexdump_key(MSG_MSGDUMP, + "PSK (from external PSK)", + psk, PMK_LEN); wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, NULL); psk_set = 1; @@ -1408,8 +1620,15 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s, if (!psk_set) { wpa_msg(wpa_s, MSG_INFO, "No PSK available for association"); + wpas_auth_failed(wpa_s, "NO_PSK_AVAILABLE"); return -1; } +#ifdef CONFIG_OWE + } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_OWE) { + /* OWE Diffie-Hellman exchange in (Re)Association + * Request/Response frames set the PMK, so do not override it + * here. */ +#endif /* CONFIG_OWE */ } else wpa_sm_set_pmk_from_pmksa(wpa_s->wpa); @@ -1425,6 +1644,10 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) case 0: /* Bits 0-7 */ break; case 1: /* Bits 8-15 */ + if (wpa_s->conf->coloc_intf_reporting) { + /* Bit 13 - Collocated Interference Reporting */ + *pos |= 0x20; + } break; case 2: /* Bits 16-23 */ #ifdef CONFIG_WNM @@ -1443,7 +1666,7 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) break; case 4: /* Bits 32-39 */ #ifdef CONFIG_INTERWORKING - if (wpa_s->drv_flags / WPA_DRIVER_FLAGS_QOS_MAPPING) + if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_QOS_MAPPING) *pos |= 0x01; /* Bit 32 - QoS Map */ #endif /* CONFIG_INTERWORKING */ break; @@ -1466,6 +1689,12 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) if (wpa_s->conf->ftm_initiator) *pos |= 0x80; /* Bit 71 - FTM initiator */ break; + case 9: /* Bits 72-79 */ +#ifdef CONFIG_FILS + if (!wpa_s->disable_fils) + *pos |= 0x01; +#endif /* CONFIG_FILS */ + break; } } @@ -1473,11 +1702,8 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx) int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen) { u8 *pos = buf; - u8 len = 6, i; + u8 len = 10, i; - if (len < 9 && - (wpa_s->conf->ftm_initiator || wpa_s->conf->ftm_responder)) - len = 9; if (len < wpa_s->extended_capa_len) len = wpa_s->extended_capa_len; if (buflen < (size_t) len + 2) { @@ -1665,6 +1891,9 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, wmm_ac_clear_saved_tspecs(wpa_s); wpa_s->reassoc_same_bss = 0; wpa_s->reassoc_same_ess = 0; +#ifdef CONFIG_TESTING_OPTIONS + wpa_s->testing_resend_assoc = 0; +#endif /* CONFIG_TESTING_OPTIONS */ if (wpa_s->last_ssid == ssid) { wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS"); @@ -1673,11 +1902,13 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, wmm_ac_save_tspecs(wpa_s); wpa_s->reassoc_same_bss = 1; } - } else if (rand_style > 0) { + } + + if (rand_style > 0 && !wpa_s->reassoc_same_ess) { 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) { + } else if (rand_style == 0 && 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"); @@ -1696,6 +1927,13 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, #ifdef CONFIG_IBSS_RSN ibss_rsn_deinit(wpa_s->ibss_rsn); wpa_s->ibss_rsn = NULL; +#else /* CONFIG_IBSS_RSN */ + if (ssid->mode == WPAS_MODE_IBSS && + !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPA_NONE))) { + wpa_msg(wpa_s, MSG_INFO, + "IBSS RSN not supported in the build"); + return; + } #endif /* CONFIG_IBSS_RSN */ if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO || @@ -1737,6 +1975,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_STARTED "ssid=\"%s\" id=%d", wpa_ssid_txt(ssid->ssid, ssid->ssid_len), ssid->id); + wpas_notify_mesh_group_started(wpa_s, ssid); #else /* CONFIG_MESH */ wpa_msg(wpa_s, MSG_ERROR, "mesh mode support not included in the build"); @@ -1744,6 +1983,20 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, return; } + /* + * Set WPA state machine configuration to match the selected network now + * so that the information is available before wpas_start_assoc_cb() + * gets called. This is needed at least for RSN pre-authentication where + * candidate APs are added to a list based on scan result processing + * before completion of the first association. + */ + wpa_supplicant_rsn_supp_set_config(wpa_s, ssid); + +#ifdef CONFIG_DPP + if (wpas_dpp_check_connect(wpa_s, ssid, bss) != 0) + return; +#endif /* CONFIG_DPP */ + #ifdef CONFIG_TDLS if (bss) wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1), @@ -1766,6 +2019,13 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, return; } +#ifdef CONFIG_SME + if (ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_MESH) { + /* Clear possibly set auth_alg, if any, from last attempt. */ + wpa_s->sme.auth_alg = WPA_AUTH_ALG_OPEN; + } +#endif /* CONFIG_SME */ + wpas_abort_ongoing_scan(wpa_s); cwork = os_zalloc(sizeof(*cwork)); @@ -1797,11 +2057,6 @@ static int drv_supports_vht(struct wpa_supplicant *wpa_s, u8 channel; int i; -#ifdef CONFIG_HT_OVERRIDES - if (ssid->disable_ht) - return 0; -#endif /* CONFIG_HT_OVERRIDES */ - hw_mode = ieee80211_freq_to_chan(ssid->frequency, &channel); if (hw_mode == NUM_HOSTAPD_MODES) return 0; @@ -2000,6 +2255,13 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, vht_freq = *freq; +#ifdef CONFIG_VHT_OVERRIDES + if (ssid->disable_vht) { + freq->vht_enabled = 0; + return; + } +#endif /* CONFIG_VHT_OVERRIDES */ + vht_freq.vht_enabled = vht_supported(mode); if (!vht_freq.vht_enabled) return; @@ -2084,147 +2346,170 @@ void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s, } -static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) +#ifdef CONFIG_FILS +static size_t wpas_add_fils_hlp_req(struct wpa_supplicant *wpa_s, u8 *ie_buf, + size_t ie_buf_len) { - 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; - u8 prev_bssid[ETH_ALEN]; -#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 */ -#ifdef CONFIG_MBO - const u8 *mbo = NULL; -#endif /* CONFIG_MBO */ + struct fils_hlp_req *req; + size_t rem_len, hdr_len, hlp_len, len, ie_len = 0; + const u8 *pos; + u8 *buf = ie_buf; - if (deinit) { - if (work->started) { - wpa_s->connect_work = NULL; + dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req, + list) { + rem_len = ie_buf_len - ie_len; + pos = wpabuf_head(req->pkt); + hdr_len = 1 + 2 * ETH_ALEN + 6; + hlp_len = wpabuf_len(req->pkt); - /* cancel possible auth. timeout */ - eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, - NULL); + if (rem_len < 2 + hdr_len + hlp_len) { + wpa_printf(MSG_ERROR, + "FILS: Cannot fit HLP - rem_len=%lu to_fill=%lu", + (unsigned long) rem_len, + (unsigned long) (2 + hdr_len + hlp_len)); + break; + } + + len = (hdr_len + hlp_len) > 255 ? 255 : hdr_len + hlp_len; + /* Element ID */ + *buf++ = WLAN_EID_EXTENSION; + /* Length */ + *buf++ = len; + /* Element ID Extension */ + *buf++ = WLAN_EID_EXT_FILS_HLP_CONTAINER; + /* Destination MAC address */ + os_memcpy(buf, req->dst, ETH_ALEN); + buf += ETH_ALEN; + /* Source MAC address */ + os_memcpy(buf, wpa_s->own_addr, ETH_ALEN); + buf += ETH_ALEN; + /* LLC/SNAP Header */ + os_memcpy(buf, "\xaa\xaa\x03\x00\x00\x00", 6); + buf += 6; + /* HLP Packet */ + os_memcpy(buf, pos, len - hdr_len); + buf += len - hdr_len; + pos += len - hdr_len; + + hlp_len -= len - hdr_len; + ie_len += 2 + len; + rem_len -= 2 + len; + + while (hlp_len) { + len = (hlp_len > 255) ? 255 : hlp_len; + if (rem_len < 2 + len) + break; + *buf++ = WLAN_EID_FRAGMENT; + *buf++ = len; + os_memcpy(buf, pos, len); + buf += len; + pos += len; + + hlp_len -= len; + ie_len += 2 + len; + rem_len -= 2 + len; } - wpas_connect_work_free(cwork); - return; } - wpa_s->connect_work = work; + return ie_len; +} - if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid) || - wpas_network_disabled(wpa_s, 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_memcpy(prev_bssid, wpa_s->bssid, ETH_ALEN); - os_memset(¶ms, 0, sizeof(params)); - wpa_s->reassociate = 0; - 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 */ - wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR - " (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid), - wpa_ssid_txt(bss->ssid, bss->ssid_len), bss->freq); - bssid_changed = !is_zero_ether_addr(wpa_s->bssid); - os_memset(wpa_s->bssid, 0, ETH_ALEN); - os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN); - if (bssid_changed) - wpas_notify_bssid_changed(wpa_s); -#ifdef CONFIG_IEEE80211R - ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN); - if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN) - md = ie + 2; - wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0); - if (md) { - /* Prepare for the next transition */ - wpa_ft_prepare_auth_request(wpa_s->wpa, ie); - } -#endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_WPS - } else if ((ssid->ssid == NULL || ssid->ssid_len == 0) && - wpa_s->conf->ap_scan == 2 && - (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); - return; -#endif /* CONFIG_WPS */ - } else { - wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'", - wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); - if (bss) - os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN); - else - os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); - } - if (!wpa_s->pno) - wpa_supplicant_cancel_sched_scan(wpa_s); +int wpa_is_fils_supported(struct wpa_supplicant *wpa_s) +{ + return (((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_FILS)) || + (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD))); +} - wpa_supplicant_cancel_scan(wpa_s); - /* Starting new association, so clear the possibly used WPA IE from the - * previous association. */ - wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); +int wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s) +{ +#ifdef CONFIG_FILS_SK_PFS + return (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_FILS); +#else /* CONFIG_FILS_SK_PFS */ + return 0; +#endif /* CONFIG_FILS_SK_PFS */ +} -#ifdef IEEE8021X_EAPOL - if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) { - if (ssid->leap) { - if (ssid->non_leap == 0) - algs = WPA_AUTH_ALG_LEAP; - else - algs |= WPA_AUTH_ALG_LEAP; - } +#endif /* CONFIG_FILS */ + + +static u8 * wpas_populate_assoc_ies( + struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, struct wpa_ssid *ssid, + struct wpa_driver_associate_params *params, + enum wpa_drv_update_connect_params_mask *mask) +{ + u8 *wpa_ie; + size_t max_wpa_ie_len = 500; + size_t wpa_ie_len; + int algs = WPA_AUTH_ALG_OPEN; +#ifdef CONFIG_MBO + const u8 *mbo_ie; +#endif +#ifdef CONFIG_FILS + const u8 *realm, *username, *rrk; + size_t realm_len, username_len, rrk_len; + u16 next_seq_num; + struct fils_hlp_req *req; + + dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req, + list) { + max_wpa_ie_len += 3 + 2 * ETH_ALEN + 6 + wpabuf_len(req->pkt) + + 2 + 2 * wpabuf_len(req->pkt) / 255; } -#endif /* IEEE8021X_EAPOL */ - wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs); - if (ssid->auth_alg) { - algs = ssid->auth_alg; - wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: " - "0x%x", algs); +#endif /* CONFIG_FILS */ + + wpa_ie = os_malloc(max_wpa_ie_len); + if (!wpa_ie) { + wpa_printf(MSG_ERROR, + "Failed to allocate connect IE buffer for %lu bytes", + (unsigned long) max_wpa_ie_len); + return NULL; } if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) || wpa_bss_get_ie(bss, WLAN_EID_RSN)) && wpa_key_mgmt_wpa(ssid->key_mgmt)) { int try_opportunistic; + const u8 *cache_id = NULL; + try_opportunistic = (ssid->proactive_key_caching < 0 ? wpa_s->conf->okc : ssid->proactive_key_caching) && (ssid->proto & WPA_PROTO_RSN); +#ifdef CONFIG_FILS + if (wpa_key_mgmt_fils(ssid->key_mgmt)) + cache_id = wpa_bss_get_fils_cache_id(bss); +#endif /* CONFIG_FILS */ if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, - ssid, try_opportunistic) == 0) + ssid, try_opportunistic, + cache_id, 0) == 0) eapol_sm_notify_pmkid_attempt(wpa_s->eapol); - wpa_ie_len = sizeof(wpa_ie); + wpa_ie_len = max_wpa_ie_len; 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; + os_free(wpa_ie); + return NULL; } +#ifdef CONFIG_HS20 + } else if (bss && wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE) && + (ssid->key_mgmt & WPA_KEY_MGMT_OSEN)) { + /* No PMKSA caching, but otherwise similar to RSN/WPA */ + wpa_ie_len = max_wpa_ie_len; + 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"); + os_free(wpa_ie); + return NULL; + } +#endif /* CONFIG_HS20 */ } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss && wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt)) { /* @@ -2236,20 +2521,20 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) wpa_ie_len = 0; wpa_s->wpa_proto = 0; } else if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) { - wpa_ie_len = sizeof(wpa_ie); + wpa_ie_len = max_wpa_ie_len; if (wpa_supplicant_set_suites(wpa_s, NULL, ssid, wpa_ie, &wpa_ie_len)) { 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; + os_free(wpa_ie); + return NULL; } #ifdef CONFIG_WPS } else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { struct wpabuf *wps_ie; wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid)); - if (wps_ie && wpabuf_len(wps_ie) <= sizeof(wpa_ie)) { + if (wps_ie && wpabuf_len(wps_ie) <= max_wpa_ie_len) { wpa_ie_len = wpabuf_len(wps_ie); os_memcpy(wpa_ie, wpabuf_head(wps_ie), wpa_ie_len); } else @@ -2257,9 +2542,9 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) wpabuf_free(wps_ie); wpa_supplicant_set_non_wpa_policy(wpa_s, ssid); if (!bss || (bss->caps & IEEE80211_CAP_PRIVACY)) - params.wps = WPS_MODE_PRIVACY; + params->wps = WPS_MODE_PRIVACY; else - params.wps = WPS_MODE_OPEN; + params->wps = WPS_MODE_OPEN; wpa_s->wpa_proto = 0; #endif /* CONFIG_WPS */ } else { @@ -2268,13 +2553,61 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) wpa_s->wpa_proto = 0; } +#ifdef IEEE8021X_EAPOL + if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + if (ssid->leap) { + if (ssid->non_leap == 0) + algs = WPA_AUTH_ALG_LEAP; + else + algs |= WPA_AUTH_ALG_LEAP; + } + } + +#ifdef CONFIG_FILS + /* Clear FILS association */ + wpa_sm_set_reset_fils_completed(wpa_s->wpa, 0); + + if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD) && + ssid->eap.erp && wpa_key_mgmt_fils(wpa_s->key_mgmt) && + eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap, &username, + &username_len, &realm, &realm_len, + &next_seq_num, &rrk, &rrk_len) == 0 && + (!wpa_s->last_con_fail_realm || + wpa_s->last_con_fail_realm_len != realm_len || + os_memcmp(wpa_s->last_con_fail_realm, realm, realm_len) != 0)) { + algs = WPA_AUTH_ALG_FILS; + params->fils_erp_username = username; + params->fils_erp_username_len = username_len; + params->fils_erp_realm = realm; + params->fils_erp_realm_len = realm_len; + params->fils_erp_next_seq_num = next_seq_num; + params->fils_erp_rrk = rrk; + params->fils_erp_rrk_len = rrk_len; + + if (mask) + *mask |= WPA_DRV_UPDATE_FILS_ERP_INFO; + } +#endif /* CONFIG_FILS */ +#endif /* IEEE8021X_EAPOL */ +#ifdef CONFIG_SAE + if (wpa_s->key_mgmt & (WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE)) + algs = WPA_AUTH_ALG_SAE; +#endif /* CONFIG_SAE */ + + wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs); + if (ssid->auth_alg) { + algs = ssid->auth_alg; + wpa_dbg(wpa_s, MSG_DEBUG, + "Overriding auth_alg selection: 0x%x", algs); + } + #ifdef CONFIG_P2P if (wpa_s->global->p2p) { u8 *pos; size_t len; int res; pos = wpa_ie + wpa_ie_len; - len = sizeof(wpa_ie) - wpa_ie_len; + len = max_wpa_ie_len - wpa_ie_len; res = wpas_p2p_assoc_req_ie(wpa_s, bss, pos, len, ssid->p2p_group); if (res >= 0) @@ -2299,21 +2632,12 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info)); #endif /* CONFIG_P2P */ -#ifdef CONFIG_MBO if (bss) { - mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE); - if (mbo) { - int len; - - len = wpas_mbo_supp_op_class_ie(wpa_s, bss->freq, - wpa_ie + wpa_ie_len, - sizeof(wpa_ie) - - wpa_ie_len); - if (len > 0) - wpa_ie_len += len; - } + wpa_ie_len += wpas_supp_op_class_ie(wpa_s, bss->freq, + wpa_ie + wpa_ie_len, + max_wpa_ie_len - + wpa_ie_len); } -#endif /* CONFIG_MBO */ /* * Workaround: Add Extended Capabilities element only if the AP @@ -2333,7 +2657,8 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) int ext_capab_len; ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab, sizeof(ext_capab)); - if (ext_capab_len > 0) { + if (ext_capab_len > 0 && + wpa_ie_len + ext_capab_len <= max_wpa_ie_len) { u8 *pos = wpa_ie; if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN) pos += 2 + pos[1]; @@ -2348,13 +2673,14 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) if (is_hs20_network(wpa_s, ssid, bss)) { struct wpabuf *hs20; - hs20 = wpabuf_alloc(20); + hs20 = wpabuf_alloc(20 + MAX_ROAMING_CONS_OI_LEN); if (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; + wpas_hs20_add_roam_cons_sel(hs20, ssid); + len = max_wpa_ie_len - wpa_ie_len; if (wpabuf_len(hs20) <= len) { os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(hs20), wpabuf_len(hs20)); @@ -2371,7 +2697,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]; size_t len; - len = sizeof(wpa_ie) - wpa_ie_len; + len = max_wpa_ie_len - wpa_ie_len; if (wpabuf_len(buf) <= len) { os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(buf), wpabuf_len(buf)); @@ -2383,7 +2709,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) if (wpa_s->fst_ies) { int fst_ies_len = wpabuf_len(wpa_s->fst_ies); - if (wpa_ie_len + fst_ies_len <= sizeof(wpa_ie)) { + if (wpa_ie_len + fst_ies_len <= max_wpa_ie_len) { os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(wpa_s->fst_ies), fst_ies_len); wpa_ie_len += fst_ies_len; @@ -2392,20 +2718,249 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) #endif /* CONFIG_FST */ #ifdef CONFIG_MBO - if (mbo) { + mbo_ie = bss ? wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE) : NULL; + if (mbo_ie) { int len; len = wpas_mbo_ie(wpa_s, wpa_ie + wpa_ie_len, - sizeof(wpa_ie) - wpa_ie_len); + max_wpa_ie_len - wpa_ie_len, + !!mbo_attr_from_mbo_ie(mbo_ie, + OCE_ATTR_ID_CAPA_IND)); if (len >= 0) wpa_ie_len += len; } #endif /* CONFIG_MBO */ +#ifdef CONFIG_FILS + if (algs == WPA_AUTH_ALG_FILS) { + size_t len; + + len = wpas_add_fils_hlp_req(wpa_s, wpa_ie + wpa_ie_len, + max_wpa_ie_len - wpa_ie_len); + wpa_ie_len += len; + } +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE +#ifdef CONFIG_TESTING_OPTIONS + if (get_ie_ext(wpa_ie, wpa_ie_len, WLAN_EID_EXT_OWE_DH_PARAM)) { + wpa_printf(MSG_INFO, "TESTING: Override OWE DH element"); + } else +#endif /* CONFIG_TESTING_OPTIONS */ + if (algs == WPA_AUTH_ALG_OPEN && + ssid->key_mgmt == WPA_KEY_MGMT_OWE) { + struct wpabuf *owe_ie; + u16 group; + + if (ssid->owe_group) { + group = ssid->owe_group; + } else if (wpa_s->assoc_status_code == + WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) { + if (wpa_s->last_owe_group == 19) + group = 20; + else if (wpa_s->last_owe_group == 20) + group = 21; + else + group = OWE_DH_GROUP; + } else { + group = OWE_DH_GROUP; + } + + wpa_s->last_owe_group = group; + wpa_printf(MSG_DEBUG, "OWE: Try to use group %u", group); + owe_ie = owe_build_assoc_req(wpa_s->wpa, group); + if (owe_ie && + wpabuf_len(owe_ie) <= max_wpa_ie_len - wpa_ie_len) { + os_memcpy(wpa_ie + wpa_ie_len, + wpabuf_head(owe_ie), wpabuf_len(owe_ie)); + wpa_ie_len += wpabuf_len(owe_ie); + wpabuf_free(owe_ie); + } + } +#endif /* CONFIG_OWE */ + +#ifdef CONFIG_IEEE80211R + /* + * Add MDIE under these conditions: the network profile allows FT, + * the AP supports FT, and the mobility domain ID matches. + */ + if (bss && wpa_key_mgmt_ft(wpa_sm_get_key_mgmt(wpa_s->wpa))) { + const u8 *mdie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN); + + if (mdie && mdie[1] >= MOBILITY_DOMAIN_ID_LEN) { + size_t len = 0; + const u8 *md = mdie + 2; + const u8 *wpa_md = wpa_sm_get_ft_md(wpa_s->wpa); + + if (os_memcmp(md, wpa_md, + MOBILITY_DOMAIN_ID_LEN) == 0) { + /* Add mobility domain IE */ + len = wpa_ft_add_mdie( + wpa_s->wpa, wpa_ie + wpa_ie_len, + max_wpa_ie_len - wpa_ie_len, mdie); + wpa_ie_len += len; + } +#ifdef CONFIG_SME + if (len > 0 && wpa_s->sme.ft_used && + wpa_sm_has_ptk(wpa_s->wpa)) { + wpa_dbg(wpa_s, MSG_DEBUG, + "SME: Trying to use FT over-the-air"); + algs |= WPA_AUTH_ALG_FT; + } +#endif /* CONFIG_SME */ + } + } +#endif /* CONFIG_IEEE80211R */ + + params->wpa_ie = wpa_ie; + params->wpa_ie_len = wpa_ie_len; + params->auth_alg = algs; + if (mask) + *mask |= WPA_DRV_UPDATE_ASSOC_IES | WPA_DRV_UPDATE_AUTH_TYPE; + + return wpa_ie; +} + + +#if defined(CONFIG_FILS) && defined(IEEE8021X_EAPOL) +static void wpas_update_fils_connect_params(struct wpa_supplicant *wpa_s) +{ + struct wpa_driver_associate_params params; + enum wpa_drv_update_connect_params_mask mask = 0; + u8 *wpa_ie; + + if (wpa_s->auth_alg != WPA_AUTH_ALG_OPEN) + return; /* nothing to do */ + + os_memset(¶ms, 0, sizeof(params)); + wpa_ie = wpas_populate_assoc_ies(wpa_s, wpa_s->current_bss, + wpa_s->current_ssid, ¶ms, &mask); + if (!wpa_ie) + return; + + if (params.auth_alg != WPA_AUTH_ALG_FILS) { + os_free(wpa_ie); + return; + } + + wpa_s->auth_alg = params.auth_alg; + wpa_drv_update_connect_params(wpa_s, ¶ms, mask); + os_free(wpa_ie); +} +#endif /* CONFIG_FILS && IEEE8021X_EAPOL */ + + +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; + int use_crypt, ret, i, bssid_changed; + unsigned int cipher_pairwise, cipher_group, cipher_group_mgmt; + struct wpa_driver_associate_params params; + int wep_keys_set = 0; + int assoc_failed = 0; + struct wpa_ssid *old_ssid; + u8 prev_bssid[ETH_ALEN]; +#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) || + wpas_network_disabled(wpa_s, 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_memcpy(prev_bssid, wpa_s->bssid, ETH_ALEN); + os_memset(¶ms, 0, sizeof(params)); + wpa_s->reassociate = 0; + 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 */ + wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR + " (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid), + wpa_ssid_txt(bss->ssid, bss->ssid_len), bss->freq); + bssid_changed = !is_zero_ether_addr(wpa_s->bssid); + os_memset(wpa_s->bssid, 0, ETH_ALEN); + os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN); + if (bssid_changed) + wpas_notify_bssid_changed(wpa_s); +#ifdef CONFIG_IEEE80211R + ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN); + if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN) + md = ie + 2; + wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0); + if (md) { + /* Prepare for the next transition */ + wpa_ft_prepare_auth_request(wpa_s->wpa, ie); + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_WPS + } else if ((ssid->ssid == NULL || ssid->ssid_len == 0) && + wpa_s->conf->ap_scan == 2 && + (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); + return; +#endif /* CONFIG_WPS */ + } else { + wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'", + wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + if (bss) + os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN); + else + os_memset(wpa_s->pending_bssid, 0, ETH_ALEN); + } + if (!wpa_s->pno) + wpa_supplicant_cancel_sched_scan(wpa_s); + + wpa_supplicant_cancel_scan(wpa_s); + + /* Starting new association, so clear the possibly used WPA IE from the + * previous association. */ + wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0); + + wpa_ie = wpas_populate_assoc_ies(wpa_s, bss, ssid, ¶ms, NULL); + if (!wpa_ie) { + wpas_connect_work_done(wpa_s); + return; + } + wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL); use_crypt = 1; cipher_pairwise = wpa_s->pairwise_cipher; cipher_group = wpa_s->group_cipher; + cipher_group_mgmt = wpa_s->mgmt_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) @@ -2443,12 +2998,14 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) if (bss) { params.ssid = bss->ssid; params.ssid_len = bss->ssid_len; - if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) { + if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set || + wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) { wpa_printf(MSG_DEBUG, "Limit connection to BSSID " MACSTR " freq=%u MHz based on scan results " - "(bssid_set=%d)", + "(bssid_set=%d wps=%d)", MAC2STR(bss->bssid), bss->freq, - ssid->bssid_set); + ssid->bssid_set, + wpa_s->key_mgmt == WPA_KEY_MGMT_WPS); params.bssid = bss->bssid; params.freq.freq = bss->freq; } @@ -2456,6 +3013,9 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) params.freq_hint = bss->freq; params.pbss = bss_is_pbss(bss); } else { + if (ssid->bssid_hint_set) + params.bssid_hint = ssid->bssid_hint; + params.ssid = ssid->ssid; params.ssid_len = ssid->ssid_len; params.pbss = (ssid->pbss != 2) ? ssid->pbss : 0; @@ -2480,13 +3040,12 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) 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.mgmt_group_suite = cipher_group_mgmt; params.key_mgmt_suite = wpa_s->key_mgmt; params.wpa_proto = wpa_s->wpa_proto; - params.auth_alg = algs; + wpa_s->auth_alg = params.auth_alg; params.mode = ssid->mode; params.bg_scan_period = ssid->bg_scan_period; for (i = 0; i < NUM_WEP_KEYS; i++) { @@ -2536,6 +3095,11 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) "MFP: require MFP"); params.mgmt_frame_protection = MGMT_FRAME_PROTECTION_REQUIRED; +#ifdef CONFIG_OWE + } else if (!rsn && (ssid->key_mgmt & WPA_KEY_MGMT_OWE) && + !ssid->owe_only) { + params.mgmt_frame_protection = NO_MGMT_FRAME_PROTECTION; +#endif /* CONFIG_OWE */ } } #endif /* CONFIG_IEEE80211W */ @@ -2578,6 +3142,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) if (wpas_p2p_handle_frequency_conflicts( wpa_s, params.freq.freq, ssid) < 0) { wpas_connect_work_done(wpa_s); + os_free(wpa_ie); return; } } @@ -2589,6 +3154,7 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) params.prev_bssid = prev_bssid; ret = wpa_drv_associate(wpa_s, ¶ms); + os_free(wpa_ie); if (ret < 0) { wpa_msg(wpa_s, MSG_INFO, "Association request to the driver " "failed"); @@ -2730,8 +3296,13 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s, #ifdef CONFIG_MESH if (wpa_s->ifmsh) { + struct mesh_conf *mconf; + + mconf = wpa_s->ifmsh->mconf; wpa_msg(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s", wpa_s->ifname); + wpas_notify_mesh_group_removed(wpa_s, mconf->meshid, + mconf->meshid_len, reason_code); wpa_supplicant_leave_mesh(wpa_s); } #endif /* CONFIG_MESH */ @@ -2756,6 +3327,7 @@ static void wpa_supplicant_enable_one_network(struct wpa_supplicant *wpa_s, return; ssid->disabled = 0; + ssid->owe_transition_bss_select_count = 0; wpas_clear_temp_disabled(wpa_s, ssid, 1); wpas_notify_network_enabled_changed(wpa_s, ssid); @@ -2921,13 +3493,19 @@ void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s, wpas_notify_network_enabled_changed( wpa_s, other_ssid); } - if (wpa_s->current_ssid) + if (wpa_s->current_ssid) { + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); + } } else if (ssid->disabled != 2) { - if (ssid == wpa_s->current_ssid) + if (ssid == wpa_s->current_ssid) { + if (wpa_s->wpa_state >= WPA_AUTHENTICATING) + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); + } was_disabled = ssid->disabled; @@ -3013,6 +3591,9 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, wpa_s->disconnected = 0; wpa_s->reassociate = 1; + wpa_s->last_owe_group = 0; + if (ssid) + ssid->owe_transition_bss_select_count = 0; if (wpa_s->connect_without_scan || wpa_supplicant_fast_associate(wpa_s) != 1) { @@ -3230,6 +3811,41 @@ int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level, } +#ifdef CONFIG_OWE +static int owe_trans_ssid_match(struct wpa_supplicant *wpa_s, const u8 *bssid, + const u8 *entry_ssid, size_t entry_ssid_len) +{ + const u8 *owe, *pos, *end; + u8 ssid_len; + struct wpa_bss *bss; + + /* Check network profile SSID aganst the SSID in the + * OWE Transition Mode element. */ + + bss = wpa_bss_get_bssid_latest(wpa_s, bssid); + if (!bss) + return 0; + + owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE); + if (!owe) + return 0; + + pos = owe + 6; + end = owe + 2 + owe[1]; + + if (end - pos < ETH_ALEN + 1) + return 0; + pos += ETH_ALEN; + ssid_len = *pos++; + if (end - pos < ssid_len || ssid_len > SSID_MAX_LEN) + return 0; + + return entry_ssid_len == ssid_len && + os_memcmp(pos, entry_ssid, ssid_len) == 0; +} +#endif /* CONFIG_OWE */ + + /** * wpa_supplicant_get_ssid - Get a pointer to the current network structure * @wpa_s: Pointer to wpa_supplicant data @@ -3278,6 +3894,15 @@ struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s) return entry; #endif /* CONFIG_WPS */ +#ifdef CONFIG_OWE + if (!wpas_network_disabled(wpa_s, entry) && + owe_trans_ssid_match(wpa_s, bssid, entry->ssid, + entry->ssid_len) && + (!entry->bssid_set || + os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) + return entry; +#endif /* CONFIG_OWE */ + if (!wpas_network_disabled(wpa_s, entry) && entry->bssid_set && entry->ssid_len == 0 && os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0) @@ -3385,16 +4010,6 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, } #endif /* CONFIG_TESTING_OPTIONS */ -#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 @@ -3505,6 +4120,8 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, os_memcpy(wpa_s->last_eapol_src, src_addr, ETH_ALEN); if (!wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) && + wpa_s->key_mgmt != WPA_KEY_MGMT_OWE && + wpa_s->key_mgmt != WPA_KEY_MGMT_DPP && eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len) > 0) return; wpa_drv_poll(wpa_s); @@ -3534,6 +4151,11 @@ int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s) wpa_supplicant_rx_eapol, wpa_s, 0); if (wpa_s->l2 == NULL) return -1; + + if (l2_packet_set_packet_filter(wpa_s->l2, + L2_PACKET_FILTER_PKTTYPE)) + wpa_dbg(wpa_s, MSG_DEBUG, + "Failed to attach pkt_type filter"); } else { const u8 *addr = wpa_drv_get_mac_addr(wpa_s); if (addr) @@ -3673,6 +4295,7 @@ wpa_supplicant_alloc(struct wpa_supplicant *parent) wpa_s->sched_scanning = 0; dl_list_init(&wpa_s->bss_tmp_disallowed); + dl_list_init(&wpa_s->fils_hlp_req); return wpa_s; } @@ -3700,8 +4323,11 @@ static int wpa_set_htcap_mcs(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_DEBUG, "set_htcap, ht_mcs -:%s:-", ht_mcs); for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { + long v; + errno = 0; - long v = strtol(tmp, &end, 16); + v = strtol(tmp, &end, 16); + if (errno == 0) { wpa_msg(wpa_s, MSG_DEBUG, "htcap value[%i]: %ld end: %p tmp: %p", @@ -3811,18 +4437,10 @@ static int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s, struct ieee80211_ht_capabilities *htcaps_mask, int disabled) { - /* Masking these out disables HT40 */ - 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); - if (disabled) - htcaps->ht_capabilities_info &= ~msk; - else - htcaps->ht_capabilities_info |= msk; - - htcaps_mask->ht_capabilities_info |= msk; + set_disable_ht40(htcaps, disabled); + set_disable_ht40(htcaps_mask, 0); return 0; } @@ -4098,10 +4716,14 @@ static int wpas_fst_send_action_cb(void *ctx, const u8 *da, struct wpabuf *data) { struct wpa_supplicant *wpa_s = ctx; - WPA_ASSERT(os_memcmp(wpa_s->bssid, da, ETH_ALEN) == 0); + if (os_memcmp(wpa_s->bssid, da, ETH_ALEN) != 0) { + wpa_printf(MSG_INFO, "FST:%s:bssid=" MACSTR " != da=" MACSTR, + __func__, MAC2STR(wpa_s->bssid), MAC2STR(da)); + return -1; + } return wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, - wpa_s->own_addr, wpa_s->bssid, - wpabuf_head(data), wpabuf_len(data), + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(data), wpabuf_len(data), 0); } @@ -4289,7 +4911,7 @@ static void radio_work_free(struct wpa_radio_work *work) if (work->started) { work->wpa_s->radio->num_active_works--; wpa_dbg(work->wpa_s, MSG_DEBUG, - "radio_work_free('%s'@%p: num_active_works --> %u", + "radio_work_free('%s'@%p): num_active_works --> %u", work->type, work, work->wpa_s->radio->num_active_works); } @@ -4299,6 +4921,20 @@ static void radio_work_free(struct wpa_radio_work *work) } +static int radio_work_is_connect(struct wpa_radio_work *work) +{ + return os_strcmp(work->type, "sme-connect") == 0 || + os_strcmp(work->type, "connect") == 0; +} + + +static int radio_work_is_scan(struct wpa_radio_work *work) +{ + return os_strcmp(work->type, "scan") == 0 || + os_strcmp(work->type, "p2p-scan") == 0; +} + + static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio) { struct wpa_radio_work *active_work = NULL; @@ -4328,8 +4964,7 @@ static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio) return NULL; } - if (os_strcmp(active_work->type, "sme-connect") == 0 || - os_strcmp(active_work->type, "connect") == 0) { + if (radio_work_is_connect(active_work)) { /* * If the active work is either connect or sme-connect, * do not parallelize them with other radio works. @@ -4348,10 +4983,20 @@ static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio) * If connect or sme-connect are enqueued, parallelize only * those operations ahead of them in the queue. */ - if (os_strcmp(tmp->type, "connect") == 0 || - os_strcmp(tmp->type, "sme-connect") == 0) + if (radio_work_is_connect(tmp)) break; + /* Serialize parallel scan and p2p_scan operations on the same + * interface since the driver_nl80211 mechanism for tracking + * scan cookies does not yet have support for this. */ + if (active_work->wpa_s == tmp->wpa_s && + radio_work_is_scan(active_work) && + radio_work_is_scan(tmp)) { + wpa_dbg(active_work->wpa_s, MSG_DEBUG, + "Do not start work '%s' when another work '%s' is already scheduled", + tmp->type, active_work->type); + continue; + } /* * Check that the radio works are distinct and * on different bands. @@ -4473,6 +5118,22 @@ void radio_remove_works(struct wpa_supplicant *wpa_s, } +void radio_remove_pending_work(struct wpa_supplicant *wpa_s, void *ctx) +{ + 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->ctx != ctx) + continue; + wpa_dbg(wpa_s, MSG_DEBUG, "Free pending radio work '%s'@%p%s", + work->type, work, work->started ? " (started)" : ""); + radio_work_free(work); + break; + } +} + + static void radio_remove_interface(struct wpa_supplicant *wpa_s) { struct wpa_radio *radio = wpa_s->radio; @@ -4625,7 +5286,7 @@ radio_work_pending(struct wpa_supplicant *wpa_s, const char *type) static int wpas_init_driver(struct wpa_supplicant *wpa_s, - struct wpa_interface *iface) + const struct wpa_interface *iface) { const char *ifname, *driver, *rn; @@ -4673,11 +5334,47 @@ next_driver: } +#ifdef CONFIG_GAS_SERVER + +static void wpas_gas_server_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) +{ + wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR + " result=%s", + freq, MAC2STR(dst), + result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" : + (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" : + "FAILED")); + gas_server_tx_status(wpa_s->gas_server, dst, data, data_len, + result == OFFCHANNEL_SEND_ACTION_SUCCESS); +} + + +static void wpas_gas_server_tx(void *ctx, int freq, const u8 *da, + struct wpabuf *buf, unsigned int wait_time) +{ + struct wpa_supplicant *wpa_s = ctx; + const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + if (wait_time > wpa_s->max_remain_on_chan) + wait_time = wpa_s->max_remain_on_chan; + + offchannel_send_action(wpa_s, freq, da, wpa_s->own_addr, broadcast, + wpabuf_head(buf), wpabuf_len(buf), + wait_time, wpas_gas_server_tx_status, 0); +} + +#endif /* CONFIG_GAS_SERVER */ + static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, - struct wpa_interface *iface) + const struct wpa_interface *iface) { struct wpa_driver_capa capa; int capa_res; + u8 dfs_domain; wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver " "'%s' ctrl_interface '%s' bridge '%s'", iface->ifname, @@ -4707,7 +5404,13 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, return -1; } wpa_s->confanother = os_rel2abs_path(iface->confanother); - wpa_config_read(wpa_s->confanother, wpa_s->conf); + if (wpa_s->confanother && + !wpa_config_read(wpa_s->confanother, wpa_s->conf)) { + wpa_printf(MSG_ERROR, + "Failed to read or parse configuration '%s'.", + wpa_s->confanother); + return -1; + } /* * Override ctrl_interface and driver_param if set on command @@ -4805,7 +5508,8 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, wpa_s->hw.modes = wpa_drv_get_hw_feature_data(wpa_s, &wpa_s->hw.num_modes, - &wpa_s->hw.flags); + &wpa_s->hw.flags, + &dfs_domain); if (wpa_s->hw.modes) { u16 i; @@ -4867,8 +5571,6 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, */ 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; @@ -4877,10 +5579,7 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, return -1; #ifdef CONFIG_TDLS - if ((!iface->p2p_mgmt || - !(wpa_s->drv_flags & - WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) && - wpa_tdls_init(wpa_s->wpa)) + if (!iface->p2p_mgmt && wpa_tdls_init(wpa_s->wpa)) return -1; #endif /* CONFIG_TDLS */ @@ -4915,6 +5614,19 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, if (wpas_wps_init(wpa_s)) return -1; +#ifdef CONFIG_GAS_SERVER + wpa_s->gas_server = gas_server_init(wpa_s, wpas_gas_server_tx); + if (!wpa_s->gas_server) { + wpa_printf(MSG_ERROR, "Failed to initialize GAS server"); + return -1; + } +#endif /* CONFIG_GAS_SERVER */ + +#ifdef CONFIG_DPP + if (wpas_dpp_init(wpa_s) < 0) + return -1; +#endif /* CONFIG_DPP */ + if (wpa_supplicant_init_eapol(wpa_s) < 0) return -1; wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol); @@ -4939,7 +5651,9 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, return -1; } - if (iface->p2p_mgmt && wpas_p2p_init(wpa_s->global, wpa_s) < 0) { + if ((!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) || + wpa_s->p2p_mgmt) && + wpas_p2p_init(wpa_s->global, wpa_s) < 0) { wpa_msg(wpa_s, MSG_ERROR, "Failed to init P2P"); return -1; } @@ -4947,6 +5661,12 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, if (wpa_bss_init(wpa_s) < 0) return -1; +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL +#ifdef CONFIG_MESH + dl_list_init(&wpa_s->mesh_external_pmksa_cache); +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ + /* * Set Wake-on-WLAN triggers, if configured. * Note: We don't restore/remove the triggers on shutdown (it doesn't @@ -4958,8 +5678,8 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, #ifdef CONFIG_EAP_PROXY { size_t len; - wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, wpa_s->imsi, - &len); + wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, -1, + 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)", @@ -4984,6 +5704,17 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, hs20_init(wpa_s); #endif /* CONFIG_HS20 */ #ifdef CONFIG_MBO + if (wpa_s->conf->oce) { + if ((wpa_s->conf->oce & OCE_STA) && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA)) + wpa_s->enable_oce = OCE_STA; + if ((wpa_s->conf->oce & OCE_STA_CFON) && + (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OCE_STA_CFON)) { + /* TODO: Need to add STA-CFON support */ + wpa_printf(MSG_ERROR, + "OCE STA-CFON feature is not yet supported"); + } + } wpas_mbo_update_non_pref_chan(wpa_s, wpa_s->conf->non_pref_chan); #endif /* CONFIG_MBO */ @@ -5248,6 +5979,7 @@ int wpa_supplicant_remove_iface(struct wpa_global *global, #ifdef CONFIG_MESH unsigned int mesh_if_created = wpa_s->mesh_if_created; char *ifname = NULL; + struct wpa_supplicant *parent = wpa_s->parent; #endif /* CONFIG_MESH */ /* Remove interface from the global list of interfaces */ @@ -5283,7 +6015,7 @@ int wpa_supplicant_remove_iface(struct wpa_global *global, #ifdef CONFIG_MESH if (mesh_if_created) { - wpa_drv_if_remove(global->ifaces, WPA_IF_MESH, ifname); + wpa_drv_if_remove(parent, WPA_IF_MESH, ifname); os_free(ifname); } #endif /* CONFIG_MESH */ @@ -5647,6 +6379,16 @@ void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s) if (wpa_s->conf->changed_parameters & CFG_CHANGED_SCHED_SCAN_PLANS) wpas_sched_scan_plans_set(wpa_s, wpa_s->conf->sched_scan_plans); + if (wpa_s->conf->changed_parameters & CFG_CHANGED_WOWLAN_TRIGGERS) { + struct wpa_driver_capa capa; + int res = wpa_drv_get_capa(wpa_s, &capa); + + if (res == 0 && wpas_set_wowlan_triggers(wpa_s, &capa) < 0) + wpa_printf(MSG_ERROR, + "Failed to update wowlan_triggers to '%s'", + wpa_s->conf->wowlan_triggers); + } + #ifdef CONFIG_WPS wpas_wps_update_config(wpa_s); #endif /* CONFIG_WPS */ @@ -5806,6 +6548,35 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid) } +#ifdef CONFIG_FILS +void fils_connection_failure(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + const u8 *realm, *username, *rrk; + size_t realm_len, username_len, rrk_len; + u16 next_seq_num; + + if (!ssid || !ssid->eap.erp || !wpa_key_mgmt_fils(ssid->key_mgmt) || + eapol_sm_get_erp_info(wpa_s->eapol, &ssid->eap, + &username, &username_len, + &realm, &realm_len, &next_seq_num, + &rrk, &rrk_len) != 0 || + !realm) + return; + + wpa_hexdump_ascii(MSG_DEBUG, + "FILS: Store last connection failure realm", + realm, realm_len); + os_free(wpa_s->last_con_fail_realm); + wpa_s->last_con_fail_realm = os_malloc(realm_len); + if (wpa_s->last_con_fail_realm) { + wpa_s->last_con_fail_realm_len = realm_len; + os_memcpy(wpa_s->last_con_fail_realm, realm, realm_len); + } +} +#endif /* CONFIG_FILS */ + + int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s) { return wpa_s->conf->ap_scan == 2 || @@ -5876,6 +6647,7 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s, case WPA_CTRL_REQ_SIM: str_clear_free(eap->external_sim_resp); eap->external_sim_resp = os_strdup(value); + eap->pending_req_sim = 0; break; case WPA_CTRL_REQ_PSK_PASSPHRASE: if (wpa_config_set(ssid, "psk", value, 0) < 0) @@ -5944,6 +6716,7 @@ 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->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk && + !(wpa_key_mgmt_sae(ssid->key_mgmt) && ssid->sae_password) && !ssid->mem_only_psk) return 1; @@ -6128,6 +6901,7 @@ void wpas_request_connection(struct wpa_supplicant *wpa_s) wpa_s->extra_blacklist_count = 0; wpa_s->disconnected = 0; wpa_s->reassociate = 1; + wpa_s->last_owe_group = 0; if (wpa_supplicant_fast_associate(wpa_s) != 1) wpa_supplicant_req_scan(wpa_s, 0, 0); @@ -6254,489 +7028,6 @@ int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, } -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 - -/* Measurement Request element + Location Subject + Maximum Age subelement */ -#define MEASURE_REQUEST_LCI_LEN (3 + 1 + 4) -/* Measurement Request element + Location Civic Request */ -#define MEASURE_REQUEST_CIVIC_LEN (3 + 5) - - -/** - * 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. - * @lci: if set, neighbor request will include LCI request - * @civic: if set, neighbor request will include civic location 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_value *ssid, - int lci, int civic, - 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) + - (lci ? 2 + MEASURE_REQUEST_LCI_LEN : 0) + - (civic ? 2 + MEASURE_REQUEST_CIVIC_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); - } - - if (lci) { - /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */ - wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); - wpabuf_put_u8(buf, MEASURE_REQUEST_LCI_LEN); - - /* - * Measurement token; nonzero number that is unique among the - * Measurement Request elements in a particular frame. - */ - wpabuf_put_u8(buf, 1); /* Measurement Token */ - - /* - * Parallel, Enable, Request, and Report bits are 0, Duration is - * reserved. - */ - wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ - wpabuf_put_u8(buf, MEASURE_TYPE_LCI); /* Measurement Type */ - - /* IEEE P802.11-REVmc/D5.0 9.4.2.21.10 - LCI request */ - /* Location Subject */ - wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); - - /* Optional Subelements */ - /* - * IEEE P802.11-REVmc/D5.0 Figure 9-170 - * The Maximum Age subelement is required, otherwise the AP can - * send only data that was determined after receiving the - * request. Setting it here to unlimited age. - */ - wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE); - wpabuf_put_u8(buf, 2); - wpabuf_put_le16(buf, 0xffff); - } - - if (civic) { - /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */ - wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); - wpabuf_put_u8(buf, MEASURE_REQUEST_CIVIC_LEN); - - /* - * Measurement token; nonzero number that is unique among the - * Measurement Request elements in a particular frame. - */ - wpabuf_put_u8(buf, 2); /* Measurement Token */ - - /* - * Parallel, Enable, Request, and Report bits are 0, Duration is - * reserved. - */ - wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ - /* Measurement Type */ - wpabuf_put_u8(buf, MEASURE_TYPE_LOCATION_CIVIC); - - /* IEEE P802.11-REVmc/D5.0 9.4.2.21.14: - * Location Civic request */ - /* Location Subject */ - wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); - wpabuf_put_u8(buf, 0); /* Civic Location Type: IETF RFC 4776 */ - /* Location Service Interval Units: Seconds */ - wpabuf_put_u8(buf, 0); - /* Location Service Interval: 0 - Only one report is requested - */ - wpabuf_put_le16(buf, 0); - /* No optional subelements */ - } - - 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; -} - - -static struct wpabuf * wpas_rrm_build_lci_report(struct wpa_supplicant *wpa_s, - const u8 *request, size_t len, - struct wpabuf *report) -{ - u8 token, type, subject; - u16 max_age = 0; - struct os_reltime t, diff; - unsigned long diff_l; - u8 *ptoken; - const u8 *subelem; - - if (!wpa_s->lci || len < 3 + 4) - return report; - - token = *request++; - /* Measurement request mode isn't used */ - request++; - type = *request++; - subject = *request++; - - wpa_printf(MSG_DEBUG, - "Measurement request token %u type %u location subject %u", - token, type, subject); - - if (type != MEASURE_TYPE_LCI || subject != LOCATION_SUBJECT_REMOTE) { - wpa_printf(MSG_INFO, - "Not building LCI report - bad type or location subject"); - return report; - } - - /* Subelements are formatted exactly like elements */ - subelem = get_ie(request, len, LCI_REQ_SUBELEM_MAX_AGE); - if (subelem && subelem[1] == 2) - max_age = WPA_GET_LE16(subelem + 2); - - if (os_get_reltime(&t)) - return report; - - os_reltime_sub(&t, &wpa_s->lci_time, &diff); - /* LCI age is calculated in 10th of a second units. */ - diff_l = diff.sec * 10 + diff.usec / 100000; - - if (max_age != 0xffff && max_age < diff_l) - return report; - - if (wpabuf_resize(&report, 2 + wpabuf_len(wpa_s->lci))) - return report; - - wpabuf_put_u8(report, WLAN_EID_MEASURE_REPORT); - wpabuf_put_u8(report, wpabuf_len(wpa_s->lci)); - /* We'll override user's measurement token */ - ptoken = wpabuf_put(report, 0); - wpabuf_put_buf(report, wpa_s->lci); - *ptoken = token; - - return report; -} - - -void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s, - const u8 *src, - const u8 *frame, size_t len) -{ - struct wpabuf *buf, *report; - u8 token; - const u8 *ie, *end; - - if (wpa_s->wpa_state != WPA_COMPLETED) { - wpa_printf(MSG_INFO, - "RRM: Ignoring radio measurement request: Not associated"); - return; - } - - if (!wpa_s->rrm.rrm_used) { - wpa_printf(MSG_INFO, - "RRM: Ignoring radio measurement request: Not RRM network"); - return; - } - - if (len < 3) { - wpa_printf(MSG_INFO, - "RRM: Ignoring too short radio measurement request"); - return; - } - - end = frame + len; - - token = *frame++; - - /* Ignore number of repetitions because it's not used in LCI request */ - frame += 2; - - report = NULL; - while ((ie = get_ie(frame, end - frame, WLAN_EID_MEASURE_REQUEST)) && - ie[1] >= 3) { - u8 msmt_type; - - msmt_type = ie[4]; - wpa_printf(MSG_DEBUG, "RRM request %d", msmt_type); - - switch (msmt_type) { - case MEASURE_TYPE_LCI: - report = wpas_rrm_build_lci_report(wpa_s, ie + 2, ie[1], - report); - break; - default: - wpa_printf(MSG_INFO, - "RRM: Unsupported radio measurement request %d", - msmt_type); - break; - } - - frame = ie + ie[1] + 2; - } - - if (!report) - return; - - buf = wpabuf_alloc(3 + wpabuf_len(report)); - if (!buf) { - wpabuf_free(report); - return; - } - - wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); - wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REPORT); - wpabuf_put_u8(buf, token); - - wpabuf_put_buf(buf, report); - wpabuf_free(report); - - 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: Radio measurement report failed: Sending Action frame failed"); - } - wpabuf_free(buf); -} - - -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); -} - - struct wpa_supplicant * wpas_vendor_elem(struct wpa_supplicant *wpa_s, enum wpa_vendor_elem_frame frame) { @@ -6850,18 +7141,56 @@ wpa_bss_tmp_disallowed * wpas_get_disallowed_bss(struct wpa_supplicant *wpa_s, } +static int wpa_set_driver_tmp_disallow_list(struct wpa_supplicant *wpa_s) +{ + struct wpa_bss_tmp_disallowed *tmp; + unsigned int num_bssid = 0; + u8 *bssids; + int ret; + + bssids = os_malloc(dl_list_len(&wpa_s->bss_tmp_disallowed) * ETH_ALEN); + if (!bssids) + return -1; + dl_list_for_each(tmp, &wpa_s->bss_tmp_disallowed, + struct wpa_bss_tmp_disallowed, list) { + os_memcpy(&bssids[num_bssid * ETH_ALEN], tmp->bssid, + ETH_ALEN); + num_bssid++; + } + ret = wpa_drv_set_bssid_blacklist(wpa_s, num_bssid, bssids); + os_free(bssids); + return ret; +} + + +static void wpa_bss_tmp_disallow_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_bss_tmp_disallowed *tmp, *bss = timeout_ctx; + + /* Make sure the bss is not already freed */ + dl_list_for_each(tmp, &wpa_s->bss_tmp_disallowed, + struct wpa_bss_tmp_disallowed, list) { + if (bss == tmp) { + dl_list_del(&tmp->list); + os_free(tmp); + wpa_set_driver_tmp_disallow_list(wpa_s); + break; + } + } +} + + void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid, unsigned int sec) { struct wpa_bss_tmp_disallowed *bss; - struct os_reltime until; - - os_get_reltime(&until); - until.sec += sec; bss = wpas_get_disallowed_bss(wpa_s, bssid); if (bss) { - bss->disallowed_until = until; + eloop_cancel_timeout(wpa_bss_tmp_disallow_timeout, wpa_s, bss); + eloop_register_timeout(sec, 0, wpa_bss_tmp_disallow_timeout, + wpa_s, bss); return; } @@ -6872,27 +7201,20 @@ void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid, return; } - bss->disallowed_until = until; os_memcpy(bss->bssid, bssid, ETH_ALEN); dl_list_add(&wpa_s->bss_tmp_disallowed, &bss->list); + wpa_set_driver_tmp_disallow_list(wpa_s); + eloop_register_timeout(sec, 0, wpa_bss_tmp_disallow_timeout, + wpa_s, bss); } int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid) { struct wpa_bss_tmp_disallowed *bss = NULL, *tmp, *prev; - struct os_reltime now, age; - - os_get_reltime(&now); dl_list_for_each_safe(tmp, prev, &wpa_s->bss_tmp_disallowed, struct wpa_bss_tmp_disallowed, list) { - if (!os_reltime_before(&now, &tmp->disallowed_until)) { - /* This BSS is not disallowed anymore */ - dl_list_del(&tmp->list); - os_free(tmp); - continue; - } if (os_memcmp(bssid, tmp->bssid, ETH_ALEN) == 0) { bss = tmp; break; @@ -6901,9 +7223,5 @@ int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid) if (!bss) return 0; - os_reltime_sub(&bss->disallowed_until, &now, &age); - wpa_printf(MSG_DEBUG, - "BSS " MACSTR " disabled for %ld.%0ld seconds", - MAC2STR(bss->bssid), age.sec, age.usec); return 1; } diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf index b2e49d8ae325..250b18720222 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant.conf +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant.conf @@ -87,9 +87,7 @@ eapol_version=1 # parameters (e.g., WPA IE generation); this mode can also be used with # 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. +# also be used when using wired Ethernet drivers (including MACsec). # 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, @@ -173,13 +171,13 @@ fast_reauth=1 # 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. +# ciphers. If not set, the value configured at build time ("DEFAULT:!EXP:!LOW" +# by default) is used. # 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 @@ -208,9 +206,15 @@ fast_reauth=1 # Wi-Fi Protected Setup (WPS) parameters # Universally Unique IDentifier (UUID; see RFC 4122) of the device -# If not configured, UUID will be generated based on the local MAC address. +# If not configured, UUID will be generated based on the mechanism selected with +# the auto_uuid parameter. #uuid=12345678-9abc-def0-1234-56789abcdef0 +# Automatic UUID behavior +# 0 = generate static value based on the local MAC address (default) +# 1 = generate a random UUID every time wpa_supplicant starts +#auto_uuid=0 + # Device Name # User-friendly description of device; up to 32 octets encoded in UTF-8 #device_name=Wireless Client @@ -412,11 +416,50 @@ fast_reauth=1 # 2 = like 1, but maintain OUI (with local admin bit set) #preassoc_mac_addr=0 +# MAC address policy for GAS operations +# 0 = use permanent MAC address +# 1 = use random MAC address +# 2 = like 1, but maintain OUI (with local admin bit set) +#gas_rand_mac_addr=0 + +# Lifetime of GAS random MAC address in seconds (default: 60) +#gas_rand_addr_lifetime=60 + # Interworking (IEEE 802.11u) # Enable Interworking # interworking=1 +# Enable P2P GO advertisement of Interworking +# go_interworking=1 + +# P2P GO Interworking: Access Network Type +# 0 = Private network +# 1 = Private network with guest access +# 2 = Chargeable public network +# 3 = Free public network +# 4 = Personal device network +# 5 = Emergency services only network +# 14 = Test or experimental +# 15 = Wildcard +#go_access_network_type=0 + +# P2P GO Interworking: Whether the network provides connectivity to the Internet +# 0 = Unspecified +# 1 = Network provides connectivity to the Internet +#go_internet=1 + +# P2P GO Interworking: Group Venue Info (optional) +# The available values are defined in IEEE Std 802.11-2016, 9.4.1.35. +# Example values (group,type): +# 0,0 = Unspecified +# 1,7 = Convention Center +# 1,13 = Coffee Shop +# 2,0 = Unspecified Business +# 7,1 Private Residence +#go_venue_group=7 +#go_venue_type=1 + # Homogenous ESS identifier # If this is set, scans will be used to request response only from BSSes # belonging to the specified Homogeneous ESS. This is used only if interworking @@ -542,6 +585,20 @@ fast_reauth=1 # pre-configured with the credential since the NAI Realm information # may not be available or fetched. # +# required_roaming_consortium: Required Roaming Consortium OI +# If required_roaming_consortium_len is non-zero, this field contains the +# Roaming Consortium OI that is required to be advertised by the AP for +# the credential to be considered matching. +# +# roaming_consortiums: Roaming Consortium OI(s) memberships +# This string field contains one or more comma delimited OIs (hexdump) +# identifying the roaming consortiums of which the provider is a member. +# The list is sorted from the most preferred one to the least preferred +# one. A match between the Roaming Consortium OIs advertised by an AP and +# the OIs in this list indicates that successful authentication is +# possible. +# (Hotspot 2.0 PerProviderSubscription/<X+>/HomeSP/RoamingConsortiumOI) +# # eap: Pre-configured EAP method # This optional field can be used to specify which EAP method will be # used with this credential. If not set, the EAP method is selected @@ -669,7 +726,7 @@ fast_reauth=1 # Format: # non_pref_chan=<oper_class>:<chan>:<preference>:<reason> # Example: -# non_pref_chan="81:5:10:2 81:1:0:2 81:9:0:2" +# non_pref_chan=81:5:10:2 81:1:0:2 81:9:0:2 # MBO Cellular Data Capabilities # 1 = Cellular data connection available @@ -677,6 +734,13 @@ fast_reauth=1 # 3 = Not cellular capable (default) #mbo_cell_capa=3 +# Optimized Connectivity Experience (OCE) +# oce: Enable OCE features (bitmap) +# Set BIT(0) to Enable OCE in non-AP STA mode (default; disabled if the driver +# does not indicate support for OCE in STA mode) +# Set BIT(1) to Enable OCE in STA-CFON mode +#oce=1 + # network block # # Each network (usually AP's sharing the same SSID) is configured as a separate @@ -789,6 +853,7 @@ fast_reauth=1 # proto: list of accepted protocols # WPA = WPA/IEEE 802.11i/D3.0 # RSN = WPA2/IEEE 802.11i (also WPA2 can be used as an alias for RSN) +# Note that RSN is used also for WPA3. # If not set, this defaults to: WPA RSN # # key_mgmt: list of accepted authenticated key management protocols @@ -801,15 +866,23 @@ fast_reauth=1 # instead) # FT-PSK = Fast BSS Transition (IEEE 802.11r) with pre-shared key # FT-EAP = Fast BSS Transition (IEEE 802.11r) with EAP authentication +# FT-EAP-SHA384 = Fast BSS Transition (IEEE 802.11r) with EAP authentication +# and using SHA384 # WPA-PSK-SHA256 = Like WPA-PSK but using stronger SHA256-based algorithms # WPA-EAP-SHA256 = Like WPA-EAP but using stronger SHA256-based algorithms # SAE = Simultaneous authentication of equals; pre-shared key/password -based # authentication with stronger security than WPA-PSK especially when using -# not that strong password +# not that strong password; a.k.a. WPA3-Personal # FT-SAE = SAE with FT # WPA-EAP-SUITE-B = Suite B 128-bit level # WPA-EAP-SUITE-B-192 = Suite B 192-bit level # OSEN = Hotspot 2.0 Rel 2 online signup connection +# FILS-SHA256 = Fast Initial Link Setup with SHA256 +# FILS-SHA384 = Fast Initial Link Setup with SHA384 +# FT-FILS-SHA256 = FT and Fast Initial Link Setup with SHA256 +# FT-FILS-SHA384 = FT and Fast Initial Link Setup with SHA384 +# OWE = Opportunistic Wireless Encryption (a.k.a. Enhanced Open) +# DPP = Device Provisioning Protocol # If not set, this defaults to: WPA-PSK WPA-EAP # # ieee80211w: whether management frame protection is enabled @@ -843,6 +916,14 @@ fast_reauth=1 # WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key [IEEE 802.11] # If not set, this defaults to: CCMP TKIP WEP104 WEP40 # +# group_mgmt: list of accepted group management ciphers for RSN (PMF) +# AES-128-CMAC = BIP-CMAC-128 +# BIP-GMAC-128 +# BIP-GMAC-256 +# BIP-CMAC-256 +# If not set, no constraint on the cipher, i.e., accept whichever cipher the AP +# indicates. +# # psk: WPA preshared key; 256-bit pre-shared key # The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e., # 32 bytes or as an ASCII passphrase (in which case, the real PSK will be @@ -860,22 +941,54 @@ fast_reauth=1 # 1 = do not store psk/passphrase to the configuration file #mem_only_psk=0 # +# sae_password: SAE password +# This parameter can be used to set a password for SAE. By default, the +# passphrase from the psk parameter is used if this separate parameter is not +# used, but psk follows the WPA-PSK constraints (8..63 characters) even though +# SAE passwords do not have such constraints. +# +# sae_password_id: SAE password identifier +# This parameter can be used to set an identifier for the SAE password. By +# default, no such identifier is used. If set, the specified identifier value +# is used by the other peer to select which password to use for authentication. +# # eapol_flags: IEEE 802.1X/EAPOL options (bit field) # Dynamic WEP key required for non-WPA mode # 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 (including macsec_qca driver), +# Note: When using wired authentication (including MACsec drivers), # 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. +# This determines how sessions are secured with MACsec (only for MACsec +# drivers). # 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. # +# macsec_integ_only: IEEE 802.1X/MACsec transmit mode +# This setting applies only when MACsec is in use, i.e., +# - macsec_policy is enabled +# - the key server has decided to enable MACsec +# 0: Encrypt traffic (default) +# 1: Integrity only +# +# macsec_port: IEEE 802.1X/MACsec port +# Port component of the SCI +# Range: 1-65534 (default: 1) +# +# mka_cak, mka_ckn, and mka_priority: IEEE 802.1X/MACsec pre-shared key mode +# This allows to configure MACsec with a pre-shared key using a (CAK,CKN) pair. +# In this mode, instances of wpa_supplicant can act as MACsec peers. The peer +# with lower priority will become the key server and start distributing SAKs. +# mka_cak (CAK = Secure Connectivity Association Key) takes a 16-bytes (128 bit) +# hex-string (32 hex-digits) +# mka_ckn (CKN = CAK Name) takes a 32-bytes (256 bit) hex-string (64 hex-digits) +# mka_priority (Priority of MKA Actor) is in 0..255 range with 255 being +# default priority +# # mixed_cell: This option can be used to configure whether so called mixed # cells, i.e., networks that use both plaintext and encryption in the same # SSID, are allowed when selecting a BSS from scan results. @@ -891,18 +1004,12 @@ fast_reauth=1 # hex without quotation, e.g., 0102030405) # wep_tx_keyidx: Default WEP key index (TX) (0..3) # -# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e DLS) is -# allowed. This is only used with RSN/WPA2. -# 0 = disabled (default) -# 1 = enabled -#peerkey=1 -# # wpa_ptk_rekey: Maximum lifetime for PTK in seconds. This can be used to # enforce rekeying of PTK to mitigate some attacks against TKIP deficiencies. # # group_rekey: Group rekeying time in seconds. This value, if non-zero, is used # as the dot11RSNAConfigGroupRekeyTime parameter when operating in -# Authenticator role in IBSS. +# Authenticator role in IBSS, or in AP and mesh modes. # # Following fields are only used with internal EAP implementation. # eap: space-separated list of accepted EAP methods @@ -1101,12 +1208,17 @@ fast_reauth=1 # 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) +# tls_disable_tlsv1_3=1 - disable use of TLSv1.3 (a workaround for AAA servers +# that have issues interoperating with updated TLS version) # tls_ext_cert_check=0 - No external server certificate validation (default) # tls_ext_cert_check=1 - External server certificate validation enabled; this # requires an external program doing validation of server certificate # chain when receiving CTRL-RSP-EXT_CERT_CHECK event from the control # interface and report the result of the validation with # CTRL-RSP_EXT_CERT_CHECK. +# tls_suiteb=0 - do not apply Suite B 192-bit constraints on TLS (default) +# tls_suiteb=1 - apply Suite B 192-bit constraints on TLS; this is used in +# particular when using Suite B with RSA keys of >= 3K (3072) bits # # Following certificate/private key fields are used in inner Phase2 # authentication when using EAP-TTLS or EAP-PEAP. @@ -1175,6 +1287,10 @@ fast_reauth=1 # update_identifier: PPS MO ID # (Hotspot 2.0 PerProviderSubscription/UpdateIdentifier) +# +# roaming_consortium_selection: Roaming Consortium Selection +# The matching Roaming Consortium OI that was used to generate this +# network profile. # Station inactivity limit # @@ -1204,6 +1320,11 @@ fast_reauth=1 # 1 = WPS disabled #wps_disabled=0 +# FILS DH Group +# 0 = PFS disabled with FILS shared key authentication (default) +# 1-65535 = DH Group to use for FILS PFS +#fils_dh_group=0 + # MAC address policy # 0 = use permanent MAC address # 1 = use random MAC address for each ESS connection @@ -1662,15 +1783,26 @@ network={ } -# 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 -#} +# Example configuration using EAP-TTLS for authentication and key +# generation for MACsec +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 +} + +# Example configuration for MACsec with preshared key +network={ + key_mgmt=NONE + eapol_flags=0 + macsec_policy=1 + mka_cak=0123456789ABCDEF0123456789ABCDEF + mka_ckn=6162636465666768696A6B6C6D6E6F707172737475767778797A303132333435 + mka_priority=128 +} diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h index ef9273d09a32..8b749f44e235 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant_i.h @@ -9,6 +9,7 @@ #ifndef WPA_SUPPLICANT_I_H #define WPA_SUPPLICANT_I_H +#include "utils/bitfield.h" #include "utils/list.h" #include "common/defs.h" #include "common/sae.h" @@ -295,7 +296,7 @@ struct wpa_global { #ifdef CONFIG_WIFI_DISPLAY int wifi_display; -#define MAX_WFD_SUBELEMS 10 +#define MAX_WFD_SUBELEMS 12 struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS]; #endif /* CONFIG_WIFI_DISPLAY */ @@ -344,6 +345,7 @@ int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq, 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_remove_pending_work(struct wpa_supplicant *wpa_s, void *ctx); 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); @@ -424,6 +426,12 @@ struct rrm_data { /* next_neighbor_rep_token - Next request's dialog token */ u8 next_neighbor_rep_token; + + /* token - Dialog token of the current radio measurement */ + u8 token; + + /* destination address of the current radio measurement request */ + u8 dst_addr[ETH_ALEN]; }; enum wpa_supplicant_test_failure { @@ -443,7 +451,28 @@ struct icon_entry { struct wpa_bss_tmp_disallowed { struct dl_list list; u8 bssid[ETH_ALEN]; - struct os_reltime disallowed_until; +}; + +struct beacon_rep_data { + u8 token; + struct wpa_driver_scan_params scan_params; + u8 ssid[SSID_MAX_LEN]; + size_t ssid_len; + u8 bssid[ETH_ALEN]; + enum beacon_report_detail report_detail; + struct bitfield *eids; +}; + + +struct external_pmksa_cache { + struct dl_list list; + void *pmksa_cache; +}; + +struct fils_hlp_req { + struct dl_list list; + u8 dst[ETH_ALEN]; + struct wpabuf *pkt; }; /** @@ -503,6 +532,8 @@ struct wpa_supplicant { struct wpa_bss *current_bss; int ap_ies_from_associnfo; unsigned int assoc_freq; + u8 *last_con_fail_realm; + size_t last_con_fail_realm_len; /* Selected configuration (based on Beacon/ProbeResp WPA IE) */ int pairwise_cipher; @@ -639,6 +670,7 @@ struct wpa_supplicant { struct os_reltime scan_min_time; int scan_runs; /* number of scan runs since WPS was started */ int *next_scan_freqs; + int *select_network_scan_freqs; int *manual_scan_freqs; int *manual_sched_scan_freqs; unsigned int manual_scan_passive:1; @@ -652,6 +684,12 @@ struct wpa_supplicant { int normal_scans; /* normal scans run before sched_scan */ int scan_for_connection; /* whether the scan request was triggered for * finding a connection */ + /* + * A unique cookie representing the vendor scan request. This cookie is + * returned from the driver interface. 0 indicates that there is no + * pending vendor scan request. + */ + u64 curr_scan_cookie; #define MAX_SCAN_ID 16 int scan_id[MAX_SCAN_ID]; unsigned int scan_id_count; @@ -705,6 +743,8 @@ struct wpa_supplicant { unsigned int mac_addr_changed:1; unsigned int added_vif:1; unsigned int wnmsleep_used:1; + unsigned int owe_transition_select:1; + unsigned int owe_transition_search:1; struct os_reltime last_mac_addr_change; int last_mac_addr_style; @@ -715,13 +755,15 @@ struct wpa_supplicant { int sta_uapsd; int set_ap_uapsd; int ap_uapsd; + int auth_alg; + u16 last_owe_group; #ifdef CONFIG_SME struct { u8 ssid[SSID_MAX_LEN]; size_t ssid_len; int freq; - u8 assoc_req_ie[200]; + u8 assoc_req_ie[1500]; size_t assoc_req_ie_len; int mfp; int ft_used; @@ -752,6 +794,8 @@ struct wpa_supplicant { struct wpabuf *sae_token; int sae_group_index; unsigned int sae_pmksa_caching:1; + u16 seq_num; + struct external_auth ext_auth; #endif /* CONFIG_SAE */ } sme; #endif /* CONFIG_SME */ @@ -770,6 +814,10 @@ struct wpa_supplicant { unsigned int mesh_if_created:1; unsigned int mesh_ht_enabled:1; unsigned int mesh_vht_enabled:1; +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL + /* struct external_pmksa_cache::list */ + struct dl_list mesh_external_pmksa_cache; +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ #endif /* CONFIG_MESH */ unsigned int off_channel_freq; @@ -789,6 +837,7 @@ struct wpa_supplicant { result); unsigned int roc_waiting_drv_freq; int action_tx_wait_time; + int action_tx_wait_time_used; int p2p_mgmt; @@ -856,6 +905,7 @@ struct wpa_supplicant { unsigned int p2p_auto_join:1; unsigned int p2p_auto_pd:1; + unsigned int p2p_go_do_acs:1; unsigned int p2p_persistent_group:1; unsigned int p2p_fallback_to_go_neg:1; unsigned int p2p_pd_before_go_neg:1; @@ -871,6 +921,7 @@ struct wpa_supplicant { unsigned int p2p_disable_ip_addr_req:1; unsigned int p2ps_method_config_any:1; unsigned int p2p_cli_probe:1; + enum hostapd_hw_mode p2p_go_acs_band; int p2p_persistent_go_freq; int p2p_persistent_id; int p2p_go_intent; @@ -923,6 +974,7 @@ struct wpa_supplicant { int best_overall_freq; struct gas_query *gas; + struct gas_server *gas_server; #ifdef CONFIG_INTERWORKING unsigned int fetch_anqp_in_progress:1; @@ -981,6 +1033,7 @@ struct wpa_supplicant { unsigned int wmm_ac_supported:1; unsigned int ext_work_in_progress:1; unsigned int own_disconnect_req:1; + unsigned int ignore_post_flush_scan_res:1; #define MAC_ADDR_RAND_SCAN BIT(0) #define MAC_ADDR_RAND_SCHED_SCAN BIT(1) @@ -1006,6 +1059,14 @@ struct wpa_supplicant { struct neighbor_report *wnm_neighbor_report_elements; struct os_reltime wnm_cand_valid_until; u8 wnm_cand_from_bss[ETH_ALEN]; + struct wpabuf *coloc_intf_elems; + u8 coloc_intf_dialog_token; + u8 coloc_intf_auto_report; + u8 coloc_intf_timeout; +#ifdef CONFIG_MBO + unsigned int wnm_mbo_trans_reason_present:1; + u8 wnm_mbo_transition_reason; +#endif /* CONFIG_MBO */ #endif /* CONFIG_WNM */ #ifdef CONFIG_TESTING_GET_GTK @@ -1024,10 +1085,19 @@ struct wpa_supplicant { struct l2_packet_data *l2_test; unsigned int extra_roc_dur; enum wpa_supplicant_test_failure test_failure; + char *get_pref_freq_list_override; unsigned int reject_btm_req_reason; unsigned int p2p_go_csa_on_inv:1; unsigned int ignore_auth_resp:1; unsigned int ignore_assoc_disallow:1; + unsigned int testing_resend_assoc:1; + struct wpabuf *sae_commit_override; + enum wpa_alg last_tk_alg; + u8 last_tk_addr[ETH_ALEN]; + int last_tk_key_idx; + u8 last_tk[WPA_TK_MAX_LEN]; + size_t last_tk_len; + struct wpabuf *last_assoc_req_wpa_ie; #endif /* CONFIG_TESTING_OPTIONS */ struct wmm_ac_assoc_data *wmm_ac_assoc_info; @@ -1038,6 +1108,7 @@ struct wpa_supplicant { u8 last_tspecs_count; struct rrm_data rrm; + struct beacon_rep_data beacon_rep_data; #ifdef CONFIG_FST struct fst_iface *fst; @@ -1055,6 +1126,14 @@ struct wpa_supplicant { } *non_pref_chan; size_t non_pref_chan_num; u8 mbo_wnm_token; + /** + * enable_oce - Enable OCE if it is enabled by user and device also + * supports OCE. + * User can enable OCE with wpa_config's 'oce' parameter as follows - + * - Set BIT(0) to enable OCE in non-AP STA mode. + * - Set BIT(1) to enable OCE in STA-CFON mode. + */ + u8 enable_oce; #endif /* CONFIG_MBO */ /* @@ -1069,6 +1148,92 @@ struct wpa_supplicant { */ struct wpabuf *lci; struct os_reltime lci_time; + + struct os_reltime beacon_rep_scan; + + /* FILS HLP requests (struct fils_hlp_req) */ + struct dl_list fils_hlp_req; + + struct sched_scan_relative_params { + /** + * relative_rssi_set - Enable relatively preferred BSS reporting + * + * 0 = Disable reporting relatively preferred BSSs + * 1 = Enable reporting relatively preferred BSSs + */ + int relative_rssi_set; + + /** + * relative_rssi - Relative RSSI for reporting better BSSs + * + * Amount of RSSI by which a BSS should be better than the + * current connected BSS so that the new BSS can be reported + * to user space. This applies to sched_scan operations. + */ + int relative_rssi; + + /** + * relative_adjust_band - Band in which RSSI is to be adjusted + */ + enum set_band relative_adjust_band; + + /** + * relative_adjust_rssi - RSSI adjustment + * + * An amount of relative_adjust_rssi should be added to the + * BSSs that belong to the relative_adjust_band while comparing + * with other bands for BSS reporting. + */ + int relative_adjust_rssi; + } srp; + + /* RIC elements for FT protocol */ + struct wpabuf *ric_ies; + + int last_auth_timeout_sec; + +#ifdef CONFIG_DPP + struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */ + struct dl_list dpp_configurator; /* struct dpp_configurator */ + int dpp_init_done; + struct dpp_authentication *dpp_auth; + struct wpa_radio_work *dpp_listen_work; + unsigned int dpp_pending_listen_freq; + unsigned int dpp_listen_freq; + u8 dpp_allowed_roles; + int dpp_qr_mutual; + int dpp_netrole_ap; + int dpp_auth_ok_on_ack; + int dpp_in_response_listen; + int dpp_gas_client; + int dpp_gas_dialog_token; + u8 dpp_intro_bssid[ETH_ALEN]; + void *dpp_intro_network; + struct dpp_pkex *dpp_pkex; + struct dpp_bootstrap_info *dpp_pkex_bi; + char *dpp_pkex_code; + char *dpp_pkex_identifier; + char *dpp_pkex_auth_cmd; + char *dpp_configurator_params; + struct os_reltime dpp_last_init; + struct os_reltime dpp_init_iter_start; + unsigned int dpp_init_max_tries; + unsigned int dpp_init_retry_time; + unsigned int dpp_resp_wait_time; + unsigned int dpp_resp_max_tries; + unsigned int dpp_resp_retry_time; +#ifdef CONFIG_TESTING_OPTIONS + char *dpp_config_obj_override; + char *dpp_discovery_override; + char *dpp_groups_override; + unsigned int dpp_ignore_netaccesskey_mismatch:1; +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_DPP */ + +#ifdef CONFIG_FILS + unsigned int disable_fils:1; +#endif /* CONFIG_FILS */ + unsigned int ieee80211ac:1; }; @@ -1101,6 +1266,7 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s); void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr); void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s, int sec, int usec); +void wpas_auth_timeout_restart(struct wpa_supplicant *wpa_s, int sec_diff); void wpa_supplicant_reinit_autoscan(struct wpa_supplicant *wpa_s); void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, enum wpa_states state); @@ -1158,6 +1324,7 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, 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); +void fils_connection_failure(struct wpa_supplicant *wpa_s); 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, char *reason); @@ -1183,22 +1350,28 @@ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, struct wpabuf *neighbor_rep), void *cb_ctx); void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s, - const u8 *src, + const u8 *src, const u8 *dst, const u8 *frame, size_t len); void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *frame, size_t len, int rssi); +void wpas_rrm_refuse_request(struct wpa_supplicant *wpa_s); +int wpas_beacon_rep_scan_process(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res, + struct scan_info *info); +void wpas_clear_beacon_rep_data(struct wpa_supplicant *wpa_s); +void wpas_flush_fils_hlp_req(struct wpa_supplicant *wpa_s); /* MBO functions */ -int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len); +int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len, + int add_oce_capa); +const u8 * mbo_attr_from_mbo_ie(const u8 *mbo_ie, enum mbo_attr_id attr); const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr); int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s, const char *non_pref_chan); void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie); -int wpas_mbo_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos, - size_t len); void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *ie, size_t len); size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos, @@ -1206,7 +1379,20 @@ size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos, enum mbo_transition_reject_reason reason); void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa); struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s, - struct wpa_bss *bss); + struct wpa_bss *bss, u32 mbo_subtypes); +void mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, + struct wpa_bss *bss, const u8 *sa, + const u8 *data, size_t slen); + +/* op_classes.c */ +enum chan_allowed { + NOT_ALLOWED, NO_IR, ALLOWED +}; + +enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 channel, + u8 bw); +size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos, + size_t len); /** * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response @@ -1238,6 +1424,7 @@ void wnm_bss_keep_alive_deinit(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); +int wpas_temp_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid); /* eap_register.c */ int eap_register_methods(void); @@ -1296,6 +1483,14 @@ int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid); struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, int i, struct wpa_bss *bss, struct wpa_ssid *group, - int only_first_ssid); + int only_first_ssid, int debug_print); + +int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s, + enum wpa_driver_if_type if_type, + unsigned int *num, + unsigned int *freq_list); + +int wpa_is_fils_supported(struct wpa_supplicant *wpa_s); +int wpa_is_fils_sk_pfs_supported(struct wpa_supplicant *wpa_s); #endif /* WPA_SUPPLICANT_I_H */ diff --git a/contrib/wpa/wpa_supplicant/wpa_supplicant_template.conf b/contrib/wpa/wpa_supplicant/wpa_supplicant_template.conf index f3f2a6417d2e..f55227f82685 100644 --- a/contrib/wpa/wpa_supplicant/wpa_supplicant_template.conf +++ b/contrib/wpa/wpa_supplicant/wpa_supplicant_template.conf @@ -4,3 +4,4 @@ eapol_version=1 ap_scan=1 fast_reauth=1 pmf=1 +p2p_add_cli_chan=1 diff --git a/contrib/wpa/wpa_supplicant/wpas_glue.c b/contrib/wpa/wpa_supplicant/wpas_glue.c index f84c8b90ac2f..4634ed7fc368 100644 --- a/contrib/wpa/wpa_supplicant/wpas_glue.c +++ b/contrib/wpa/wpa_supplicant/wpas_glue.c @@ -10,6 +10,7 @@ #include "common.h" #include "eapol_supp/eapol_supp_sm.h" +#include "eap_peer/eap.h" #include "rsn_supp/wpa.h" #include "eloop.h" #include "config.h" @@ -145,6 +146,8 @@ static int wpa_supplicant_eapol_send(void *ctx, int type, const u8 *buf, * extra copy here */ if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || + wpa_s->key_mgmt == WPA_KEY_MGMT_OWE || + wpa_s->key_mgmt == WPA_KEY_MGMT_DPP || wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) { /* Current SSID is not using IEEE 802.1X/EAP, so drop possible * EAPOL frames (mainly, EAPOL-Start) from EAPOL state @@ -499,6 +502,16 @@ static int wpa_supplicant_set_key(void *_wpa_s, enum wpa_alg alg, wpa_s->last_gtk_len = key_len; } #endif /* CONFIG_TESTING_GET_GTK */ +#ifdef CONFIG_TESTING_OPTIONS + if (addr && !is_broadcast_ether_addr(addr)) { + wpa_s->last_tk_alg = alg; + os_memcpy(wpa_s->last_tk_addr, addr, ETH_ALEN); + wpa_s->last_tk_key_idx = key_idx; + if (key) + os_memcpy(wpa_s->last_tk, key, key_len); + wpa_s->last_tk_len = key_len; + } +#endif /* CONFIG_TESTING_OPTIONS */ return wpa_drv_set_key(wpa_s, alg, addr, key_idx, set_tx, seq, seq_len, key, key_len); } @@ -513,17 +526,74 @@ static int wpa_supplicant_mlme_setprotection(void *wpa_s, const u8 *addr, } -static int wpa_supplicant_add_pmkid(void *wpa_s, - const u8 *bssid, const u8 *pmkid) +static struct wpa_ssid * wpas_get_network_ctx(struct wpa_supplicant *wpa_s, + void *network_ctx) +{ + struct wpa_ssid *ssid; + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (network_ctx == ssid) + return ssid; + } + + return NULL; +} + + +static int wpa_supplicant_add_pmkid(void *_wpa_s, void *network_ctx, + const u8 *bssid, const u8 *pmkid, + const u8 *fils_cache_id, + const u8 *pmk, size_t pmk_len) { - return wpa_drv_add_pmkid(wpa_s, bssid, pmkid); + struct wpa_supplicant *wpa_s = _wpa_s; + struct wpa_ssid *ssid; + struct wpa_pmkid_params params; + + os_memset(¶ms, 0, sizeof(params)); + ssid = wpas_get_network_ctx(wpa_s, network_ctx); + if (ssid) + wpa_msg(wpa_s, MSG_INFO, PMKSA_CACHE_ADDED MACSTR " %d", + MAC2STR(bssid), ssid->id); + if (ssid && fils_cache_id) { + params.ssid = ssid->ssid; + params.ssid_len = ssid->ssid_len; + params.fils_cache_id = fils_cache_id; + } else { + params.bssid = bssid; + } + + params.pmkid = pmkid; + params.pmk = pmk; + params.pmk_len = pmk_len; + + return wpa_drv_add_pmkid(wpa_s, ¶ms); } -static int wpa_supplicant_remove_pmkid(void *wpa_s, - const u8 *bssid, const u8 *pmkid) +static int wpa_supplicant_remove_pmkid(void *_wpa_s, void *network_ctx, + const u8 *bssid, const u8 *pmkid, + const u8 *fils_cache_id) { - return wpa_drv_remove_pmkid(wpa_s, bssid, pmkid); + struct wpa_supplicant *wpa_s = _wpa_s; + struct wpa_ssid *ssid; + struct wpa_pmkid_params params; + + os_memset(¶ms, 0, sizeof(params)); + ssid = wpas_get_network_ctx(wpa_s, network_ctx); + if (ssid) + wpa_msg(wpa_s, MSG_INFO, PMKSA_CACHE_REMOVED MACSTR " %d", + MAC2STR(bssid), ssid->id); + if (ssid && fils_cache_id) { + params.ssid = ssid->ssid; + params.ssid_len = ssid->ssid_len; + params.fils_cache_id = fils_cache_id; + } else { + params.bssid = bssid; + } + + params.pmkid = pmkid; + + return wpa_drv_remove_pmkid(wpa_s, ¶ms); } @@ -865,12 +935,13 @@ static void wpa_supplicant_eap_param_needed(void *ctx, #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->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, -1, wpa_s->imsi, &len); if (wpa_s->mnc_len > 0) { wpa_s->imsi[len] = '\0'; @@ -880,6 +951,52 @@ static void wpa_supplicant_eap_proxy_cb(void *ctx) wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available"); } } + + +static void wpa_sm_sim_state_error_handler(struct wpa_supplicant *wpa_s) +{ + int i; + struct wpa_ssid *ssid; + const struct eap_method_type *eap_methods; + + if (!wpa_s->conf) + return; + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + eap_methods = ssid->eap.eap_methods; + if (!eap_methods) + continue; + + for (i = 0; eap_methods[i].method != EAP_TYPE_NONE; i++) { + if (eap_methods[i].vendor == EAP_VENDOR_IETF && + (eap_methods[i].method == EAP_TYPE_SIM || + eap_methods[i].method == EAP_TYPE_AKA || + eap_methods[i].method == EAP_TYPE_AKA_PRIME)) { + wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid); + break; + } + } + } +} + + +static void +wpa_supplicant_eap_proxy_notify_sim_status(void *ctx, + enum eap_proxy_sim_state sim_state) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpa_printf(MSG_DEBUG, "eap_proxy: SIM card status %u", sim_state); + switch (sim_state) { + case SIM_STATE_ERROR: + wpa_sm_sim_state_error_handler(wpa_s); + break; + default: + wpa_printf(MSG_DEBUG, "eap_proxy: SIM card status unknown"); + break; + } +} + #endif /* CONFIG_EAP_PROXY */ @@ -921,6 +1038,14 @@ static void wpa_supplicant_status_cb(void *ctx, const char *status, } +static void wpa_supplicant_eap_error_cb(void *ctx, int error_code) +{ + struct wpa_supplicant *wpa_s = ctx; + + wpas_notify_eap_error(wpa_s, error_code); +} + + static void wpa_supplicant_set_anon_id(void *ctx, const u8 *id, size_t len) { struct wpa_supplicant *wpa_s = ctx; @@ -990,12 +1115,15 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s) ctx->eap_param_needed = wpa_supplicant_eap_param_needed; #ifdef CONFIG_EAP_PROXY ctx->eap_proxy_cb = wpa_supplicant_eap_proxy_cb; + ctx->eap_proxy_notify_sim_status = + wpa_supplicant_eap_proxy_notify_sim_status; #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->eap_error_cb = wpa_supplicant_eap_error_cb; ctx->set_anon_id = wpa_supplicant_set_anon_id; ctx->cb_ctx = wpa_s; wpa_s->eapol = eapol_sm_init(ctx); @@ -1012,6 +1140,7 @@ 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, size_t kek_len, const u8 *kck, size_t kck_len, @@ -1035,6 +1164,25 @@ static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk, else return 0; } + + +static void wpa_supplicant_fils_hlp_rx(void *ctx, const u8 *dst, const u8 *src, + const u8 *pkt, size_t pkt_len) +{ + struct wpa_supplicant *wpa_s = ctx; + char *hex; + size_t hexlen; + + hexlen = pkt_len * 2 + 1; + hex = os_malloc(hexlen); + if (!hex) + return; + wpa_snprintf_hex(hex, hexlen, pkt, pkt_len); + wpa_msg(wpa_s, MSG_INFO, FILS_HLP_RX "dst=" MACSTR " src=" MACSTR + " frame=%s", MAC2STR(dst), MAC2STR(src), hex); + os_free(hex); +} + #endif /* CONFIG_NO_WPA */ @@ -1084,6 +1232,7 @@ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s) #endif /* CONFIG_TDLS */ ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload; ctx->key_mgmt_set_pmk = wpa_supplicant_key_mgmt_set_pmk; + ctx->fils_hlp_rx = wpa_supplicant_fils_hlp_rx; wpa_s->wpa = wpa_sm_init(ctx); if (wpa_s->wpa == NULL) { @@ -1105,7 +1254,6 @@ void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s, if (ssid) { os_memset(&conf, 0, sizeof(conf)); conf.network_ctx = ssid; - conf.peerkey_enabled = ssid->peerkey; conf.allowed_pairwise_cipher = ssid->pairwise_cipher; #ifdef IEEE8021X_EAPOL conf.proactive_key_caching = ssid->proactive_key_caching < 0 ? @@ -1133,6 +1281,11 @@ void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_P2P */ conf.wpa_rsc_relaxation = wpa_s->conf->wpa_rsc_relaxation; +#ifdef CONFIG_FILS + if (wpa_key_mgmt_fils(wpa_s->key_mgmt)) + conf.fils_cache_id = + wpa_bss_get_fils_cache_id(wpa_s->current_bss); +#endif /* CONFIG_FILS */ } wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL); } diff --git a/contrib/wpa/wpa_supplicant/wpas_kay.c b/contrib/wpa/wpa_supplicant/wpas_kay.c index d6ec8c5090e9..d3d06b8ae231 100644 --- a/contrib/wpa/wpa_supplicant/wpas_kay.c +++ b/contrib/wpa/wpa_supplicant/wpas_kay.c @@ -5,7 +5,7 @@ * 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" @@ -38,12 +38,24 @@ static int wpas_macsec_deinit(void *priv) } +static int wpas_macsec_get_capability(void *priv, enum macsec_cap *cap) +{ + return wpa_drv_macsec_get_capability(priv, cap); +} + + static int wpas_enable_protect_frames(void *wpa_s, Boolean enabled) { return wpa_drv_enable_protect_frames(wpa_s, enabled); } +static int wpas_enable_encrypt(void *wpa_s, Boolean enabled) +{ + return wpa_drv_enable_encrypt(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); @@ -62,30 +74,21 @@ static int wpas_enable_controlled_port(void *wpa_s, Boolean 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) +static int wpas_get_receive_lowest_pn(void *wpa_s, struct receive_sa *sa) { - return wpa_drv_get_transmit_next_pn(wpa_s, channel, an, next_pn); + return wpa_drv_get_receive_lowest_pn(wpa_s, sa); } -static int wpas_set_transmit_next_pn(void *wpa_s, u32 channel, - u8 an, u32 next_pn) +static int wpas_get_transmit_next_pn(void *wpa_s, struct transmit_sa *sa) { - return wpa_drv_set_transmit_next_pn(wpa_s, channel, an, next_pn); + return wpa_drv_get_transmit_next_pn(wpa_s, sa); } -static int wpas_get_available_receive_sc(void *wpa_s, u32 *channel) +static int wpas_set_transmit_next_pn(void *wpa_s, struct transmit_sa *sa) { - return wpa_drv_get_available_receive_sc(wpa_s, channel); + return wpa_drv_set_transmit_next_pn(wpa_s, sa); } @@ -103,83 +106,79 @@ static unsigned int conf_offset_val(enum confidentiality_offset co) } -static int wpas_create_receive_sc(void *wpa_s, u32 channel, - struct ieee802_1x_mka_sci *sci, +static int wpas_create_receive_sc(void *wpa_s, struct receive_sc *sc, enum validate_frames vf, enum confidentiality_offset co) { - return wpa_drv_create_receive_sc(wpa_s, channel, sci->addr, - be_to_host16(sci->port), - conf_offset_val(co), vf); + return wpa_drv_create_receive_sc(wpa_s, sc, conf_offset_val(co), vf); } -static int wpas_delete_receive_sc(void *wpa_s, u32 channel) +static int wpas_delete_receive_sc(void *wpa_s, struct receive_sc *sc) { - return wpa_drv_delete_receive_sc(wpa_s, channel); + return wpa_drv_delete_receive_sc(wpa_s, sc); } -static int wpas_create_receive_sa(void *wpa_s, u32 channel, u8 an, - u32 lowest_pn, const u8 *sak) +static int wpas_create_receive_sa(void *wpa_s, struct receive_sa *sa) { - return wpa_drv_create_receive_sa(wpa_s, channel, an, lowest_pn, sak); + return wpa_drv_create_receive_sa(wpa_s, sa); } -static int wpas_enable_receive_sa(void *wpa_s, u32 channel, u8 an) +static int wpas_delete_receive_sa(void *wpa_s, struct receive_sa *sa) { - return wpa_drv_enable_receive_sa(wpa_s, channel, an); + return wpa_drv_delete_receive_sa(wpa_s, sa); } -static int wpas_disable_receive_sa(void *wpa_s, u32 channel, u8 an) +static int wpas_enable_receive_sa(void *wpa_s, struct receive_sa *sa) { - return wpa_drv_disable_receive_sa(wpa_s, channel, an); + return wpa_drv_enable_receive_sa(wpa_s, sa); } -static int wpas_get_available_transmit_sc(void *wpa_s, u32 *channel) +static int wpas_disable_receive_sa(void *wpa_s, struct receive_sa *sa) { - return wpa_drv_get_available_transmit_sc(wpa_s, channel); + return wpa_drv_disable_receive_sa(wpa_s, sa); } static int -wpas_create_transmit_sc(void *wpa_s, u32 channel, - const struct ieee802_1x_mka_sci *sci, +wpas_create_transmit_sc(void *wpa_s, struct transmit_sc *sc, enum confidentiality_offset co) { - return wpa_drv_create_transmit_sc(wpa_s, channel, sci->addr, - be_to_host16(sci->port), - conf_offset_val(co)); + return wpa_drv_create_transmit_sc(wpa_s, sc, conf_offset_val(co)); +} + + +static int wpas_delete_transmit_sc(void *wpa_s, struct transmit_sc *sc) +{ + return wpa_drv_delete_transmit_sc(wpa_s, sc); } -static int wpas_delete_transmit_sc(void *wpa_s, u32 channel) +static int wpas_create_transmit_sa(void *wpa_s, struct transmit_sa *sa) { - return wpa_drv_delete_transmit_sc(wpa_s, channel); + return wpa_drv_create_transmit_sa(wpa_s, sa); } -static int wpas_create_transmit_sa(void *wpa_s, u32 channel, u8 an, - u32 next_pn, Boolean confidentiality, - const u8 *sak) +static int wpas_delete_transmit_sa(void *wpa_s, struct transmit_sa *sa) { - return wpa_drv_create_transmit_sa(wpa_s, channel, an, next_pn, - confidentiality, sak); + return wpa_drv_delete_transmit_sa(wpa_s, sa); } -static int wpas_enable_transmit_sa(void *wpa_s, u32 channel, u8 an) +static int wpas_enable_transmit_sa(void *wpa_s, struct transmit_sa *sa) { - return wpa_drv_enable_transmit_sa(wpa_s, channel, an); + return wpa_drv_enable_transmit_sa(wpa_s, sa); } -static int wpas_disable_transmit_sa(void *wpa_s, u32 channel, u8 an) +static int wpas_disable_transmit_sa(void *wpa_s, struct transmit_sa *sa) { - return wpa_drv_disable_transmit_sa(wpa_s, channel, an); + return wpa_drv_disable_transmit_sa(wpa_s, sa); } @@ -194,7 +193,14 @@ int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) if (!ssid || ssid->macsec_policy == 0) return 0; - policy = ssid->macsec_policy == 1 ? SHOULD_SECURE : DO_NOT_SECURE; + if (ssid->macsec_policy == 1) { + if (ssid->macsec_integ_only == 1) + policy = SHOULD_SECURE; + else + policy = SHOULD_ENCRYPT; + } else { + policy = DO_NOT_SECURE; + } kay_ctx = os_zalloc(sizeof(*kay_ctx)); if (!kay_ctx) @@ -204,32 +210,34 @@ int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid) kay_ctx->macsec_init = wpas_macsec_init; kay_ctx->macsec_deinit = wpas_macsec_deinit; + kay_ctx->macsec_get_capability = wpas_macsec_get_capability; kay_ctx->enable_protect_frames = wpas_enable_protect_frames; + kay_ctx->enable_encrypt = wpas_enable_encrypt; 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->delete_receive_sa = wpas_delete_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->delete_transmit_sa = wpas_delete_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, + res = ieee802_1x_kay_init(kay_ctx, policy, ssid->macsec_port, + ssid->mka_priority, wpa_s->ifname, wpa_s->own_addr); - if (res == NULL) { - os_free(kay_ctx); + /* ieee802_1x_kay_init() frees kay_ctx on failure */ + if (res == NULL) return -1; - } wpa_s->kay = res; @@ -260,7 +268,7 @@ static int ieee802_1x_auth_get_session_id(struct wpa_supplicant *wpa_s, return -1; } - need_len = 1 + 2 * SSL3_RANDOM_SIZE; + need_len = 1 + 2 * 32 /* random size */; if (need_len > id_len) { wpa_printf(MSG_DEBUG, "EAP Session-Id not long enough"); return -1; @@ -377,3 +385,49 @@ fail: return res; } + + +void * ieee802_1x_create_preshared_mka(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct mka_key *cak; + struct mka_key_name *ckn; + void *res = NULL; + + if ((ssid->mka_psk_set & MKA_PSK_SET) != MKA_PSK_SET) + goto end; + + ckn = os_zalloc(sizeof(*ckn)); + if (!ckn) + goto end; + + cak = os_zalloc(sizeof(*cak)); + if (!cak) + goto free_ckn; + + if (ieee802_1x_alloc_kay_sm(wpa_s, ssid) < 0 || !wpa_s->kay) + goto free_cak; + + if (wpa_s->kay->policy == DO_NOT_SECURE) + goto dealloc; + + cak->len = MACSEC_CAK_LEN; + os_memcpy(cak->key, ssid->mka_cak, cak->len); + + ckn->len = MACSEC_CKN_LEN; + os_memcpy(ckn->name, ssid->mka_ckn, ckn->len); + + res = ieee802_1x_kay_create_mka(wpa_s->kay, ckn, cak, 0, PSK, FALSE); + if (res) + goto free_cak; + +dealloc: + /* Failed to create MKA */ + ieee802_1x_dealloc_kay_sm(wpa_s); +free_cak: + os_free(cak); +free_ckn: + os_free(ckn); +end: + return res; +} diff --git a/contrib/wpa/wpa_supplicant/wpas_kay.h b/contrib/wpa/wpa_supplicant/wpas_kay.h index b7236d0776c4..81f8e0ce329e 100644 --- a/contrib/wpa/wpa_supplicant/wpas_kay.h +++ b/contrib/wpa/wpa_supplicant/wpas_kay.h @@ -17,6 +17,9 @@ 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); +void * ieee802_1x_create_preshared_mka(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid); + #else /* CONFIG_MACSEC */ static inline int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s, @@ -36,6 +39,13 @@ static inline void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s) { } +static inline void * +ieee802_1x_create_preshared_mka(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + return 0; +} + #endif /* CONFIG_MACSEC */ #endif /* WPAS_KAY_H */ diff --git a/contrib/wpa/wpa_supplicant/wps_supplicant.c b/contrib/wpa/wpa_supplicant/wps_supplicant.c index 74a420c671d0..1a2677b8eea4 100644 --- a/contrib/wpa/wpa_supplicant/wps_supplicant.c +++ b/contrib/wpa/wpa_supplicant/wps_supplicant.c @@ -203,6 +203,9 @@ static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s, if (ssid->ssid == NULL) return; bss = wpa_bss_get(wpa_s, cred->mac_addr, ssid->ssid, ssid->ssid_len); + if (!bss) + bss = wpa_bss_get(wpa_s, wpa_s->bssid, + ssid->ssid, ssid->ssid_len); if (bss == NULL) { wpa_printf(MSG_DEBUG, "WPS: The AP was not found from BSS " "table - use credential as-is"); @@ -490,6 +493,16 @@ static int wpa_supplicant_wps_cred(void *ctx, ssid->pairwise_cipher |= WPA_CIPHER_GCMP; ssid->group_cipher |= WPA_CIPHER_GCMP; } + if (wpa_s->drv_capa_known && + (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP_256)) { + ssid->pairwise_cipher |= WPA_CIPHER_GCMP_256; + ssid->group_cipher |= WPA_CIPHER_GCMP_256; + } + if (wpa_s->drv_capa_known && + (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_CCMP_256)) { + ssid->pairwise_cipher |= WPA_CIPHER_CCMP_256; + ssid->group_cipher |= WPA_CIPHER_CCMP_256; + } break; } @@ -1027,10 +1040,9 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s, continue; os_free(ssid->ssid); - ssid->ssid = os_malloc(bss->ssid_len); + ssid->ssid = os_memdup(bss->ssid, bss->ssid_len); if (ssid->ssid == NULL) break; - os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len); ssid->ssid_len = bss->ssid_len; wpa_hexdump_ascii(MSG_DEBUG, "WPS: Picked SSID from " "scan results", @@ -1169,6 +1181,7 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, return -1; if (wpa_s->wps_fragment_size) ssid->eap.fragment_size = wpa_s->wps_fragment_size; + wpa_supplicant_wps_event(wpa_s, WPS_EV_PBC_ACTIVE, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, wpa_s, NULL); wpas_wps_reassoc(wpa_s, ssid, bssid, 0); @@ -1481,6 +1494,9 @@ static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s, wpa_s->global->ifaces->wps->uuid, WPS_UUID_LEN); src = "from the first interface"; + } else if (wpa_s->conf->auto_uuid == 1) { + uuid_random(wps->uuid); + src = "based on random data"; } else { uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid); src = "based on MAC address"; |