diff options
author | Cy Schubert <cy@FreeBSD.org> | 2018-12-06 05:04:28 +0000 |
---|---|---|
committer | Cy Schubert <cy@FreeBSD.org> | 2018-12-06 05:04:28 +0000 |
commit | 8a36c5c2ca4d1f8a900ca3d9ffde40b96463def7 (patch) | |
tree | b9a3166587c75d5325dc46c7c83ca435f2e54917 | |
parent | 765ef8a7642d07aa9616f2b1a9cdebb8e3552f6a (diff) |
Notes
400 files changed, 69903 insertions, 10415 deletions
diff --git a/CONTRIBUTIONS b/CONTRIBUTIONS index 76600bc872808..053e8ecda90f2 100644 --- a/CONTRIBUTIONS +++ b/CONTRIBUTIONS @@ -140,7 +140,7 @@ The license terms used for hostap.git files Modified BSD license (no advertisement clause): -Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi> and contributors +Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi> and contributors All Rights Reserved. Redistribution and use in source and binary forms, with or without @@ -1,7 +1,7 @@ wpa_supplicant and hostapd -------------------------- -Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi> and contributors +Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi> and contributors All Rights Reserved. @@ -1,7 +1,7 @@ wpa_supplicant and hostapd -------------------------- -Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi> and contributors +Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi> and contributors All Rights Reserved. These programs are licensed under the BSD license (the one with diff --git a/hostapd/Android.mk b/hostapd/Android.mk index ea3a39a9719fa..322f6a63255c9 100644 --- a/hostapd/Android.mk +++ b/hostapd/Android.mk @@ -38,6 +38,9 @@ endif L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\" L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/system/hostapd\" +# Use Android specific directory for hostapd_cli command completion history +L_CFLAGS += -DCONFIG_HOSTAPD_CLI_HISTORY_DIR=\"/data/misc/wifi\" + # To force sizeof(enum) = 4 ifeq ($(TARGET_ARCH),arm) L_CFLAGS += -mabi=aapcs-linux @@ -212,11 +215,6 @@ L_CFLAGS += -DCONFIG_RSN_PREAUTH CONFIG_L2_PACKET=y endif -ifdef CONFIG_PEERKEY -L_CFLAGS += -DCONFIG_PEERKEY -OBJS += src/ap/peerkey_auth.c -endif - ifdef CONFIG_HS20 NEED_AES_OMAC1=y CONFIG_PROXYARP=y @@ -244,11 +242,20 @@ NEED_AES_OMAC1=y endif ifdef CONFIG_IEEE80211R -L_CFLAGS += -DCONFIG_IEEE80211R +L_CFLAGS += -DCONFIG_IEEE80211R -DCONFIG_IEEE80211R_AP OBJS += src/ap/wpa_auth_ft.c NEED_SHA256=y NEED_AES_OMAC1=y NEED_AES_UNWRAP=y +NEED_AES_SIV=y +NEED_ETH_P_OUI=y +NEED_SHA256=y +NEED_HMAC_SHA256_KDF=y +endif + +ifdef NEED_ETH_P_OUI +L_CFLAGS += -DCONFIG_ETH_P_OUI +OBJS += src/ap/eth_p_oui.c endif ifdef CONFIG_SAE @@ -258,8 +265,30 @@ NEED_ECC=y NEED_DH_GROUPS=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 +OBJS += src/ap/fils_hlp.c +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_WNM -L_CFLAGS += -DCONFIG_WNM +L_CFLAGS += -DCONFIG_WNM -DCONFIG_WNM_AP OBJS += src/ap/wnm_ap.c endif @@ -271,6 +300,10 @@ ifdef CONFIG_IEEE80211AC L_CFLAGS += -DCONFIG_IEEE80211AC endif +ifdef CONFIG_IEEE80211AX +L_CFLAGS += -DCONFIG_IEEE80211AX +endif + ifdef CONFIG_MBO L_CFLAGS += -DCONFIG_MBO OBJS += src/ap/mbo_ap.c @@ -422,6 +455,7 @@ ifdef CONFIG_EAP_PWD L_CFLAGS += -DEAP_SERVER_PWD OBJS += src/eap_server/eap_server_pwd.c src/eap_common/eap_pwd_common.c NEED_SHA256=y +NEED_ECC=y endif ifdef CONFIG_EAP_EKE @@ -499,6 +533,23 @@ endif endif +ifdef CONFIG_DPP +L_CFLAGS += -DCONFIG_DPP +OBJS += src/common/dpp.c +OBJS += src/ap/dpp_hostapd.c +OBJS += src/ap/gas_query_ap.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=y +NEED_BASE64=y +endif + ifdef CONFIG_EAP_IKEV2 L_CFLAGS += -DEAP_SERVER_IKEV2 OBJS += src/eap_server/eap_server_ikev2.c src/eap_server/ikev2.c @@ -581,25 +632,40 @@ NEED_SHA256=y NEED_TLS_PRF_SHA256=y LIBS += -lcrypto LIBS_h += -lcrypto +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 -HOBJS += src/crypto/crypto_gnutls.c +OBJS += src/crypto/crypto_$(CONFIG_CRYPTO).c +HOBJS += 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_h += -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 @@ -715,6 +781,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 @@ -749,8 +821,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 @@ -774,8 +848,10 @@ OBJS += $(SHA1OBJS) endif ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), gnutls) OBJS += src/crypto/md5.c endif +endif ifdef NEED_MD5 ifdef CONFIG_INTERNAL_MD5 @@ -811,8 +887,10 @@ endif ifdef NEED_SHA256 L_CFLAGS += -DCONFIG_SHA256 ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), gnutls) OBJS += src/crypto/sha256.c endif +endif OBJS += src/crypto/sha256-prf.c ifdef CONFIG_INTERNAL_SHA256 OBJS += src/crypto/sha256-internal.c @@ -820,11 +898,36 @@ endif ifdef NEED_TLS_PRF_SHA256 OBJS += src/crypto/sha256-tlsprf.c endif +ifdef NEED_HMAC_SHA256_KDF +OBJS += src/crypto/sha256-kdf.c +endif +ifdef NEED_HMAC_SHA384_KDF +OBJS += src/crypto/sha384-kdf.c +endif +ifdef NEED_HMAC_SHA512_KDF +OBJS += src/crypto/sha512-kdf.c +endif 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), linux) +ifneq ($(CONFIG_TLS), gnutls) +OBJS += src/crypto/sha512.c +endif +endif +endif +OBJS += src/crypto/sha512-prf.c +endif ifdef CONFIG_INTERNAL_SHA384 L_CFLAGS += -DCONFIG_INTERNAL_SHA384 @@ -881,6 +984,11 @@ ifdef NEED_BASE64 OBJS += src/utils/base64.c endif +ifdef NEED_JSON +OBJS += src/utils/json.c +L_CFLAGS += -DCONFIG_JSON +endif + ifdef NEED_AP_MLME OBJS += src/ap/wmm.c OBJS += src/ap/ap_list.c @@ -897,6 +1005,10 @@ ifdef CONFIG_IEEE80211AC OBJS += src/ap/ieee802_11_vht.c endif +ifdef CONFIG_IEEE80211AX +OBJS += src/ap/ieee802_11_he.c +endif + ifdef CONFIG_P2P_MANAGER L_CFLAGS += -DCONFIG_P2P_MANAGER OBJS += src/ap/p2p_hostapd.c @@ -910,6 +1022,10 @@ endif ifdef CONFIG_INTERWORKING L_CFLAGS += -DCONFIG_INTERWORKING +NEED_GAS=y +endif + +ifdef NEED_GAS OBJS += src/common/gas.c OBJS += src/ap/gas_serv.c endif @@ -935,6 +1051,10 @@ ifdef CONFIG_NO_STDOUT_DEBUG L_CFLAGS += -DCONFIG_NO_STDOUT_DEBUG endif +ifdef CONFIG_DEBUG_SYSLOG +L_CFLAGS += -DCONFIG_DEBUG_SYSLOG +endif + ifdef CONFIG_DEBUG_LINUX_TRACING L_CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING endif @@ -968,6 +1088,7 @@ endif include $(CLEAR_VARS) LOCAL_MODULE := hostapd_cli LOCAL_MODULE_TAGS := debug +LOCAL_PROPRIETARY_MODULE := true LOCAL_SHARED_LIBRARIES := libc libcutils liblog LOCAL_CFLAGS := $(L_CFLAGS) LOCAL_SRC_FILES := $(OBJS_c) @@ -978,6 +1099,7 @@ include $(BUILD_EXECUTABLE) include $(CLEAR_VARS) LOCAL_MODULE := hostapd LOCAL_MODULE_TAGS := optional +LOCAL_PROPRIETARY_MODULE := true ifdef CONFIG_DRIVER_CUSTOM LOCAL_STATIC_LIBRARIES := libCustomWifi endif diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog index d2b669b586546..f1366b4a9542d 100644 --- a/hostapd/ChangeLog +++ b/hostapd/ChangeLog @@ -1,5 +1,60 @@ ChangeLog for hostapd +2018-12-02 - v2.7 + * fixed WPA packet number reuse with replayed messages and key + reinstallation + [http://w1.fi/security/2017-1/] (CVE-2017-13082) + * 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) + * FT: + - added local generation of PMK-R0/PMK-R1 for FT-PSK + (ft_psk_generate_local=1) + - replaced inter-AP protocol with a cleaner design that is more + easily extensible; this breaks backward compatibility and requires + all APs in the ESS to be updated at the same time to maintain FT + functionality + - added support for wildcard R0KH/R1KH + - replaced r0_key_lifetime (minutes) parameter with + ft_r0_key_lifetime (seconds) + - fixed wpa_psk_file use for FT-PSK + - fixed FT-SAE PMKID matching + - added expiration to PMK-R0 and PMK-R1 cache + - added IEEE VLAN support (including tagged VLANs) + - added support for SHA384 based AKM + * SAE + - fixed some PMKSA caching cases with SAE + - added support for configuring SAE password separately of the + WPA2 PSK/passphrase + - added option to require MFP for SAE associations + (sae_require_pmf=1) + - 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 + * hostapd_cli: added support for command history and completion + * added support for requesting beacon report + * large number of other fixes, cleanup, and extensions + * added option to configure EAPOL-Key retry limits + (wpa_group_update_count and wpa_pairwise_update_count) + * removed all PeerKey functionality + * fixed nl80211 AP mode configuration regression with Linux 4.15 and + newer + * added support for using wolfSSL cryptographic library + * fixed some 20/40 MHz coexistence cases where the BSS could drop to + 20 MHz even when 40 MHz would be allowed + * Hotspot 2.0 + - added support for setting Venue URL ANQP-element (venue_url) + - added support for advertising Hotspot 2.0 operator icons + - 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 using OpenSSL 1.1.1 + * added EAP-pwd server support for salted passwords + 2016-10-02 - v2.6 * fixed EAP-pwd last fragment validation [http://w1.fi/security/2015-7/] (CVE-2015-5314) diff --git a/hostapd/Makefile b/hostapd/Makefile index 46dffe5a3acaf..2ce8b7ded8424 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -258,11 +258,6 @@ CFLAGS += -DCONFIG_RSN_PREAUTH CONFIG_L2_PACKET=y endif -ifdef CONFIG_PEERKEY -CFLAGS += -DCONFIG_PEERKEY -OBJS += ../src/ap/peerkey_auth.o -endif - ifdef CONFIG_HS20 NEED_AES_OMAC1=y CONFIG_PROXYARP=y @@ -290,11 +285,20 @@ NEED_AES_OMAC1=y endif ifdef CONFIG_IEEE80211R -CFLAGS += -DCONFIG_IEEE80211R +CFLAGS += -DCONFIG_IEEE80211R -DCONFIG_IEEE80211R_AP OBJS += ../src/ap/wpa_auth_ft.o NEED_SHA256=y NEED_AES_OMAC1=y NEED_AES_UNWRAP=y +NEED_AES_SIV=y +NEED_ETH_P_OUI=y +NEED_SHA256=y +NEED_HMAC_SHA256_KDF=y +endif + +ifdef NEED_ETH_P_OUI +CFLAGS += -DCONFIG_ETH_P_OUI +OBJS += ../src/ap/eth_p_oui.o endif ifdef CONFIG_SAE @@ -305,8 +309,30 @@ NEED_DH_GROUPS=y NEED_AP_MLME=y endif +ifdef CONFIG_OWE +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 +CFLAGS += -DCONFIG_FILS +OBJS += ../src/ap/fils_hlp.o +NEED_SHA384=y +NEED_AES_SIV=y +ifdef CONFIG_FILS_SK_PFS +CFLAGS += -DCONFIG_FILS_SK_PFS +NEED_ECC=y +endif +endif + ifdef CONFIG_WNM -CFLAGS += -DCONFIG_WNM +CFLAGS += -DCONFIG_WNM -DCONFIG_WNM_AP OBJS += ../src/ap/wnm_ap.o endif @@ -318,6 +344,11 @@ ifdef CONFIG_IEEE80211AC CFLAGS += -DCONFIG_IEEE80211AC endif +ifdef CONFIG_IEEE80211AX +CFLAGS += -DCONFIG_IEEE80211AX +OBJS += ../src/ap/ieee802_11_he.o +endif + ifdef CONFIG_MBO CFLAGS += -DCONFIG_MBO OBJS += ../src/ap/mbo_ap.o @@ -458,6 +489,7 @@ ifdef CONFIG_EAP_PWD CFLAGS += -DEAP_SERVER_PWD OBJS += ../src/eap_server/eap_server_pwd.o ../src/eap_common/eap_pwd_common.o NEED_SHA256=y +NEED_ECC=y endif ifdef CONFIG_EAP_EKE @@ -535,6 +567,23 @@ endif endif +ifdef CONFIG_DPP +CFLAGS += -DCONFIG_DPP +OBJS += ../src/common/dpp.o +OBJS += ../src/ap/dpp_hostapd.o +OBJS += ../src/ap/gas_query_ap.o +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=y +NEED_BASE64=y +endif + ifdef CONFIG_EAP_IKEV2 CFLAGS += -DEAP_SERVER_IKEV2 OBJS += ../src/eap_server/eap_server_ikev2.o ../src/eap_server/ikev2.o @@ -602,7 +651,29 @@ CFLAGS += -DCONFIG_TLSV12 NEED_SHA256=y endif +ifeq ($(CONFIG_TLS), wolfssl) +CONFIG_CRYPTO=wolfssl +ifdef TLS_FUNCS +OBJS += ../src/crypto/tls_wolfssl.o +LIBS += -lwolfssl -lm +endif +OBJS += ../src/crypto/crypto_wolfssl.o +HOBJS += ../src/crypto/crypto_wolfssl.o +ifdef NEED_FIPS186_2_PRF +OBJS += ../src/crypto/fips_prf_wolfssl.o +endif +NEED_SHA256=y +NEED_TLS_PRF_SHA256=y +LIBS += -lwolfssl -lm +LIBS_h += -lwolfssl -lm +ifdef CONFIG_TLS_ADD_DL +LIBS += -ldl +LIBS_h += -ldl +endif +endif + ifeq ($(CONFIG_TLS), openssl) +CONFIG_CRYPTO=openssl ifdef TLS_FUNCS OBJS += ../src/crypto/tls_openssl.o OBJS += ../src/crypto/tls_openssl_ocsp.o @@ -617,29 +688,46 @@ NEED_SHA256=y NEED_TLS_PRF_SHA256=y LIBS += -lcrypto LIBS_h += -lcrypto +LIBS_n += -lcrypto ifdef CONFIG_TLS_ADD_DL LIBS += -ldl LIBS_h += -ldl endif +ifndef CONFIG_TLS_DEFAULT_CIPHERS +CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW" +endif +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.o LIBS += -lgnutls -lgpg-error endif -OBJS += ../src/crypto/crypto_gnutls.o -HOBJS += ../src/crypto/crypto_gnutls.o +OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o +HOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o ifdef NEED_FIPS186_2_PRF OBJS += ../src/crypto/fips_prf_internal.o SHA1OBJS += ../src/crypto/sha1-internal.o endif +ifeq ($(CONFIG_CRYPTO), gnutls) LIBS += -lgcrypt LIBS_h += -lgcrypt -CONFIG_INTERNAL_SHA256=y +LIBS_n += -lgcrypt +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 @@ -720,6 +808,47 @@ CONFIG_INTERNAL_RC4=y endif endif +ifeq ($(CONFIG_TLS), linux) +OBJS += ../src/crypto/crypto_linux.o +ifdef TLS_FUNCS +OBJS += ../src/crypto/crypto_internal-rsa.o +OBJS += ../src/crypto/tls_internal.o +OBJS += ../src/tls/tlsv1_common.o +OBJS += ../src/tls/tlsv1_record.o +OBJS += ../src/tls/tlsv1_cred.o +OBJS += ../src/tls/tlsv1_server.o +OBJS += ../src/tls/tlsv1_server_write.o +OBJS += ../src/tls/tlsv1_server_read.o +OBJS += ../src/tls/asn1.o +OBJS += ../src/tls/rsa.o +OBJS += ../src/tls/x509v3.o +OBJS += ../src/tls/pkcs1.o +OBJS += ../src/tls/pkcs5.o +OBJS += ../src/tls/pkcs8.o +NEED_SHA256=y +NEED_BASE64=y +NEED_TLS_PRF=y +ifdef CONFIG_TLSV12 +NEED_TLS_PRF_SHA256=y +endif +NEED_MODEXP=y +NEED_CIPHER=y +CFLAGS += -DCONFIG_TLS_INTERNAL +CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER +endif +ifdef NEED_MODEXP +OBJS += ../src/crypto/crypto_internal-modexp.o +OBJS += ../src/tls/bignum.o +CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH +CFLAGS += -DLTM_FAST +endif +CONFIG_INTERNAL_DH_GROUP5=y +ifdef NEED_FIPS186_2_PRF +OBJS += ../src/crypto/fips_prf_internal.o +OBJS += ../src/crypto/sha1-internal.o +endif +endif + ifeq ($(CONFIG_TLS), none) ifdef TLS_FUNCS OBJS += ../src/crypto/tls_none.o @@ -750,11 +879,19 @@ AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-enc.o endif ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), wolfssl) AESOBJS += ../src/crypto/aes-wrap.o endif +endif ifdef NEED_AES_EAX AESOBJS += ../src/crypto/aes-eax.o NEED_AES_CTR=y +NEED_AES_OMAC1=y +endif +ifdef NEED_AES_SIV +AESOBJS += ../src/crypto/aes-siv.o +NEED_AES_CTR=y +NEED_AES_OMAC1=y endif ifdef NEED_AES_CTR AESOBJS += ../src/crypto/aes-ctr.o @@ -763,20 +900,32 @@ ifdef NEED_AES_ENCBLOCK AESOBJS += ../src/crypto/aes-encblock.o endif ifdef NEED_AES_OMAC1 +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), wolfssl) AESOBJS += ../src/crypto/aes-omac1.o endif +endif +endif ifdef NEED_AES_UNWRAP ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), wolfssl) NEED_AES_DEC=y AESOBJS += ../src/crypto/aes-unwrap.o endif endif +endif +endif ifdef NEED_AES_CBC NEED_AES_DEC=y ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), wolfssl) AESOBJS += ../src/crypto/aes-cbc.o endif endif +endif +endif ifdef NEED_AES_DEC ifdef CONFIG_INTERNAL_AES AESOBJS += ../src/crypto/aes-internal-dec.o @@ -788,8 +937,14 @@ endif ifdef NEED_SHA1 ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), gnutls) +ifneq ($(CONFIG_TLS), wolfssl) SHA1OBJS += ../src/crypto/sha1.o endif +endif +endif +endif SHA1OBJS += ../src/crypto/sha1-prf.o ifdef CONFIG_INTERNAL_SHA1 SHA1OBJS += ../src/crypto/sha1-internal.o @@ -798,8 +953,10 @@ SHA1OBJS += ../src/crypto/fips_prf_internal.o endif endif ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), wolfssl) SHA1OBJS += ../src/crypto/sha1-pbkdf2.o endif +endif ifdef NEED_T_PRF SHA1OBJS += ../src/crypto/sha1-tprf.o endif @@ -813,8 +970,14 @@ OBJS += $(SHA1OBJS) endif ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), gnutls) +ifneq ($(CONFIG_TLS), wolfssl) OBJS += ../src/crypto/md5.o endif +endif +endif +endif ifdef NEED_MD5 ifdef CONFIG_INTERNAL_MD5 @@ -830,6 +993,7 @@ endif endif ifdef NEED_DES +CFLAGS += -DCONFIG_DES ifdef CONFIG_INTERNAL_DES OBJS += ../src/crypto/des-internal.o endif @@ -850,8 +1014,14 @@ endif ifdef NEED_SHA256 CFLAGS += -DCONFIG_SHA256 ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), gnutls) +ifneq ($(CONFIG_TLS), wolfssl) OBJS += ../src/crypto/sha256.o endif +endif +endif +endif OBJS += ../src/crypto/sha256-prf.o ifdef CONFIG_INTERNAL_SHA256 OBJS += ../src/crypto/sha256-internal.o @@ -862,11 +1032,39 @@ endif ifdef NEED_HMAC_SHA256_KDF OBJS += ../src/crypto/sha256-kdf.o endif +ifdef NEED_HMAC_SHA384_KDF +OBJS += ../src/crypto/sha384-kdf.o +endif +ifdef NEED_HMAC_SHA512_KDF +OBJS += ../src/crypto/sha512-kdf.o +endif endif ifdef NEED_SHA384 CFLAGS += -DCONFIG_SHA384 +ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), gnutls) +ifneq ($(CONFIG_TLS), wolfssl) +OBJS += ../src/crypto/sha384.o +endif +endif +endif +endif OBJS += ../src/crypto/sha384-prf.o endif +ifdef NEED_SHA512 +CFLAGS += -DCONFIG_SHA512 +ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), gnutls) +ifneq ($(CONFIG_TLS), wolfssl) +OBJS += ../src/crypto/sha512.o +endif +endif +endif +endif +OBJS += ../src/crypto/sha512-prf.o +endif ifdef CONFIG_INTERNAL_SHA384 CFLAGS += -DCONFIG_INTERNAL_SHA384 @@ -902,9 +1100,13 @@ HOBJS += ../src/crypto/random.o HOBJS += ../src/utils/eloop.o HOBJS += $(SHA1OBJS) ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), wolfssl) HOBJS += ../src/crypto/md5.o endif endif +endif +endif ifdef CONFIG_RADIUS_SERVER CFLAGS += -DRADIUS_SERVER @@ -923,6 +1125,11 @@ ifdef NEED_BASE64 OBJS += ../src/utils/base64.o endif +ifdef NEED_JSON +OBJS += ../src/utils/json.o +CFLAGS += -DCONFIG_JSON +endif + ifdef NEED_AP_MLME OBJS += ../src/ap/wmm.o OBJS += ../src/ap/ap_list.o @@ -952,6 +1159,10 @@ endif ifdef CONFIG_INTERWORKING CFLAGS += -DCONFIG_INTERWORKING +NEED_GAS=y +endif + +ifdef NEED_GAS OBJS += ../src/common/gas.o OBJS += ../src/ap/gas_serv.o endif @@ -983,6 +1194,10 @@ ifdef CONFIG_NO_STDOUT_DEBUG CFLAGS += -DCONFIG_NO_STDOUT_DEBUG endif +ifdef CONFIG_DEBUG_SYSLOG +CFLAGS += -DCONFIG_DEBUG_SYSLOG +endif + ifdef CONFIG_DEBUG_LINUX_TRACING CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING endif @@ -1082,16 +1297,14 @@ endif ifdef CONFIG_INTERNAL_MD5 NOBJS += ../src/crypto/md5-internal.o endif -NOBJS += ../src/crypto/crypto_openssl.o ../src/utils/os_$(CONFIG_OS).o +NOBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o +NOBJS += ../src/utils/os_$(CONFIG_OS).o NOBJS += ../src/utils/wpa_debug.o NOBJS += ../src/utils/wpabuf.o ifdef CONFIG_WPA_TRACE NOBJS += ../src/utils/trace.o LIBS_n += -lbfd endif -ifdef TLS_FUNCS -LIBS_n += -lcrypto -endif HOBJS += hlr_auc_gw.o ../src/utils/common.o ../src/utils/wpa_debug.o ../src/utils/os_$(CONFIG_OS).o ../src/utils/wpabuf.o ../src/crypto/milenage.o HOBJS += ../src/crypto/aes-encblock.o @@ -1099,6 +1312,9 @@ ifdef CONFIG_INTERNAL_AES HOBJS += ../src/crypto/aes-internal.o HOBJS += ../src/crypto/aes-internal-enc.o endif +ifeq ($(CONFIG_TLS), linux) +HOBJS += ../src/crypto/crypto_linux.o +endif nt_password_hash: $(NOBJS) $(Q)$(CC) $(LDFLAGS) -o nt_password_hash $(NOBJS) $(LIBS_n) diff --git a/hostapd/README b/hostapd/README index 5d5fd365bb62f..ae5317698ee80 100644 --- a/hostapd/README +++ b/hostapd/README @@ -2,7 +2,7 @@ hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP Authenticator and RADIUS authentication server ================================================================ -Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi> and contributors +Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi> and contributors All Rights Reserved. This program is licensed under the BSD license (the one with @@ -70,7 +70,7 @@ Requirements Current hardware/software requirements: - drivers: Host AP driver for Prism2/2.5/3. - (http://hostap.epitest.fi/) + (http://w1.fi/hostap-driver.html) Please note that station firmware version needs to be 1.7.0 or newer to work in WPA mode. @@ -81,8 +81,7 @@ Current hardware/software requirements: Any wired Ethernet driver for wired IEEE 802.1X authentication (experimental code) - FreeBSD -current (with some kernel mods that have not yet been - committed when hostapd v0.3.0 was released) + FreeBSD -current BSD net80211 layer (e.g., Atheros driver) @@ -186,23 +185,13 @@ Authenticator and RADIUS encapsulation between the Authenticator and the Authentication Server. Other than this, the functionality is similar to the case with the co-located Authentication Server. -Authentication Server and Supplicant ------------------------------------- +Authentication Server +--------------------- Any RADIUS server supporting EAP should be usable as an IEEE 802.1X Authentication Server with hostapd Authenticator. FreeRADIUS (http://www.freeradius.org/) has been successfully tested with hostapd -Authenticator and both Xsupplicant (http://www.open1x.org) and Windows -XP Supplicants. EAP/TLS was used with Xsupplicant and -EAP/MD5-Challenge with Windows XP. - -http://www.missl.cs.umd.edu/wireless/eaptls/ has useful information -about using EAP/TLS with FreeRADIUS and Xsupplicant (just replace -Cisco access point with Host AP driver, hostapd daemon, and a Prism2 -card ;-). http://www.freeradius.org/doc/EAP-MD5.html has information -about using EAP/MD5 with FreeRADIUS, including instructions for WinXP -configuration. http://www.denobula.com/EAPTLS.pdf has a HOWTO on -EAP/TLS use with WinXP Supplicant. +Authenticator. Automatic WEP key configuration ------------------------------- @@ -243,16 +232,15 @@ networks that require some kind of security. Task group I (Security) of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked to address the flaws of the base standard and has in practice completed its work in May 2004. The IEEE 802.11i amendment to the IEEE -802.11 standard was approved in June 2004 and this amendment is likely -to be published in July 2004. +802.11 standard was approved in June 2004 and this amendment was +published in July 2004. Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the IEEE 802.11i work (draft 3.0) to define a subset of the security enhancements that can be implemented with existing wlan hardware. This is called Wi-Fi Protected Access<TM> (WPA). This has now become a mandatory component of interoperability testing and certification done -by Wi-Fi Alliance. Wi-Fi provides information about WPA at its web -site (http://www.wi-fi.org/OpenSection/protected_access.asp). +by Wi-Fi Alliance. IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm for protecting wireless networks. WEP uses RC4 with 40-bit keys, diff --git a/hostapd/android.config b/hostapd/android.config index e382c4081ebba..08d21f044aa7d 100644 --- a/hostapd/android.config +++ b/hostapd/android.config @@ -44,9 +44,6 @@ CONFIG_DRIVER_NL80211_QCA=y # WPA2/IEEE 802.11i RSN pre-authentication #CONFIG_RSN_PREAUTH=y -# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) -#CONFIG_PEERKEY=y - # IEEE 802.11w (management frame protection) # This version is an experimental implementation based on IEEE 802.11w/D1.0 # draft and is subject to change since the standard has not yet been finalized. @@ -199,3 +196,17 @@ CONFIG_AP=y # These extentions facilitate efficient use of multiple frequency bands # available to the AP and the devices that may associate with it. #CONFIG_MBO=y + +# Include internal line edit mode in hostapd_cli. +CONFIG_WPA_CLI_EDIT=y + +# Opportunistic Wireless Encryption (OWE) +# Experimental implementation of draft-harkins-owe-07.txt +#CONFIG_OWE=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 diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 5079f69e3bc5a..b26da71a84100 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -1,6 +1,6 @@ /* * hostapd / Configuration file parser - * 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. @@ -14,6 +14,8 @@ #include "utils/common.h" #include "utils/uuid.h" #include "common/ieee802_11_defs.h" +#include "crypto/sha256.h" +#include "crypto/tls.h" #include "drivers/driver.h" #include "eap_server/eap.h" #include "radius/radius_client.h" @@ -111,7 +113,7 @@ static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss, #endif /* CONFIG_NO_VLAN */ -static int hostapd_acl_comp(const void *a, const void *b) +int hostapd_acl_comp(const void *a, const void *b) { const struct mac_acl_entry *aa = a; const struct mac_acl_entry *bb = b; @@ -119,6 +121,44 @@ static int hostapd_acl_comp(const void *a, const void *b) } +int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num, + int vlan_id, const u8 *addr) +{ + struct mac_acl_entry *newacl; + + newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl)); + if (!newacl) { + wpa_printf(MSG_ERROR, "MAC list reallocation failed"); + return -1; + } + + *acl = newacl; + os_memcpy((*acl)[*num].addr, addr, ETH_ALEN); + os_memset(&(*acl)[*num].vlan_id, 0, sizeof((*acl)[*num].vlan_id)); + (*acl)[*num].vlan_id.untagged = vlan_id; + (*acl)[*num].vlan_id.notempty = !!vlan_id; + (*num)++; + + return 0; +} + + +void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num, + const u8 *addr) +{ + int i = 0; + + while (i < *num) { + if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) == 0) { + os_remove_in_array(*acl, *num, sizeof(**acl), i); + (*num)--; + } else { + i++; + } + } +} + + static int hostapd_config_read_maclist(const char *fname, struct mac_acl_entry **acl, int *num) { @@ -126,12 +166,8 @@ static int hostapd_config_read_maclist(const char *fname, char buf[128], *pos; int line = 0; u8 addr[ETH_ALEN]; - struct mac_acl_entry *newacl; int vlan_id; - if (!fname) - return 0; - f = fopen(fname, "r"); if (!f) { wpa_printf(MSG_ERROR, "MAC list file '%s' not found.", fname); @@ -139,7 +175,7 @@ static int hostapd_config_read_maclist(const char *fname, } while (fgets(buf, sizeof(buf), f)) { - int i, rem = 0; + int rem = 0; line++; @@ -169,16 +205,7 @@ static int hostapd_config_read_maclist(const char *fname, } if (rem) { - i = 0; - while (i < *num) { - if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) == - 0) { - os_remove_in_array(*acl, *num, - sizeof(**acl), i); - (*num)--; - } else - i++; - } + hostapd_remove_acl_mac(acl, num, addr); continue; } vlan_id = 0; @@ -190,31 +217,78 @@ static int hostapd_config_read_maclist(const char *fname, if (*pos != '\0') vlan_id = atoi(pos); - newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl)); - if (newacl == NULL) { - wpa_printf(MSG_ERROR, "MAC list reallocation failed"); + if (hostapd_add_acl_maclist(acl, num, vlan_id, addr) < 0) { fclose(f); return -1; } - - *acl = newacl; - os_memcpy((*acl)[*num].addr, addr, ETH_ALEN); - os_memset(&(*acl)[*num].vlan_id, 0, - sizeof((*acl)[*num].vlan_id)); - (*acl)[*num].vlan_id.untagged = vlan_id; - (*acl)[*num].vlan_id.notempty = !!vlan_id; - (*num)++; } fclose(f); - qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp); + if (*acl) + qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp); return 0; } #ifdef EAP_SERVER + +static int hostapd_config_eap_user_salted(struct hostapd_eap_user *user, + const char *hash, size_t len, + char **pos, int line, + const char *fname) +{ + char *pos2 = *pos; + + while (*pos2 != '\0' && *pos2 != ' ' && *pos2 != '\t' && *pos2 != '#') + pos2++; + + if (pos2 - *pos < (int) (2 * (len + 1))) { /* at least 1 byte of salt */ + wpa_printf(MSG_ERROR, + "Invalid salted %s hash on line %d in '%s'", + hash, line, fname); + return -1; + } + + user->password = os_malloc(len); + if (!user->password) { + wpa_printf(MSG_ERROR, + "Failed to allocate memory for salted %s hash", + hash); + return -1; + } + + if (hexstr2bin(*pos, user->password, len) < 0) { + wpa_printf(MSG_ERROR, + "Invalid salted password on line %d in '%s'", + line, fname); + return -1; + } + user->password_len = len; + *pos += 2 * len; + + user->salt_len = (pos2 - *pos) / 2; + user->salt = os_malloc(user->salt_len); + if (!user->salt) { + wpa_printf(MSG_ERROR, + "Failed to allocate memory for salted %s hash", + hash); + return -1; + } + + if (hexstr2bin(*pos, user->salt, user->salt_len) < 0) { + wpa_printf(MSG_ERROR, + "Invalid salt for password on line %d in '%s'", + line, fname); + return -1; + } + + *pos = pos2; + return 0; +} + + static int hostapd_config_read_eap_user(const char *fname, struct hostapd_bss_config *conf) { @@ -223,9 +297,6 @@ static int hostapd_config_read_eap_user(const char *fname, int line = 0, ret = 0, num_methods; struct hostapd_eap_user *user = NULL, *tail = NULL, *new_user = NULL; - if (!fname) - return 0; - if (os_strncmp(fname, "sqlite:", 7) == 0) { #ifdef CONFIG_SQLITE os_free(conf->eap_user_sqlite); @@ -312,13 +383,12 @@ static int hostapd_config_read_eap_user(const char *fname, goto failed; } - user->identity = os_malloc(pos - start); + user->identity = os_memdup(start, pos - start); if (user->identity == NULL) { wpa_printf(MSG_ERROR, "Failed to allocate " "memory for EAP identity"); goto failed; } - os_memcpy(user->identity, start, pos - start); user->identity_len = pos - start; if (pos[0] == '"' && pos[1] == '*') { @@ -436,13 +506,12 @@ static int hostapd_config_read_eap_user(const char *fname, goto failed; } - user->password = os_malloc(pos - start); + user->password = os_memdup(start, pos - start); if (user->password == NULL) { wpa_printf(MSG_ERROR, "Failed to allocate " "memory for EAP password"); goto failed; } - os_memcpy(user->password, start, pos - start); user->password_len = pos - start; pos++; @@ -471,6 +540,24 @@ static int hostapd_config_read_eap_user(const char *fname, user->password_len = 16; user->password_hash = 1; pos = pos2; + } else if (os_strncmp(pos, "ssha1:", 6) == 0) { + pos += 6; + if (hostapd_config_eap_user_salted(user, "sha1", 20, + &pos, + line, fname) < 0) + goto failed; + } else if (os_strncmp(pos, "ssha256:", 8) == 0) { + pos += 8; + if (hostapd_config_eap_user_salted(user, "sha256", 32, + &pos, + line, fname) < 0) + goto failed; + } else if (os_strncmp(pos, "ssha512:", 8) == 0) { + pos += 8; + if (hostapd_config_eap_user_salted(user, "sha512", 64, + &pos, + line, fname) < 0) + goto failed; } else { pos2 = pos; while (*pos2 != '\0' && *pos2 != ' ' && @@ -522,19 +609,15 @@ static int hostapd_config_read_eap_user(const char *fname, fclose(f); if (ret == 0) { - user = conf->eap_user; - while (user) { - struct hostapd_eap_user *prev; - - prev = user; - user = user->next; - hostapd_config_free_eap_user(prev); - } + hostapd_config_free_eap_users(conf->eap_user); conf->eap_user = new_user; + } else { + hostapd_config_free_eap_users(new_user); } return ret; } + #endif /* EAP_SERVER */ @@ -684,12 +767,16 @@ static int hostapd_config_parse_key_mgmt(int line, const char *value) val |= WPA_KEY_MGMT_PSK; else if (os_strcmp(start, "WPA-EAP") == 0) val |= WPA_KEY_MGMT_IEEE8021X; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP else if (os_strcmp(start, "FT-PSK") == 0) val |= WPA_KEY_MGMT_FT_PSK; else if (os_strcmp(start, "FT-EAP") == 0) val |= WPA_KEY_MGMT_FT_IEEE8021X; -#endif /* CONFIG_IEEE80211R */ +#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_AP */ #ifdef CONFIG_IEEE80211W else if (os_strcmp(start, "WPA-PSK-SHA256") == 0) val |= WPA_KEY_MGMT_PSK_SHA256; @@ -710,6 +797,30 @@ static int hostapd_config_parse_key_mgmt(int line, const char *value) 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_AP + 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_AP */ +#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 */ +#ifdef CONFIG_HS20 + else if (os_strcmp(start, "OSEN") == 0) + val |= WPA_KEY_MGMT_OSEN; +#endif /* CONFIG_HS20 */ else { wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'", line, start); @@ -755,17 +866,34 @@ static int hostapd_config_read_wep(struct hostapd_wep_keys *wep, int keyidx, { size_t len = os_strlen(val); - if (keyidx < 0 || keyidx > 3 || wep->key[keyidx] != NULL) + if (keyidx < 0 || keyidx > 3) + return -1; + + if (len == 0) { + int i, set = 0; + + bin_clear_free(wep->key[keyidx], wep->len[keyidx]); + wep->key[keyidx] = NULL; + wep->len[keyidx] = 0; + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (wep->key[i]) + set++; + } + if (!set) + wep->keys_set = 0; + return 0; + } + + if (wep->key[keyidx] != NULL) return -1; if (val[0] == '"') { if (len < 2 || val[len - 1] != '"') return -1; len -= 2; - wep->key[keyidx] = os_malloc(len); + wep->key[keyidx] = os_memdup(val + 1, len); if (wep->key[keyidx] == NULL) return -1; - os_memcpy(wep->key[keyidx], val + 1, len); wep->len[keyidx] = len; } else { if (len & 1) @@ -978,7 +1106,27 @@ static int hostapd_config_tx_queue(struct hostapd_config *conf, } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP + +static int rkh_derive_key(const char *pos, u8 *key, size_t key_len) +{ + u8 oldkey[16]; + int ret; + + if (!hexstr2bin(pos, key, key_len)) + return 0; + + /* Try to use old short key for backwards compatibility */ + if (hexstr2bin(pos, oldkey, sizeof(oldkey))) + return -1; + + ret = hmac_sha256_kdf(oldkey, sizeof(oldkey), "FT OLDKEY", NULL, 0, + key, key_len); + os_memset(oldkey, 0, sizeof(oldkey)); + return ret; +} + + static int add_r0kh(struct hostapd_bss_config *bss, char *value) { struct ft_remote_r0kh *r0kh; @@ -1012,7 +1160,7 @@ static int add_r0kh(struct hostapd_bss_config *bss, char *value) os_memcpy(r0kh->id, pos, r0kh->id_len); pos = next; - if (hexstr2bin(pos, r0kh->key, sizeof(r0kh->key))) { + if (rkh_derive_key(pos, r0kh->key, sizeof(r0kh->key)) < 0) { wpa_printf(MSG_ERROR, "Invalid R0KH key: '%s'", pos); os_free(r0kh); return -1; @@ -1057,7 +1205,7 @@ static int add_r1kh(struct hostapd_bss_config *bss, char *value) } pos = next; - if (hexstr2bin(pos, r1kh->key, sizeof(r1kh->key))) { + if (rkh_derive_key(pos, r1kh->key, sizeof(r1kh->key)) < 0) { wpa_printf(MSG_ERROR, "Invalid R1KH key: '%s'", pos); os_free(r1kh); return -1; @@ -1068,7 +1216,7 @@ static int add_r1kh(struct hostapd_bss_config *bss, char *value) return 0; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211N @@ -1085,6 +1233,12 @@ static int hostapd_config_ht_capab(struct hostapd_config *conf, conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; conf->secondary_channel = 1; } + if (os_strstr(capab, "[HT40+]") && os_strstr(capab, "[HT40-]")) { + conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; + conf->ht40_plus_minus_allowed = 1; + } + if (!os_strstr(capab, "[HT40+]") && !os_strstr(capab, "[HT40-]")) + conf->secondary_channel = 0; if (os_strstr(capab, "[SMPS-STATIC]")) { conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK; conf->ht_capab |= HT_CAP_INFO_SMPS_STATIC; @@ -1307,6 +1461,44 @@ static int parse_venue_name(struct hostapd_bss_config *bss, char *pos, } +static int parse_venue_url(struct hostapd_bss_config *bss, char *pos, + int line) +{ + char *sep; + size_t nlen; + struct hostapd_venue_url *url; + int ret = -1; + + sep = os_strchr(pos, ':'); + if (!sep) + goto fail; + *sep++ = '\0'; + + nlen = os_strlen(sep); + if (nlen > 254) + goto fail; + + url = os_realloc_array(bss->venue_url, bss->venue_url_count + 1, + sizeof(struct hostapd_venue_url)); + if (!url) + goto fail; + + bss->venue_url = url; + url = &bss->venue_url[bss->venue_url_count++]; + + url->venue_number = atoi(pos); + url->url_len = nlen; + os_memcpy(url->url, sep, nlen); + + ret = 0; +fail: + if (ret) + wpa_printf(MSG_ERROR, "Line %d: Invalid venue_url '%s'", + line, pos); + return ret; +} + + static int parse_3gpp_cell_net(struct hostapd_bss_config *bss, char *buf, int line) { @@ -1857,6 +2049,24 @@ static int hs20_parse_osu_nai(struct hostapd_bss_config *bss, } +static int hs20_parse_osu_nai2(struct hostapd_bss_config *bss, + char *pos, int line) +{ + if (bss->last_osu == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); + return -1; + } + + os_free(bss->last_osu->osu_nai2); + bss->last_osu->osu_nai2 = os_strdup(pos); + if (bss->last_osu->osu_nai2 == NULL) + return -1; + bss->hs20_osu_providers_nai_count++; + + return 0; +} + + static int hs20_parse_osu_method_list(struct hostapd_bss_config *bss, char *pos, int line) { @@ -1916,6 +2126,25 @@ static int hs20_parse_osu_service_desc(struct hostapd_bss_config *bss, return 0; } + +static int hs20_parse_operator_icon(struct hostapd_bss_config *bss, char *pos, + int line) +{ + char **n; + + n = os_realloc_array(bss->hs20_operator_icon, + bss->hs20_operator_icon_count + 1, sizeof(char *)); + if (!n) + return -1; + bss->hs20_operator_icon = n; + bss->hs20_operator_icon[bss->hs20_operator_icon_count] = os_strdup(pos); + if (!bss->hs20_operator_icon[bss->hs20_operator_icon_count]) + return -1; + bss->hs20_operator_icon_count++; + + return 0; +} + #endif /* CONFIG_HS20 */ @@ -1986,6 +2215,118 @@ static int parse_wpabuf_hex(int line, const char *name, struct wpabuf **buf, } +#ifdef CONFIG_FILS +static int parse_fils_realm(struct hostapd_bss_config *bss, const char *val) +{ + struct fils_realm *realm; + size_t len; + + len = os_strlen(val); + realm = os_zalloc(sizeof(*realm) + len + 1); + if (!realm) + return -1; + + os_memcpy(realm->realm, val, len); + if (fils_domain_name_hash(val, realm->hash) < 0) { + os_free(realm); + return -1; + } + dl_list_add_tail(&bss->fils_realms, &realm->list); + + return 0; +} +#endif /* CONFIG_FILS */ + + +#ifdef EAP_SERVER +static unsigned int parse_tls_flags(const char *val) +{ + unsigned int flags = 0; + + /* Disable TLS v1.3 by default for now to avoid interoperability issue. + * This can be enabled by default once the implementation has been fully + * completed and tested with other implementations. */ + flags |= TLS_CONN_DISABLE_TLSv1_3; + + if (os_strstr(val, "[ALLOW-SIGN-RSA-MD5]")) + flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5; + if (os_strstr(val, "[DISABLE-TIME-CHECKS]")) + flags |= TLS_CONN_DISABLE_TIME_CHECKS; + if (os_strstr(val, "[DISABLE-TLSv1.0]")) + flags |= TLS_CONN_DISABLE_TLSv1_0; + if (os_strstr(val, "[DISABLE-TLSv1.1]")) + flags |= TLS_CONN_DISABLE_TLSv1_1; + if (os_strstr(val, "[DISABLE-TLSv1.2]")) + flags |= TLS_CONN_DISABLE_TLSv1_2; + if (os_strstr(val, "[DISABLE-TLSv1.3]")) + flags |= TLS_CONN_DISABLE_TLSv1_3; + if (os_strstr(val, "[ENABLE-TLSv1.3]")) + flags &= ~TLS_CONN_DISABLE_TLSv1_3; + if (os_strstr(val, "[SUITEB]")) + flags |= TLS_CONN_SUITEB; + if (os_strstr(val, "[SUITEB-NO-ECDH]")) + flags |= TLS_CONN_SUITEB_NO_ECDH | TLS_CONN_SUITEB; + + return flags; +} +#endif /* EAP_SERVER */ + + +#ifdef CONFIG_SAE +static int parse_sae_password(struct hostapd_bss_config *bss, const char *val) +{ + struct sae_password_entry *pw; + const char *pos = val, *pos2, *end = NULL; + + pw = os_zalloc(sizeof(*pw)); + if (!pw) + return -1; + os_memset(pw->peer_addr, 0xff, ETH_ALEN); /* default to wildcard */ + + pos2 = os_strstr(pos, "|mac="); + if (pos2) { + end = pos2; + pos2 += 5; + if (hwaddr_aton(pos2, pw->peer_addr) < 0) + goto fail; + pos = pos2 + ETH_ALEN * 3 - 1; + } + + pos2 = os_strstr(pos, "|id="); + if (pos2) { + if (!end) + end = pos2; + pos2 += 4; + pw->identifier = os_strdup(pos2); + if (!pw->identifier) + goto fail; + } + + if (!end) { + pw->password = os_strdup(val); + if (!pw->password) + goto fail; + } else { + pw->password = os_malloc(end - val + 1); + if (!pw->password) + goto fail; + os_memcpy(pw->password, val, end - val); + pw->password[end - val] = '\0'; + } + + pw->next = bss->sae_passwords; + bss->sae_passwords = pw; + + return 0; +fail: + str_clear_free(pw->password); + os_free(pw->identifier); + os_free(pw); + return -1; +} +#endif /* CONFIG_SAE */ + + static int hostapd_config_fill(struct hostapd_config *conf, struct hostapd_bss_config *bss, const char *buf, char *pos, int line) @@ -2001,20 +2342,21 @@ static int hostapd_config_fill(struct hostapd_config *conf, os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge)); } else if (os_strcmp(buf, "driver") == 0) { int j; - /* clear to get error below if setting is invalid */ - conf->driver = NULL; + const struct wpa_driver_ops *driver = NULL; + for (j = 0; wpa_drivers[j]; j++) { if (os_strcmp(pos, wpa_drivers[j]->name) == 0) { - conf->driver = wpa_drivers[j]; + driver = wpa_drivers[j]; break; } } - if (conf->driver == NULL) { + if (!driver) { wpa_printf(MSG_ERROR, "Line %d: invalid/unknown driver '%s'", line, pos); return 1; } + conf->driver = driver; } else if (os_strcmp(buf, "driver_params") == 0) { os_free(conf->driver_params); conf->driver_params = os_strdup(pos); @@ -2058,13 +2400,16 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "utf8_ssid") == 0) { bss->ssid.utf8_ssid = atoi(pos) > 0; } else if (os_strcmp(buf, "macaddr_acl") == 0) { - bss->macaddr_acl = atoi(pos); - if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED && - bss->macaddr_acl != DENY_UNLESS_ACCEPTED && - bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) { + enum macaddr_acl acl = atoi(pos); + + if (acl != ACCEPT_UNLESS_DENIED && + acl != DENY_UNLESS_ACCEPTED && + acl != USE_EXTERNAL_RADIUS_AUTH) { wpa_printf(MSG_ERROR, "Line %d: unknown macaddr_acl %d", - line, bss->macaddr_acl); + line, acl); + return 1; } + bss->macaddr_acl = acl; } else if (os_strcmp(buf, "accept_mac_file") == 0) { if (hostapd_config_read_maclist(pos, &bss->accept_mac, &bss->num_accept_mac)) { @@ -2091,8 +2436,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->skip_inactivity_poll = atoi(pos); } else if (os_strcmp(buf, "country_code") == 0) { os_memcpy(conf->country, pos, 2); - /* FIX: make this configurable */ - conf->country[2] = ' '; + } else if (os_strcmp(buf, "country3") == 0) { + conf->country[2] = strtol(pos, NULL, 16); } else if (os_strcmp(buf, "ieee80211d") == 0) { conf->ieee80211d = atoi(pos); } else if (os_strcmp(buf, "ieee80211h") == 0) { @@ -2100,13 +2445,15 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "ieee8021x") == 0) { bss->ieee802_1x = atoi(pos); } else if (os_strcmp(buf, "eapol_version") == 0) { - bss->eapol_version = atoi(pos); - if (bss->eapol_version < 1 || bss->eapol_version > 2) { + int eapol_version = atoi(pos); + + if (eapol_version < 1 || eapol_version > 2) { wpa_printf(MSG_ERROR, "Line %d: invalid EAPOL version (%d): '%s'.", - line, bss->eapol_version, pos); + line, eapol_version, pos); return 1; } + bss->eapol_version = eapol_version; wpa_printf(MSG_DEBUG, "eapol_version=%d", bss->eapol_version); #ifdef EAP_SERVER } else if (os_strcmp(buf, "eap_authenticator") == 0) { @@ -2133,6 +2480,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->check_crl = atoi(pos); } else if (os_strcmp(buf, "tls_session_lifetime") == 0) { bss->tls_session_lifetime = atoi(pos); + } else if (os_strcmp(buf, "tls_flags") == 0) { + bss->tls_flags = parse_tls_flags(pos); } else if (os_strcmp(buf, "ocsp_stapling_response") == 0) { os_free(bss->ocsp_stapling_response); bss->ocsp_stapling_response = os_strdup(pos); @@ -2207,8 +2556,10 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "pwd_group") == 0) { bss->pwd_group = atoi(pos); #endif /* EAP_SERVER_PWD */ +#ifdef CONFIG_ERP } else if (os_strcmp(buf, "eap_server_erp") == 0) { bss->eap_server_erp = atoi(pos); +#endif /* CONFIG_ERP */ #endif /* EAP_SERVER */ } else if (os_strcmp(buf, "eap_message") == 0) { char *term; @@ -2234,24 +2585,25 @@ static int hostapd_config_fill(struct hostapd_config *conf, os_free(bss->erp_domain); bss->erp_domain = os_strdup(pos); } else if (os_strcmp(buf, "wep_key_len_broadcast") == 0) { - bss->default_wep_key_len = atoi(pos); - if (bss->default_wep_key_len > 13) { - wpa_printf(MSG_ERROR, "Line %d: invalid WEP key len %lu (= %lu bits)", - line, - (unsigned long) bss->default_wep_key_len, - (unsigned long) - bss->default_wep_key_len * 8); + int val = atoi(pos); + + if (val < 0 || val > 13) { + wpa_printf(MSG_ERROR, + "Line %d: invalid WEP key len %d (= %d bits)", + line, val, val * 8); return 1; } + bss->default_wep_key_len = val; } else if (os_strcmp(buf, "wep_key_len_unicast") == 0) { - bss->individual_wep_key_len = atoi(pos); - if (bss->individual_wep_key_len < 0 || - bss->individual_wep_key_len > 13) { - wpa_printf(MSG_ERROR, "Line %d: invalid WEP key len %d (= %d bits)", - line, bss->individual_wep_key_len, - bss->individual_wep_key_len * 8); + int val = atoi(pos); + + if (val < 0 || val > 13) { + wpa_printf(MSG_ERROR, + "Line %d: invalid WEP key len %d (= %d bits)", + line, val, val * 8); return 1; } + bss->individual_wep_key_len = val; } else if (os_strcmp(buf, "wep_rekey_period") == 0) { bss->wep_rekeying_period = atoi(pos); if (bss->wep_rekeying_period < 0) { @@ -2433,12 +2785,37 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->wpa = atoi(pos); } else if (os_strcmp(buf, "wpa_group_rekey") == 0) { bss->wpa_group_rekey = atoi(pos); + bss->wpa_group_rekey_set = 1; } else if (os_strcmp(buf, "wpa_strict_rekey") == 0) { bss->wpa_strict_rekey = atoi(pos); } else if (os_strcmp(buf, "wpa_gmk_rekey") == 0) { bss->wpa_gmk_rekey = atoi(pos); } else if (os_strcmp(buf, "wpa_ptk_rekey") == 0) { bss->wpa_ptk_rekey = atoi(pos); + } else if (os_strcmp(buf, "wpa_group_update_count") == 0) { + char *endp; + unsigned long val = strtoul(pos, &endp, 0); + + if (*endp || val < 1 || val > (u32) -1) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid wpa_group_update_count=%lu; allowed range 1..4294967295", + line, val); + return 1; + } + bss->wpa_group_update_count = (u32) val; + } else if (os_strcmp(buf, "wpa_pairwise_update_count") == 0) { + char *endp; + unsigned long val = strtoul(pos, &endp, 0); + + if (*endp || val < 1 || val > (u32) -1) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid wpa_pairwise_update_count=%lu; allowed range 1..4294967295", + line, val); + return 1; + } + bss->wpa_pairwise_update_count = (u32) val; + } else if (os_strcmp(buf, "wpa_disable_eapol_key_retries") == 0) { + bss->wpa_disable_eapol_key_retries = atoi(pos); } else if (os_strcmp(buf, "wpa_passphrase") == 0) { int len = os_strlen(pos); if (len < 8 || len > 63) { @@ -2497,7 +2874,7 @@ static int hostapd_config_fill(struct hostapd_config *conf, if (bss->wpa_pairwise & (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) { wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'", - bss->wpa_pairwise, pos); + line, pos); return 1; } } else if (os_strcmp(buf, "rsn_pairwise") == 0) { @@ -2507,7 +2884,21 @@ static int hostapd_config_fill(struct hostapd_config *conf, if (bss->rsn_pairwise & (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) { wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'", - bss->rsn_pairwise, pos); + line, pos); + return 1; + } + } else if (os_strcmp(buf, "group_cipher") == 0) { + bss->group_cipher = hostapd_config_parse_cipher(line, pos); + if (bss->group_cipher == -1 || bss->group_cipher == 0) + return 1; + if (bss->group_cipher != WPA_CIPHER_TKIP && + bss->group_cipher != WPA_CIPHER_CCMP && + bss->group_cipher != WPA_CIPHER_GCMP && + bss->group_cipher != WPA_CIPHER_GCMP_256 && + bss->group_cipher != WPA_CIPHER_CCMP_256) { + wpa_printf(MSG_ERROR, + "Line %d: unsupported group cipher suite '%s'", + line, pos); return 1; } #ifdef CONFIG_RSN_PREAUTH @@ -2517,11 +2908,10 @@ static int hostapd_config_fill(struct hostapd_config *conf, os_free(bss->rsn_preauth_interfaces); bss->rsn_preauth_interfaces = os_strdup(pos); #endif /* CONFIG_RSN_PREAUTH */ -#ifdef CONFIG_PEERKEY } else if (os_strcmp(buf, "peerkey") == 0) { - bss->peerkey = atoi(pos); -#endif /* CONFIG_PEERKEY */ -#ifdef CONFIG_IEEE80211R + wpa_printf(MSG_INFO, + "Line %d: Obsolete peerkey parameter ignored", line); +#ifdef CONFIG_IEEE80211R_AP } else if (os_strcmp(buf, "mobility_domain") == 0) { if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN || hexstr2bin(pos, bss->mobility_domain, @@ -2540,9 +2930,22 @@ static int hostapd_config_fill(struct hostapd_config *conf, return 1; } } else if (os_strcmp(buf, "r0_key_lifetime") == 0) { + /* DEPRECATED: Use ft_r0_key_lifetime instead. */ + bss->r0_key_lifetime = atoi(pos) * 60; + } else if (os_strcmp(buf, "ft_r0_key_lifetime") == 0) { bss->r0_key_lifetime = atoi(pos); + } else if (os_strcmp(buf, "r1_max_key_lifetime") == 0) { + bss->r1_max_key_lifetime = atoi(pos); } else if (os_strcmp(buf, "reassociation_deadline") == 0) { bss->reassociation_deadline = atoi(pos); + } else if (os_strcmp(buf, "rkh_pos_timeout") == 0) { + bss->rkh_pos_timeout = atoi(pos); + } else if (os_strcmp(buf, "rkh_neg_timeout") == 0) { + bss->rkh_neg_timeout = atoi(pos); + } else if (os_strcmp(buf, "rkh_pull_timeout") == 0) { + bss->rkh_pull_timeout = atoi(pos); + } else if (os_strcmp(buf, "rkh_pull_retries") == 0) { + bss->rkh_pull_retries = atoi(pos); } else if (os_strcmp(buf, "r0kh") == 0) { if (add_r0kh(bss, pos) < 0) { wpa_printf(MSG_DEBUG, "Line %d: Invalid r0kh '%s'", @@ -2559,7 +2962,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->pmk_r1_push = atoi(pos); } else if (os_strcmp(buf, "ft_over_ds") == 0) { bss->ft_over_ds = atoi(pos); -#endif /* CONFIG_IEEE80211R */ + } else if (os_strcmp(buf, "ft_psk_generate_local") == 0) { + bss->ft_psk_generate_local = atoi(pos); +#endif /* CONFIG_IEEE80211R_AP */ #ifndef CONFIG_NO_CTRL_IFACE } else if (os_strcmp(buf, "ctrl_interface") == 0) { os_free(bss->ctrl_interface); @@ -2637,6 +3042,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, line, pos); return 1; } + } else if (os_strcmp(buf, "acs_exclude_dfs") == 0) { + conf->acs_exclude_dfs = atoi(pos); } else if (os_strcmp(buf, "channel") == 0) { if (os_strcmp(pos, "acs_survey") == 0) { #ifndef CONFIG_ACS @@ -2687,21 +3094,34 @@ static int hostapd_config_fill(struct hostapd_config *conf, } #endif /* CONFIG_ACS */ } else if (os_strcmp(buf, "dtim_period") == 0) { - bss->dtim_period = atoi(pos); - if (bss->dtim_period < 1 || bss->dtim_period > 255) { + int val = atoi(pos); + + if (val < 1 || val > 255) { wpa_printf(MSG_ERROR, "Line %d: invalid dtim_period %d", - line, bss->dtim_period); + line, val); return 1; } + bss->dtim_period = val; } else if (os_strcmp(buf, "bss_load_update_period") == 0) { - bss->bss_load_update_period = atoi(pos); - if (bss->bss_load_update_period < 0 || - bss->bss_load_update_period > 100) { + int val = atoi(pos); + + if (val < 0 || val > 100) { wpa_printf(MSG_ERROR, "Line %d: invalid bss_load_update_period %d", - line, bss->bss_load_update_period); + line, val); return 1; } + bss->bss_load_update_period = val; + } else if (os_strcmp(buf, "chan_util_avg_period") == 0) { + int val = atoi(pos); + + if (val < 0) { + wpa_printf(MSG_ERROR, + "Line %d: invalid chan_util_avg_period", + line); + return 1; + } + bss->chan_util_avg_period = val; } else if (os_strcmp(buf, "rts_threshold") == 0) { conf->rts_threshold = atoi(pos); if (conf->rts_threshold < -1 || conf->rts_threshold > 65535) { @@ -2741,6 +3161,40 @@ static int hostapd_config_fill(struct hostapd_config *conf, line); return 1; } + } else if (os_strcmp(buf, "beacon_rate") == 0) { + int val; + + if (os_strncmp(pos, "ht:", 3) == 0) { + val = atoi(pos + 3); + if (val < 0 || val > 31) { + wpa_printf(MSG_ERROR, + "Line %d: invalid beacon_rate HT-MCS %d", + line, val); + return 1; + } + conf->rate_type = BEACON_RATE_HT; + conf->beacon_rate = val; + } else if (os_strncmp(pos, "vht:", 4) == 0) { + val = atoi(pos + 4); + if (val < 0 || val > 9) { + wpa_printf(MSG_ERROR, + "Line %d: invalid beacon_rate VHT-MCS %d", + line, val); + return 1; + } + conf->rate_type = BEACON_RATE_VHT; + conf->beacon_rate = val; + } else { + val = atoi(pos); + if (val < 10 || val > 10000) { + wpa_printf(MSG_ERROR, + "Line %d: invalid legacy beacon_rate %d", + line, val); + return 1; + } + conf->rate_type = BEACON_RATE_LEGACY; + conf->beacon_rate = val; + } } else if (os_strcmp(buf, "preamble") == 0) { if (atoi(pos)) conf->preamble = SHORT_PREAMBLE; @@ -2898,6 +3352,24 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "use_sta_nsts") == 0) { bss->use_sta_nsts = atoi(pos); #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX + } else if (os_strcmp(buf, "ieee80211ax") == 0) { + conf->ieee80211ax = atoi(pos); + } else if (os_strcmp(buf, "he_su_beamformer") == 0) { + conf->he_phy_capab.he_su_beamformer = atoi(pos); + } else if (os_strcmp(buf, "he_su_beamformee") == 0) { + conf->he_phy_capab.he_su_beamformee = atoi(pos); + } else if (os_strcmp(buf, "he_mu_beamformer") == 0) { + conf->he_phy_capab.he_mu_beamformer = atoi(pos); + } else if (os_strcmp(buf, "he_bss_color") == 0) { + conf->he_op.he_bss_color = atoi(pos); + } else if (os_strcmp(buf, "he_default_pe_duration") == 0) { + conf->he_op.he_default_pe_duration = atoi(pos); + } else if (os_strcmp(buf, "he_twt_required") == 0) { + conf->he_op.he_twt_required = atoi(pos); + } else if (os_strcmp(buf, "he_rts_threshold") == 0) { + conf->he_op.he_rts_threshold = atoi(pos); +#endif /* CONFIG_IEEE80211AX */ } else if (os_strcmp(buf, "max_listen_interval") == 0) { bss->max_listen_interval = atoi(pos); } else if (os_strcmp(buf, "disable_pmksa_caching") == 0) { @@ -2978,7 +3450,10 @@ static int hostapd_config_fill(struct hostapd_config *conf, } } else if (os_strcmp(buf, "ap_pin") == 0) { os_free(bss->ap_pin); - bss->ap_pin = os_strdup(pos); + if (*pos == '\0') + bss->ap_pin = NULL; + else + bss->ap_pin = os_strdup(pos); } else if (os_strcmp(buf, "skip_cred_build") == 0) { bss->skip_cred_build = atoi(pos); } else if (os_strcmp(buf, "extra_cred") == 0) { @@ -3089,12 +3564,14 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->time_zone = os_strdup(pos); if (bss->time_zone == NULL) return 1; -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP } else if (os_strcmp(buf, "wnm_sleep_mode") == 0) { bss->wnm_sleep_mode = atoi(pos); + } else if (os_strcmp(buf, "wnm_sleep_mode_no_keys") == 0) { + bss->wnm_sleep_mode_no_keys = atoi(pos); } else if (os_strcmp(buf, "bss_transition") == 0) { bss->bss_transition = atoi(pos); -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ #ifdef CONFIG_INTERWORKING } else if (os_strcmp(buf, "interworking") == 0) { bss->interworking = atoi(pos); @@ -3132,6 +3609,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "venue_name") == 0) { if (parse_venue_name(bss, pos, line) < 0) return 1; + } else if (os_strcmp(buf, "venue_url") == 0) { + if (parse_venue_url(bss, pos, line) < 0) + return 1; } else if (os_strcmp(buf, "network_auth_type") == 0) { u8 auth_type; u16 redirect_url_len; @@ -3210,7 +3690,15 @@ static int hostapd_config_fill(struct hostapd_config *conf, if (parse_anqp_elem(bss, pos, line) < 0) return 1; } else if (os_strcmp(buf, "gas_frag_limit") == 0) { - bss->gas_frag_limit = atoi(pos); + int val = atoi(pos); + + if (val <= 0) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid gas_frag_limit '%s'", + line, pos); + return 1; + } + bss->gas_frag_limit = val; } else if (os_strcmp(buf, "gas_comeback_delay") == 0) { bss->gas_comeback_delay = atoi(pos); } else if (os_strcmp(buf, "qos_map_set") == 0) { @@ -3291,6 +3779,9 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "osu_nai") == 0) { if (hs20_parse_osu_nai(bss, pos, line) < 0) return 1; + } else if (os_strcmp(buf, "osu_nai2") == 0) { + if (hs20_parse_osu_nai2(bss, pos, line) < 0) + return 1; } else if (os_strcmp(buf, "osu_method_list") == 0) { if (hs20_parse_osu_method_list(bss, pos, line) < 0) return 1; @@ -3300,15 +3791,30 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "osu_service_desc") == 0) { if (hs20_parse_osu_service_desc(bss, pos, line) < 0) return 1; + } else if (os_strcmp(buf, "operator_icon") == 0) { + if (hs20_parse_operator_icon(bss, pos, line) < 0) + return 1; } else if (os_strcmp(buf, "subscr_remediation_url") == 0) { os_free(bss->subscr_remediation_url); bss->subscr_remediation_url = os_strdup(pos); } else if (os_strcmp(buf, "subscr_remediation_method") == 0) { bss->subscr_remediation_method = atoi(pos); + } else if (os_strcmp(buf, "hs20_t_c_filename") == 0) { + os_free(bss->t_c_filename); + bss->t_c_filename = os_strdup(pos); + } else if (os_strcmp(buf, "hs20_t_c_timestamp") == 0) { + bss->t_c_timestamp = strtol(pos, NULL, 0); + } else if (os_strcmp(buf, "hs20_t_c_server_url") == 0) { + os_free(bss->t_c_server_url); + bss->t_c_server_url = os_strdup(pos); #endif /* CONFIG_HS20 */ #ifdef CONFIG_MBO } else if (os_strcmp(buf, "mbo") == 0) { bss->mbo_enabled = atoi(pos); + } else if (os_strcmp(buf, "mbo_cell_data_conn_pref") == 0) { + bss->mbo_cell_data_conn_pref = atoi(pos); + } else if (os_strcmp(buf, "oce") == 0) { + bss->oce = atoi(pos); #endif /* CONFIG_MBO */ #ifdef CONFIG_TESTING_OPTIONS #define PARSE_TEST_PROBABILITY(_val) \ @@ -3377,7 +3883,20 @@ static int hostapd_config_fill(struct hostapd_config *conf, wpabuf_free(bss->own_ie_override); bss->own_ie_override = tmp; + } else if (os_strcmp(buf, "sae_reflection_attack") == 0) { + bss->sae_reflection_attack = atoi(pos); + } else if (os_strcmp(buf, "sae_commit_override") == 0) { + wpabuf_free(bss->sae_commit_override); + bss->sae_commit_override = wpabuf_parse_bin(pos); #endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_SAE + } else if (os_strcmp(buf, "sae_password") == 0) { + if (parse_sae_password(bss, pos) < 0) { + wpa_printf(MSG_ERROR, "Line %d: Invalid sae_password", + line); + return 1; + } +#endif /* CONFIG_SAE */ } else if (os_strcmp(buf, "vendor_elements") == 0) { if (parse_wpabuf_hex(line, buf, &bss->vendor_elements, pos)) return 1; @@ -3386,6 +3905,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, return 1; } else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) { bss->sae_anti_clogging_threshold = atoi(pos); + } else if (os_strcmp(buf, "sae_sync") == 0) { + bss->sae_sync = atoi(pos); } else if (os_strcmp(buf, "sae_groups") == 0) { if (hostapd_parse_intlist(&bss->sae_groups, pos)) { wpa_printf(MSG_ERROR, @@ -3393,6 +3914,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, line, pos); return 1; } + } else if (os_strcmp(buf, "sae_require_mfp") == 0) { + bss->sae_require_mfp = atoi(pos); } else if (os_strcmp(buf, "local_pwr_constraint") == 0) { int val = atoi(pos); if (val < 0 || val > 255) { @@ -3478,19 +4001,116 @@ static int hostapd_config_fill(struct hostapd_config *conf, } else if (os_strcmp(buf, "lci") == 0) { wpabuf_free(conf->lci); conf->lci = wpabuf_parse_bin(pos); + if (conf->lci && wpabuf_len(conf->lci) == 0) { + wpabuf_free(conf->lci); + conf->lci = NULL; + } } else if (os_strcmp(buf, "civic") == 0) { wpabuf_free(conf->civic); conf->civic = wpabuf_parse_bin(pos); + if (conf->civic && wpabuf_len(conf->civic) == 0) { + wpabuf_free(conf->civic); + conf->civic = NULL; + } } else if (os_strcmp(buf, "rrm_neighbor_report") == 0) { if (atoi(pos)) bss->radio_measurements[0] |= WLAN_RRM_CAPS_NEIGHBOR_REPORT; + } else if (os_strcmp(buf, "rrm_beacon_report") == 0) { + if (atoi(pos)) + bss->radio_measurements[0] |= + WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE | + WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE | + WLAN_RRM_CAPS_BEACON_REPORT_TABLE; } else if (os_strcmp(buf, "gas_address3") == 0) { bss->gas_address3 = atoi(pos); + } else if (os_strcmp(buf, "stationary_ap") == 0) { + conf->stationary_ap = atoi(pos); } else if (os_strcmp(buf, "ftm_responder") == 0) { bss->ftm_responder = atoi(pos); } else if (os_strcmp(buf, "ftm_initiator") == 0) { bss->ftm_initiator = atoi(pos); +#ifdef CONFIG_FILS + } else if (os_strcmp(buf, "fils_cache_id") == 0) { + if (hexstr2bin(pos, bss->fils_cache_id, FILS_CACHE_ID_LEN)) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid fils_cache_id '%s'", + line, pos); + return 1; + } + bss->fils_cache_id_set = 1; + } else if (os_strcmp(buf, "fils_realm") == 0) { + if (parse_fils_realm(bss, pos) < 0) + return 1; + } else if (os_strcmp(buf, "fils_dh_group") == 0) { + bss->fils_dh_group = atoi(pos); + } else if (os_strcmp(buf, "dhcp_server") == 0) { + if (hostapd_parse_ip_addr(pos, &bss->dhcp_server)) { + wpa_printf(MSG_ERROR, + "Line %d: invalid IP address '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "dhcp_rapid_commit_proxy") == 0) { + bss->dhcp_rapid_commit_proxy = atoi(pos); + } else if (os_strcmp(buf, "fils_hlp_wait_time") == 0) { + bss->fils_hlp_wait_time = atoi(pos); + } else if (os_strcmp(buf, "dhcp_server_port") == 0) { + bss->dhcp_server_port = atoi(pos); + } else if (os_strcmp(buf, "dhcp_relay_port") == 0) { + bss->dhcp_relay_port = atoi(pos); +#endif /* CONFIG_FILS */ + } else if (os_strcmp(buf, "multicast_to_unicast") == 0) { + bss->multicast_to_unicast = atoi(pos); + } else if (os_strcmp(buf, "broadcast_deauth") == 0) { + bss->broadcast_deauth = atoi(pos); +#ifdef CONFIG_DPP + } else if (os_strcmp(buf, "dpp_connector") == 0) { + os_free(bss->dpp_connector); + bss->dpp_connector = os_strdup(pos); + } else if (os_strcmp(buf, "dpp_netaccesskey") == 0) { + if (parse_wpabuf_hex(line, buf, &bss->dpp_netaccesskey, pos)) + return 1; + } else if (os_strcmp(buf, "dpp_netaccesskey_expiry") == 0) { + bss->dpp_netaccesskey_expiry = strtol(pos, NULL, 0); + } else if (os_strcmp(buf, "dpp_csign") == 0) { + if (parse_wpabuf_hex(line, buf, &bss->dpp_csign, pos)) + return 1; +#endif /* CONFIG_DPP */ +#ifdef CONFIG_OWE + } else if (os_strcmp(buf, "owe_transition_bssid") == 0) { + if (hwaddr_aton(pos, bss->owe_transition_bssid)) { + wpa_printf(MSG_ERROR, + "Line %d: invalid owe_transition_bssid", + line); + return 1; + } + } else if (os_strcmp(buf, "owe_transition_ssid") == 0) { + size_t slen; + char *str = wpa_config_parse_string(pos, &slen); + + if (!str || slen < 1 || slen > SSID_MAX_LEN) { + wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'", + line, pos); + os_free(str); + return 1; + } + os_memcpy(bss->owe_transition_ssid, str, slen); + bss->owe_transition_ssid_len = slen; + os_free(str); + } else if (os_strcmp(buf, "owe_transition_ifname") == 0) { + os_strlcpy(bss->owe_transition_ifname, pos, + sizeof(bss->owe_transition_ifname)); + } else if (os_strcmp(buf, "owe_groups") == 0) { + if (hostapd_parse_intlist(&bss->owe_groups, pos)) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid owe_groups value '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "coloc_intf_reporting") == 0) { + bss->coloc_intf_reporting = atoi(pos); +#endif /* CONFIG_OWE */ } else { wpa_printf(MSG_ERROR, "Line %d: unknown configuration item '%s'", diff --git a/hostapd/config_file.h b/hostapd/config_file.h index c98bdb683ba10..9830f5a2232f6 100644 --- a/hostapd/config_file.h +++ b/hostapd/config_file.h @@ -13,5 +13,10 @@ struct hostapd_config * hostapd_config_read(const char *fname); int hostapd_set_iface(struct hostapd_config *conf, struct hostapd_bss_config *bss, const char *field, char *value); +int hostapd_acl_comp(const void *a, const void *b); +int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num, + int vlan_id, const u8 *addr); +void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num, + const u8 *addr); #endif /* CONFIG_FILE_H */ diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index d7db4a7c3c484..75f12e543f210 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -1,6 +1,6 @@ /* * hostapd / UNIX domain socket -based control interface - * Copyright (c) 2004-2015, 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,6 +29,10 @@ #include "common/version.h" #include "common/ieee802_11_defs.h" #include "common/ctrl_iface_common.h" +#ifdef CONFIG_DPP +#include "common/dpp.h" +#endif /* CONFIG_DPP */ +#include "common/wpa_ctrl.h" #include "crypto/tls.h" #include "drivers/driver.h" #include "eapol_auth/eapol_auth_sm.h" @@ -50,6 +54,7 @@ #include "ap/beacon.h" #include "ap/neighbor_db.h" #include "ap/rrm.h" +#include "ap/dpp_hostapd.h" #include "wps/wps_defs.h" #include "wps/wps.h" #include "fst/fst_ctrl_iface.h" @@ -76,9 +81,9 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd, struct sockaddr_storage *from, - socklen_t fromlen) + socklen_t fromlen, const char *input) { - return ctrl_iface_attach(&hapd->ctrl_dst, from, fromlen); + return ctrl_iface_attach(&hapd->ctrl_dst, from, fromlen, input); } @@ -763,7 +768,7 @@ static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd, #endif /* CONFIG_INTERWORKING */ -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd, const char *cmd) @@ -838,7 +843,7 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, char *url = NULL; int ret; u8 nei_rep[1000]; - u8 *nei_pos = nei_rep; + int nei_len; u8 mbo[10]; size_t mbo_len = 0; @@ -888,99 +893,10 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, WPA_PUT_LE16(&bss_term_dur[10], atoi(end)); } - - /* - * BSS Transition Candidate List Entries - Neighbor Report elements - * neighbor=<BSSID>,<BSSID Information>,<Operating Class>, - * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>] - */ - pos = cmd; - while (pos) { - u8 *nei_start; - long int val; - char *endptr, *tmp; - - pos = os_strstr(pos, " neighbor="); - if (!pos) - break; - if (nei_pos + 15 > nei_rep + sizeof(nei_rep)) { - wpa_printf(MSG_DEBUG, - "Not enough room for additional neighbor"); - return -1; - } - pos += 10; - - nei_start = nei_pos; - *nei_pos++ = WLAN_EID_NEIGHBOR_REPORT; - nei_pos++; /* length to be filled in */ - - if (hwaddr_aton(pos, nei_pos)) { - wpa_printf(MSG_DEBUG, "Invalid BSSID"); - return -1; - } - nei_pos += ETH_ALEN; - pos += 17; - if (*pos != ',') { - wpa_printf(MSG_DEBUG, "Missing BSSID Information"); - return -1; - } - pos++; - - val = strtol(pos, &endptr, 0); - WPA_PUT_LE32(nei_pos, val); - nei_pos += 4; - if (*endptr != ',') { - wpa_printf(MSG_DEBUG, "Missing Operating Class"); - return -1; - } - pos = endptr + 1; - - *nei_pos++ = atoi(pos); /* Operating Class */ - pos = os_strchr(pos, ','); - if (pos == NULL) { - wpa_printf(MSG_DEBUG, "Missing Channel Number"); - return -1; - } - pos++; - - *nei_pos++ = atoi(pos); /* Channel Number */ - pos = os_strchr(pos, ','); - if (pos == NULL) { - wpa_printf(MSG_DEBUG, "Missing PHY Type"); - return -1; - } - pos++; - - *nei_pos++ = atoi(pos); /* PHY Type */ - end = os_strchr(pos, ' '); - tmp = os_strchr(pos, ','); - if (tmp && (!end || tmp < end)) { - /* Optional Subelements (hexdump) */ - size_t len; - - pos = tmp + 1; - end = os_strchr(pos, ' '); - if (end) - len = end - pos; - else - len = os_strlen(pos); - if (nei_pos + len / 2 > nei_rep + sizeof(nei_rep)) { - wpa_printf(MSG_DEBUG, - "Not enough room for neighbor subelements"); - return -1; - } - if (len & 0x01 || - hexstr2bin(pos, nei_pos, len / 2) < 0) { - wpa_printf(MSG_DEBUG, - "Invalid neighbor subelement info"); - return -1; - } - nei_pos += len / 2; - pos = end; - } - - nei_start[1] = nei_pos - nei_start - 2; - } + nei_len = ieee802_11_parse_candidate_list(cmd, nei_rep, + sizeof(nei_rep)); + if (nei_len < 0) + return -1; pos = os_strstr(cmd, " url="); if (pos) { @@ -1017,14 +933,16 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, if (ret != 3) { wpa_printf(MSG_DEBUG, "MBO requires three arguments: mbo=<reason>:<reassoc_delay>:<cell_pref>"); - return -1; + ret = -1; + goto fail; } if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP) { wpa_printf(MSG_DEBUG, "Invalid MBO transition reason code %u", mbo_reason); - return -1; + ret = -1; + goto fail; } /* Valid values for Cellular preference are: 0, 1, 255 */ @@ -1032,7 +950,8 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, wpa_printf(MSG_DEBUG, "Invalid MBO cellular capability %u", cell_pref); - return -1; + ret = -1; + goto fail; } if (reassoc_delay > 65535 || @@ -1040,7 +959,8 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, !(req_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))) { wpa_printf(MSG_DEBUG, "MBO: Assoc retry delay is only valid in disassoc imminent mode"); - return -1; + ret = -1; + goto fail; } *mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON; @@ -1063,14 +983,52 @@ static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd, ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer, valid_int, bss_term_dur, url, - nei_pos > nei_rep ? nei_rep : NULL, - nei_pos - nei_rep, mbo_len ? mbo : NULL, - mbo_len); + nei_len ? nei_rep : NULL, nei_len, + mbo_len ? mbo : NULL, mbo_len); +#ifdef CONFIG_MBO +fail: +#endif /* CONFIG_MBO */ os_free(url); return ret; } -#endif /* CONFIG_WNM */ + +static int hostapd_ctrl_iface_coloc_intf_req(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + const char *pos; + unsigned int auto_report, timeout; + + if (hwaddr_aton(cmd, addr)) { + wpa_printf(MSG_DEBUG, "Invalid STA MAC address"); + return -1; + } + + sta = ap_get_sta(hapd, addr); + if (!sta) { + wpa_printf(MSG_DEBUG, "Station " MACSTR + " not found for Collocated Interference Request", + MAC2STR(addr)); + return -1; + } + + pos = cmd + 17; + if (*pos != ' ') + return -1; + pos++; + auto_report = atoi(pos); + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + timeout = atoi(pos); + + return wnm_send_coloc_intf_req(hapd, sta, auto_report, timeout); +} + +#endif /* CONFIG_WNM_AP */ static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd, @@ -1096,7 +1054,7 @@ static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd, return pos - buf; pos += ret; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { ret = os_snprintf(pos, end - pos, "FT-PSK "); if (os_snprintf_error(end - pos, ret)) @@ -1109,6 +1067,14 @@ static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd, return pos - buf; pos += ret; } +#ifdef CONFIG_SHA384 + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { + ret = os_snprintf(pos, end - pos, "FT-EAP-SHA384 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_SHA384 */ #ifdef CONFIG_SAE if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) { ret = os_snprintf(pos, end - pos, "FT-SAE "); @@ -1117,7 +1083,21 @@ static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd, pos += ret; } #endif /* CONFIG_SAE */ -#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_FILS + if (hapd->conf->wpa_key_mgmt & WPA_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 (hapd->conf->wpa_key_mgmt & WPA_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_FILS */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 "); @@ -1154,6 +1134,38 @@ static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd, return pos - buf; pos += ret; } +#ifdef CONFIG_FILS + if (hapd->conf->wpa_key_mgmt & WPA_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 (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) { + ret = os_snprintf(pos, end - pos, "FILS-SHA384 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE + if (hapd->conf->wpa_key_mgmt & WPA_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 (hapd->conf->wpa_key_mgmt & WPA_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 */ if (pos > buf && *(pos - 1) == ' ') { *(pos - 1) = '\0'; @@ -1282,6 +1294,42 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, } +static void hostapd_disassoc_accept_mac(struct hostapd_data *hapd) +{ + struct sta_info *sta; + struct vlan_description vlan_id; + + if (hapd->conf->macaddr_acl != DENY_UNLESS_ACCEPTED) + return; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (!hostapd_maclist_found(hapd->conf->accept_mac, + hapd->conf->num_accept_mac, + sta->addr, &vlan_id) || + (vlan_id.notempty && + vlan_compare(&vlan_id, sta->vlan_desc))) + ap_sta_disconnect(hapd, sta, sta->addr, + WLAN_REASON_UNSPECIFIED); + } +} + + +static void hostapd_disassoc_deny_mac(struct hostapd_data *hapd) +{ + struct sta_info *sta; + struct vlan_description vlan_id; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (hostapd_maclist_found(hapd->conf->deny_mac, + hapd->conf->num_deny_mac, sta->addr, + &vlan_id) && + (!vlan_id.notempty || + !vlan_compare(&vlan_id, sta->vlan_desc))) + ap_sta_disconnect(hapd, sta, sta->addr, + WLAN_REASON_UNSPECIFIED); + } +} + static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd) { char *value; @@ -1319,19 +1367,27 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd) wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d", wps_corrupt_pkhash); #endif /* CONFIG_WPS_TESTING */ -#ifdef CONFIG_INTERWORKING - } else if (os_strcasecmp(cmd, "gas_frag_limit") == 0) { - int val = atoi(value); - if (val <= 0) - ret = -1; - else - hapd->gas_frag_limit = val; -#endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_TESTING_OPTIONS } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) { hapd->ext_mgmt_frame_handling = atoi(value); } else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) { hapd->ext_eapol_frame_io = atoi(value); +#ifdef CONFIG_DPP + } else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) { + os_free(hapd->dpp_config_obj_override); + hapd->dpp_config_obj_override = os_strdup(value); + } else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) { + os_free(hapd->dpp_discovery_override); + hapd->dpp_discovery_override = os_strdup(value); + } else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) { + os_free(hapd->dpp_groups_override); + hapd->dpp_groups_override = os_strdup(value); + } else if (os_strcasecmp(cmd, + "dpp_ignore_netaccesskey_mismatch") == 0) { + hapd->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_MBO } else if (os_strcasecmp(cmd, "mbo_assoc_disallow") == 0) { @@ -1352,39 +1408,26 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd) * disallowing station logic. */ #endif /* CONFIG_MBO */ +#ifdef CONFIG_DPP + } else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) { + os_free(hapd->dpp_configurator_params); + hapd->dpp_configurator_params = os_strdup(value); +#endif /* CONFIG_DPP */ } else { - struct sta_info *sta; - struct vlan_description vlan_id; - ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value); if (ret) return ret; if (os_strcasecmp(cmd, "deny_mac_file") == 0) { - for (sta = hapd->sta_list; sta; sta = sta->next) { - if (hostapd_maclist_found( - hapd->conf->deny_mac, - hapd->conf->num_deny_mac, sta->addr, - &vlan_id) && - (!vlan_id.notempty || - !vlan_compare(&vlan_id, sta->vlan_desc))) - ap_sta_disconnect( - hapd, sta, sta->addr, - WLAN_REASON_UNSPECIFIED); - } - } else if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED && - os_strcasecmp(cmd, "accept_mac_file") == 0) { - for (sta = hapd->sta_list; sta; sta = sta->next) { - if (!hostapd_maclist_found( - hapd->conf->accept_mac, - hapd->conf->num_accept_mac, - sta->addr, &vlan_id) || - (vlan_id.notempty && - vlan_compare(&vlan_id, sta->vlan_desc))) - ap_sta_disconnect( - hapd, sta, sta->addr, - WLAN_REASON_UNSPECIFIED); - } + hostapd_disassoc_deny_mac(hapd); + } else if (os_strcasecmp(cmd, "accept_mac_file") == 0) { + hostapd_disassoc_accept_mac(hapd); + } else if (os_strncmp(cmd, "wme_ac_", 7) == 0 || + os_strncmp(cmd, "wmm_ac_", 7) == 0) { + hapd->parameter_set_count++; + if (ieee802_11_update_beacons(hapd->iface)) + wpa_printf(MSG_DEBUG, + "Failed to update beacons with WMM parameters"); } } @@ -1534,6 +1577,137 @@ static int hostapd_ctrl_iface_mgmt_tx(struct hostapd_data *hapd, char *cmd) } +static int hostapd_ctrl_iface_mgmt_tx_status_process(struct hostapd_data *hapd, + char *cmd) +{ + char *pos, *param; + size_t len; + u8 *buf; + int stype = 0, ok = 0; + union wpa_event_data event; + + if (!hapd->ext_mgmt_frame_handling) + return -1; + + /* stype=<val> ok=<0/1> buf=<frame hexdump> */ + + wpa_printf(MSG_DEBUG, "External MGMT TX status process: %s", cmd); + + pos = cmd; + param = os_strstr(pos, "stype="); + if (param) { + param += 6; + stype = atoi(param); + } + + param = os_strstr(pos, " ok="); + if (param) { + param += 4; + ok = atoi(param); + } + + param = os_strstr(pos, " buf="); + if (!param) + return -1; + param += 5; + + len = os_strlen(param); + if (len & 1) + return -1; + len /= 2; + + buf = os_malloc(len); + if (!buf || hexstr2bin(param, buf, len) < 0) { + os_free(buf); + return -1; + } + + os_memset(&event, 0, sizeof(event)); + event.tx_status.type = WLAN_FC_TYPE_MGMT; + event.tx_status.data = buf; + event.tx_status.data_len = len; + event.tx_status.stype = stype; + event.tx_status.ack = ok; + hapd->ext_mgmt_frame_handling = 0; + wpa_supplicant_event(hapd, EVENT_TX_STATUS, &event); + hapd->ext_mgmt_frame_handling = 1; + + os_free(buf); + + return 0; +} + + +static int hostapd_ctrl_iface_mgmt_rx_process(struct hostapd_data *hapd, + char *cmd) +{ + char *pos, *param; + size_t len; + u8 *buf; + int freq = 0, datarate = 0, ssi_signal = 0; + union wpa_event_data event; + + if (!hapd->ext_mgmt_frame_handling) + return -1; + + /* freq=<MHz> datarate=<val> ssi_signal=<val> frame=<frame hexdump> */ + + wpa_printf(MSG_DEBUG, "External MGMT RX process: %s", cmd); + + pos = cmd; + param = os_strstr(pos, "freq="); + if (param) { + param += 5; + freq = atoi(param); + } + + param = os_strstr(pos, " datarate="); + if (param) { + param += 10; + datarate = atoi(param); + } + + param = os_strstr(pos, " ssi_signal="); + if (param) { + param += 12; + ssi_signal = atoi(param); + } + + param = os_strstr(pos, " frame="); + if (param == NULL) + return -1; + param += 7; + + len = os_strlen(param); + if (len & 1) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(param, buf, len) < 0) { + os_free(buf); + return -1; + } + + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.freq = freq; + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = len; + event.rx_mgmt.ssi_signal = ssi_signal; + event.rx_mgmt.datarate = datarate; + hapd->ext_mgmt_frame_handling = 0; + wpa_supplicant_event(hapd, EVENT_RX_MGMT, &event); + hapd->ext_mgmt_frame_handling = 1; + + os_free(buf); + + return 0; +} + + static int hostapd_ctrl_iface_eapol_rx(struct hostapd_data *hapd, char *cmd) { char *pos; @@ -1843,6 +2017,245 @@ static int hostapd_ctrl_get_fail(struct hostapd_data *hapd, #endif /* WPA_TRACE_BFD */ } + +static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd) +{ + struct sta_info *sta; + u8 addr[ETH_ALEN]; + u8 zero[WPA_TK_MAX_LEN]; + + os_memset(zero, 0, sizeof(zero)); + + if (hwaddr_aton(cmd, addr)) + return -1; + +#ifdef CONFIG_IEEE80211W + if (is_broadcast_ether_addr(addr) && os_strstr(cmd, "IGTK")) { + if (hapd->last_igtk_alg == WPA_ALG_NONE) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Reset IPN for IGTK"); + + /* First, use a zero key to avoid any possible duplicate key + * avoidance in the driver. */ + if (hostapd_drv_set_key(hapd->conf->iface, hapd, + hapd->last_igtk_alg, + broadcast_ether_addr, + hapd->last_igtk_key_idx, 1, NULL, 0, + zero, hapd->last_igtk_len) < 0) + return -1; + + /* Set the previously configured key to reset its TSC */ + return hostapd_drv_set_key(hapd->conf->iface, hapd, + hapd->last_igtk_alg, + broadcast_ether_addr, + hapd->last_igtk_key_idx, 1, NULL, 0, + hapd->last_igtk, + hapd->last_igtk_len); + } +#endif /* CONFIG_IEEE80211W */ + + if (is_broadcast_ether_addr(addr)) { + if (hapd->last_gtk_alg == WPA_ALG_NONE) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Reset PN for GTK"); + + /* First, use a zero key to avoid any possible duplicate key + * avoidance in the driver. */ + if (hostapd_drv_set_key(hapd->conf->iface, hapd, + hapd->last_gtk_alg, + broadcast_ether_addr, + hapd->last_gtk_key_idx, 1, NULL, 0, + zero, hapd->last_gtk_len) < 0) + return -1; + + /* Set the previously configured key to reset its TSC */ + return hostapd_drv_set_key(hapd->conf->iface, hapd, + hapd->last_gtk_alg, + broadcast_ether_addr, + hapd->last_gtk_key_idx, 1, NULL, 0, + hapd->last_gtk, hapd->last_gtk_len); + } + + sta = ap_get_sta(hapd, addr); + if (!sta) + return -1; + + if (sta->last_tk_alg == WPA_ALG_NONE) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Reset PN for " MACSTR, + MAC2STR(sta->addr)); + + /* First, use a zero key to avoid any possible duplicate key avoidance + * in the driver. */ + if (hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg, + sta->addr, sta->last_tk_key_idx, 1, NULL, 0, + zero, sta->last_tk_len) < 0) + return -1; + + /* Set the previously configured key to reset its TSC/RSC */ + return hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg, + sta->addr, sta->last_tk_key_idx, 1, NULL, 0, + sta->last_tk, sta->last_tk_len); +} + + +static int hostapd_ctrl_set_key(struct hostapd_data *hapd, const char *cmd) +{ + u8 addr[ETH_ALEN]; + const char *pos = cmd; + enum wpa_alg alg; + int idx, set_tx; + u8 seq[6], key[WPA_TK_MAX_LEN]; + size_t key_len; + + /* parameters: alg addr idx set_tx seq key */ + + alg = atoi(pos); + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + if (hwaddr_aton(pos, addr)) + return -1; + pos += 17; + if (*pos != ' ') + return -1; + pos++; + idx = atoi(pos); + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + set_tx = atoi(pos); + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + if (hexstr2bin(pos, seq, sizeof(seq)) < 0) + return -1; + pos += 2 * 6; + if (*pos != ' ') + return -1; + pos++; + key_len = os_strlen(pos) / 2; + if (hexstr2bin(pos, key, key_len) < 0) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Set key"); + return hostapd_drv_set_key(hapd->conf->iface, hapd, alg, addr, idx, + set_tx, seq, 6, key, key_len); +} + + +static void restore_tk(void *ctx1, void *ctx2) +{ + struct hostapd_data *hapd = ctx1; + struct sta_info *sta = ctx2; + + wpa_printf(MSG_INFO, "TESTING: Restore TK for " MACSTR, + MAC2STR(sta->addr)); + /* This does not really restore the TSC properly, so this will result + * in replay protection issues for now since there is no clean way of + * preventing encryption of a single EAPOL frame. */ + hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg, + sta->addr, sta->last_tk_key_idx, 1, NULL, 0, + sta->last_tk, sta->last_tk_len); +} + + +static int hostapd_ctrl_resend_m1(struct hostapd_data *hapd, const char *cmd) +{ + struct sta_info *sta; + u8 addr[ETH_ALEN]; + int plain = os_strstr(cmd, "plaintext") != NULL; + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->wpa_sm) + return -1; + + if (plain && sta->last_tk_alg == WPA_ALG_NONE) + plain = 0; /* no need for special processing */ + if (plain) { + wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR, + MAC2STR(sta->addr)); + hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE, + sta->addr, sta->last_tk_key_idx, 0, NULL, 0, + NULL, 0); + } + + wpa_printf(MSG_INFO, "TESTING: Send M1 to " MACSTR, MAC2STR(sta->addr)); + return wpa_auth_resend_m1(sta->wpa_sm, + os_strstr(cmd, "change-anonce") != NULL, + plain ? restore_tk : NULL, hapd, sta); +} + + +static int hostapd_ctrl_resend_m3(struct hostapd_data *hapd, const char *cmd) +{ + struct sta_info *sta; + u8 addr[ETH_ALEN]; + int plain = os_strstr(cmd, "plaintext") != NULL; + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->wpa_sm) + return -1; + + if (plain && sta->last_tk_alg == WPA_ALG_NONE) + plain = 0; /* no need for special processing */ + if (plain) { + wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR, + MAC2STR(sta->addr)); + hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE, + sta->addr, sta->last_tk_key_idx, 0, NULL, 0, + NULL, 0); + } + + wpa_printf(MSG_INFO, "TESTING: Send M3 to " MACSTR, MAC2STR(sta->addr)); + return wpa_auth_resend_m3(sta->wpa_sm, + plain ? restore_tk : NULL, hapd, sta); +} + + +static int hostapd_ctrl_resend_group_m1(struct hostapd_data *hapd, + const char *cmd) +{ + struct sta_info *sta; + u8 addr[ETH_ALEN]; + int plain = os_strstr(cmd, "plaintext") != NULL; + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->wpa_sm) + return -1; + + if (plain && sta->last_tk_alg == WPA_ALG_NONE) + plain = 0; /* no need for special processing */ + if (plain) { + wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR, + MAC2STR(sta->addr)); + hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE, + sta->addr, sta->last_tk_key_idx, 0, NULL, 0, + NULL, 0); + } + + wpa_printf(MSG_INFO, + "TESTING: Send group M1 for the same GTK and zero RSC to " + MACSTR, MAC2STR(sta->addr)); + return wpa_auth_resend_group_m1(sta->wpa_sm, + plain ? restore_tk : NULL, hapd, sta); +} + #endif /* CONFIG_TESTING_OPTIONS */ @@ -1859,6 +2272,11 @@ static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface, return ret; for (i = 0; i < iface->num_bss; i++) { + + /* Save CHAN_SWITCH VHT config */ + hostapd_chan_switch_vht_config( + iface->bss[i], settings.freq_params.vht_enabled); + ret = hostapd_switch_channel(iface->bss[i], &settings); if (ret) { /* FIX: What do we do if CSA fails in the middle of @@ -2055,8 +2473,9 @@ static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd, int ret; os_reltime_sub(&now, &info->last_seen, &age); - ret = os_snprintf(pos, end - pos, MACSTR " %u\n", - MAC2STR(info->addr), (unsigned int) age.sec); + ret = os_snprintf(pos, end - pos, MACSTR " %u %d\n", + MAC2STR(info->addr), (unsigned int) age.sec, + info->ssi_signal); if (os_snprintf_error(end - pos, ret)) break; pos += ret; @@ -2140,11 +2559,52 @@ static int hostapd_ctrl_iface_req_range(struct hostapd_data *hapd, char *cmd) } +static int hostapd_ctrl_iface_req_beacon(struct hostapd_data *hapd, + const char *cmd, char *reply, + size_t reply_size) +{ + u8 addr[ETH_ALEN]; + const char *pos; + struct wpabuf *req; + int ret; + u8 req_mode = 0; + + if (hwaddr_aton(cmd, addr)) + return -1; + pos = os_strchr(cmd, ' '); + if (!pos) + return -1; + pos++; + if (os_strncmp(pos, "req_mode=", 9) == 0) { + int val = hex2byte(pos + 9); + + if (val < 0) + return -1; + req_mode = val; + pos += 11; + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + } + req = wpabuf_parse_bin(pos); + if (!req) + return -1; + + ret = hostapd_send_beacon_req(hapd, addr, req_mode, req); + wpabuf_free(req); + if (ret >= 0) + ret = os_snprintf(reply, reply_size, "%d", ret); + return ret; +} + + static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf) { struct wpa_ssid_value ssid; u8 bssid[ETH_ALEN]; struct wpabuf *nr, *lci = NULL, *civic = NULL; + int stationary = 0; char *tmp; int ret; @@ -2223,8 +2683,15 @@ static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf) } } + if (!buf) + goto set; + + if (os_strstr(buf, "stat")) + stationary = 1; + set: - ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic); + ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic, + stationary); wpabuf_free(nr); wpabuf_free(lci); @@ -2285,6 +2752,80 @@ static int hostapd_ctrl_driver_flags(struct hostapd_iface *iface, char *buf, } +static int hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num, + const char *txtaddr) +{ + u8 addr[ETH_ALEN]; + struct vlan_description vlan_id; + + if (!(*num)) + return 0; + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + if (hostapd_maclist_found(*acl, *num, addr, &vlan_id)) + hostapd_remove_acl_mac(acl, num, addr); + + return 0; +} + + +static void hostapd_ctrl_iface_acl_clear_list(struct mac_acl_entry **acl, + int *num) +{ + while (*num) + hostapd_remove_acl_mac(acl, num, (*acl)[0].addr); +} + + +static int hostapd_ctrl_iface_acl_show_mac(struct mac_acl_entry *acl, int num, + char *buf, size_t buflen) +{ + int i = 0, len = 0, ret = 0; + + if (!acl) + return 0; + + while (i < num) { + ret = os_snprintf(buf + len, buflen - len, + MACSTR " VLAN_ID=%d\n", + MAC2STR(acl[i].addr), + acl[i].vlan_id.untagged); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + i++; + len += ret; + } + return len; +} + + +static int hostapd_ctrl_iface_acl_add_mac(struct mac_acl_entry **acl, int *num, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + struct vlan_description vlan_id; + int ret = 0, vlanid = 0; + const char *pos; + + if (hwaddr_aton(cmd, addr)) + return -1; + + pos = os_strstr(cmd, "VLAN_ID="); + if (pos) + vlanid = atoi(pos + 8); + + if (!hostapd_maclist_found(*acl, *num, addr, &vlan_id)) { + ret = hostapd_add_acl_maclist(acl, num, vlanid, addr); + if (ret != -1 && *acl) + qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp); + } + + return ret < 0 ? -1 : 0; +} + + static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, char *buf, char *reply, int reply_size, @@ -2302,6 +2843,8 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "RELOG", 5) == 0) { if (wpa_debug_reopen_file() < 0) reply_len = -1; + } else if (os_strncmp(buf, "NOTE ", 5) == 0) { + wpa_printf(MSG_INFO, "NOTE: %s", buf + 5); } else if (os_strcmp(buf, "STATUS") == 0) { reply_len = hostapd_ctrl_iface_status(hapd, reply, reply_size); @@ -2349,7 +2892,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply, reply_size); } else if (os_strcmp(buf, "ATTACH") == 0) { - if (hostapd_ctrl_iface_attach(hapd, from, fromlen)) + if (hostapd_ctrl_iface_attach(hapd, from, fromlen, NULL)) + reply_len = -1; + } else if (os_strncmp(buf, "ATTACH ", 7) == 0) { + if (hostapd_ctrl_iface_attach(hapd, from, fromlen, buf + 7)) reply_len = -1; } else if (os_strcmp(buf, "DETACH") == 0) { if (hostapd_ctrl_iface_detach(hapd, from, fromlen)) @@ -2441,7 +2987,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16)) reply_len = -1; #endif /* CONFIG_HS20 */ -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP } else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) { if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18)) reply_len = -1; @@ -2451,7 +2997,10 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) { if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11)) reply_len = -1; -#endif /* CONFIG_WNM */ + } else if (os_strncmp(buf, "COLOC_INTF_REQ ", 15) == 0) { + if (hostapd_ctrl_iface_coloc_intf_req(hapd, buf + 15)) + reply_len = -1; +#endif /* CONFIG_WNM_AP */ } else if (os_strcmp(buf, "GET_CONFIG") == 0) { reply_len = hostapd_ctrl_iface_get_config(hapd, reply, reply_size); @@ -2480,6 +3029,13 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) { if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8)) reply_len = -1; + } else if (os_strncmp(buf, "MGMT_TX_STATUS_PROCESS ", 23) == 0) { + if (hostapd_ctrl_iface_mgmt_tx_status_process(hapd, + buf + 23) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "MGMT_RX_PROCESS ", 16) == 0) { + if (hostapd_ctrl_iface_mgmt_rx_process(hapd, buf + 16) < 0) + reply_len = -1; } else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) { if (hostapd_ctrl_iface_eapol_rx(hapd, buf + 9) < 0) reply_len = -1; @@ -2503,6 +3059,24 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, reply_len = -1; } else if (os_strcmp(buf, "GET_FAIL") == 0) { reply_len = hostapd_ctrl_get_fail(hapd, reply, reply_size); + } else if (os_strncmp(buf, "RESET_PN ", 9) == 0) { + if (hostapd_ctrl_reset_pn(hapd, buf + 9) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "SET_KEY ", 8) == 0) { + if (hostapd_ctrl_set_key(hapd, buf + 8) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "RESEND_M1 ", 10) == 0) { + if (hostapd_ctrl_resend_m1(hapd, buf + 10) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "RESEND_M3 ", 10) == 0) { + if (hostapd_ctrl_resend_m3(hapd, buf + 10) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "RESEND_GROUP_M1 ", 16) == 0) { + if (hostapd_ctrl_resend_group_m1(hapd, buf + 16) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "REKEY_GTK") == 0) { + if (wpa_auth_rekey_gtk(hapd->wpa_auth) < 0) + reply_len = -1; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) { if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12)) @@ -2534,6 +3108,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, reply_size); } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) { hostapd_ctrl_iface_pmksa_flush(hapd); + } else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) { + if (hostapd_ctrl_iface_pmksa_add(hapd, buf + 10) < 0) + reply_len = -1; } else if (os_strncmp(buf, "SET_NEIGHBOR ", 13) == 0) { if (hostapd_ctrl_iface_set_neighbor(hapd, buf + 13)) reply_len = -1; @@ -2546,9 +3123,136 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "REQ_RANGE ", 10) == 0) { if (hostapd_ctrl_iface_req_range(hapd, buf + 10)) reply_len = -1; + } else if (os_strncmp(buf, "REQ_BEACON ", 11) == 0) { + reply_len = hostapd_ctrl_iface_req_beacon(hapd, buf + 11, + reply, reply_size); } else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) { reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply, reply_size); + } else if (os_strcmp(buf, "TERMINATE") == 0) { + eloop_terminate(); + } else if (os_strncmp(buf, "ACCEPT_ACL ", 11) == 0) { + if (os_strncmp(buf + 11, "ADD_MAC ", 8) == 0) { + if (!hostapd_ctrl_iface_acl_add_mac( + &hapd->conf->accept_mac, + &hapd->conf->num_accept_mac, buf + 19)) + hostapd_disassoc_accept_mac(hapd); + else + reply_len = -1; + } else if (os_strncmp((buf + 11), "DEL_MAC ", 8) == 0) { + hostapd_ctrl_iface_acl_del_mac( + &hapd->conf->accept_mac, + &hapd->conf->num_accept_mac, buf + 19); + } else if (os_strcmp(buf + 11, "SHOW") == 0) { + reply_len = hostapd_ctrl_iface_acl_show_mac( + hapd->conf->accept_mac, + hapd->conf->num_accept_mac, reply, reply_size); + } else if (os_strcmp(buf + 11, "CLEAR") == 0) { + hostapd_ctrl_iface_acl_clear_list( + &hapd->conf->accept_mac, + &hapd->conf->num_accept_mac); + } + } else if (os_strncmp(buf, "DENY_ACL ", 9) == 0) { + if (os_strncmp(buf + 9, "ADD_MAC ", 8) == 0) { + if (!hostapd_ctrl_iface_acl_add_mac( + &hapd->conf->deny_mac, + &hapd->conf->num_deny_mac, buf + 17)) + hostapd_disassoc_deny_mac(hapd); + } else if (os_strncmp(buf + 9, "DEL_MAC ", 8) == 0) { + hostapd_ctrl_iface_acl_del_mac( + &hapd->conf->deny_mac, + &hapd->conf->num_deny_mac, buf + 17); + } else if (os_strcmp(buf + 9, "SHOW") == 0) { + reply_len = hostapd_ctrl_iface_acl_show_mac( + hapd->conf->deny_mac, + hapd->conf->num_deny_mac, reply, reply_size); + } else if (os_strcmp(buf + 9, "CLEAR") == 0) { + hostapd_ctrl_iface_acl_clear_list( + &hapd->conf->deny_mac, + &hapd->conf->num_deny_mac); + } +#ifdef CONFIG_DPP + } else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) { + res = hostapd_dpp_qr_code(hapd, 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) { + res = hostapd_dpp_bootstrap_gen(hapd, 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 (hostapd_dpp_bootstrap_remove(hapd, buf + 21) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) { + const char *uri; + + uri = hostapd_dpp_bootstrap_get_uri(hapd, 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 = hostapd_dpp_bootstrap_info(hapd, atoi(buf + 19), + reply, reply_size); + } else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) { + if (hostapd_dpp_auth_init(hapd, buf + 13) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_LISTEN ", 11) == 0) { + if (hostapd_dpp_listen(hapd, buf + 11) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "DPP_STOP_LISTEN") == 0) { + hostapd_dpp_stop(hapd); + hostapd_dpp_listen_stop(hapd); + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) { + res = hostapd_dpp_configurator_add(hapd, 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 (hostapd_dpp_configurator_remove(hapd, buf + 24) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_SIGN ", 22) == 0) { + if (hostapd_dpp_configurator_sign(hapd, buf + 22) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_GET_KEY ", 25) == 0) { + reply_len = hostapd_dpp_configurator_get_key(hapd, + atoi(buf + 25), + reply, reply_size); + } else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) { + res = hostapd_dpp_pkex_add(hapd, 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 (hostapd_dpp_pkex_remove(hapd, buf + 16) < 0) + reply_len = -1; +#endif /* CONFIG_DPP */ +#ifdef RADIUS_SERVER + } else if (os_strncmp(buf, "DAC_REQUEST ", 12) == 0) { + if (radius_server_dac_request(hapd->radius_srv, buf + 12) < 0) + reply_len = -1; +#endif /* RADIUS_SERVER */ } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -2999,9 +3703,10 @@ static int hostapd_ctrl_iface_remove(struct hapd_interfaces *interfaces, static int hostapd_global_ctrl_iface_attach(struct hapd_interfaces *interfaces, struct sockaddr_storage *from, - socklen_t fromlen) + socklen_t fromlen, char *input) { - return ctrl_iface_attach(&interfaces->global_ctrl_dst, from, fromlen); + return ctrl_iface_attach(&interfaces->global_ctrl_dst, from, fromlen, + input); } @@ -3020,6 +3725,16 @@ static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces) wps_testing_dummy_cred = 0; wps_corrupt_pkhash = 0; #endif /* CONFIG_WPS_TESTING */ + +#ifdef CONFIG_TESTING_OPTIONS +#ifdef CONFIG_DPP + dpp_test = DPP_TEST_DISABLED; +#endif /* CONFIG_DPP */ +#endif /* CONFIG_TESTING_OPTIONS */ + +#ifdef CONFIG_DPP + hostapd_dpp_deinit_global(interfaces); +#endif /* CONFIG_DPP */ } @@ -3371,7 +4086,11 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, reply_len = -1; } else if (os_strcmp(buf, "ATTACH") == 0) { if (hostapd_global_ctrl_iface_attach(interfaces, &from, - fromlen)) + fromlen, NULL)) + reply_len = -1; + } else if (os_strncmp(buf, "ATTACH ", 7) == 0) { + if (hostapd_global_ctrl_iface_attach(interfaces, &from, + fromlen, buf + 7)) reply_len = -1; } else if (os_strcmp(buf, "DETACH") == 0) { if (hostapd_global_ctrl_iface_detach(interfaces, &from, @@ -3478,8 +4197,6 @@ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) } } - dl_list_init(&interface->global_ctrl_dst); - interface->global_ctrl_sock = -1; os_get_random(gcookie, COOKIE_LEN); #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE @@ -3689,6 +4406,18 @@ void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces) } +static int hostapd_ctrl_check_event_enabled(struct wpa_ctrl_dst *dst, + const char *buf) +{ + /* Enable Probe Request events based on explicit request. + * Other events are enabled by default. + */ + if (str_starts(buf, RX_PROBE_REQUEST)) + return !!(dst->events & WPA_EVENT_RX_PROBE_REQUEST); + return 1; +} + + static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, enum wpa_msg_type type, const char *buf, size_t len) @@ -3723,7 +4452,8 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, idx = 0; dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) { - if (level >= dst->debug_level) { + if ((level >= dst->debug_level) && + hostapd_ctrl_check_event_enabled(dst, buf)) { sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor send", &dst->addr, dst->addrlen); msg.msg_name = &dst->addr; diff --git a/hostapd/defconfig b/hostapd/defconfig index 4659dd1e6bcbd..77a894d5e11a3 100644 --- a/hostapd/defconfig +++ b/hostapd/defconfig @@ -31,7 +31,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) @@ -50,9 +50,6 @@ CONFIG_IAPP=y # WPA2/IEEE 802.11i RSN pre-authentication CONFIG_RSN_PREAUTH=y -# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) -CONFIG_PEERKEY=y - # IEEE 802.11w (management frame protection) CONFIG_IEEE80211W=y @@ -157,6 +154,12 @@ CONFIG_IPV6=y # IEEE 802.11ac (Very High Throughput) support #CONFIG_IEEE80211AC=y +# IEEE 802.11ax HE support +# Note: This is experimental and work in progress. The definitions are still +# subject to change and this should not be expected to interoperate with the +# final IEEE 802.11ax version. +#CONFIG_IEEE80211AX=y + # Remove debugging code that is printing out debug messages to stdout. # This can be used to reduce the size of the hostapd considerably if debugging # code is not needed. @@ -166,6 +169,9 @@ CONFIG_IPV6=y # Disabled by default. #CONFIG_DEBUG_FILE=y +# Send debug messages to syslog instead of stdout +#CONFIG_DEBUG_SYSLOG=y + # Add support for sending all debug messages (regardless of debug verbosity) # to the Linux kernel tracing facility. This helps debug the entire stack by # making it easy to record everything happening from the driver up into the @@ -256,6 +262,7 @@ CONFIG_IPV6=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 @@ -268,6 +275,10 @@ CONFIG_IPV6=y # can be enabled to enable use of stronger crypto algorithms. #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 @@ -343,3 +354,22 @@ CONFIG_IPV6=y # a client, from which a signature can be produced which can identify the model # of client device like "Nexus 6P" or "iPhone 5s". #CONFIG_TAXONOMY=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 + +# Include internal line edit mode in hostapd_cli. This can be used to provide +# limited command line editing and history support. +#CONFIG_WPA_CLI_EDIT=y + +# Opportunistic Wireless Encryption (OWE) +# Experimental implementation of draft-harkins-owe-07.txt +#CONFIG_OWE=y + +# Override default value for the wpa_disable_eapol_key_retries configuration +# parameter. See that parameter in hostapd.conf for more details. +#CFLAGS += -DDEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES=1 diff --git a/hostapd/hlr_auc_gw.c b/hostapd/hlr_auc_gw.c index 2117d3423a1b6..5caa779dd648b 100644 --- a/hostapd/hlr_auc_gw.c +++ b/hostapd/hlr_auc_gw.c @@ -1,6 +1,6 @@ /* * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator - * Copyright (c) 2005-2007, 2012-2016, Jouni Malinen <j@w1.fi> + * Copyright (c) 2005-2007, 2012-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -973,7 +973,7 @@ static void usage(void) { printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA " "database/authenticator\n" - "Copyright (c) 2005-2016, Jouni Malinen <j@w1.fi>\n" + "Copyright (c) 2005-2017, Jouni Malinen <j@w1.fi>\n" "\n" "usage:\n" "hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] " diff --git a/hostapd/hostapd.android.rc b/hostapd/hostapd.android.rc index 83e8d8791e3cb..26a87b8089141 100644 --- a/hostapd/hostapd.android.rc +++ b/hostapd/hostapd.android.rc @@ -9,8 +9,7 @@ on post-fs-data mkdir /data/misc/wifi/hostapd 0770 wifi wifi -service hostapd /system/bin/hostapd \ - -e /data/misc/wifi/entropy.bin \ +service hostapd /vendor/bin/hostapd \ /data/misc/wifi/hostapd.conf class main user wifi diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index fa9a855a6e5f9..a005217116f1c 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -98,8 +98,25 @@ ssid=test # Country code (ISO/IEC 3166-1). Used to set regulatory domain. # Set as needed to indicate country in which device is operating. # This can limit available channels and transmit power. +# These two octets are used as the first two octets of the Country String +# (dot11CountryString) #country_code=US +# The third octet of the Country String (dot11CountryString) +# This parameter is used to set the third octet of the country string. +# +# All environments of the current frequency band and country (default) +#country3=0x20 +# Outdoor environment only +#country3=0x4f +# Indoor environment only +#country3=0x49 +# Noncountry entity (country_code=XX) +#country3=0x58 +# IEEE 802.11 standard Annex E table indication: 0x01 .. 0x1f +# Annex E, Table E-4 (Global operating classes) +#country3=0x04 + # Enable IEEE 802.11d. This advertises the country_code and the set of allowed # channels and transmit power levels based on the regulatory limits. The # country_code setting must be configured with the correct country for @@ -182,6 +199,11 @@ channel=1 #chanlist=100 104 108 112 116 #chanlist=1 6 11-13 +# Exclude DFS channels from ACS +# This option can be used to exclude all DFS channels from the ACS channel list +# in cases where the driver supports DFS channels. +#acs_exclude_dfs=1 + # Beacon interval in kus (1.024 ms) (default: 100; range 15..65535) beacon_int=100 @@ -227,6 +249,19 @@ fragm_threshold=-1 #basic_rates=10 20 55 110 #basic_rates=60 120 240 +# Beacon frame TX rate configuration +# This sets the TX rate that is used to transmit Beacon frames. If this item is +# not included, the driver default rate (likely lowest rate) is used. +# Legacy (CCK/OFDM rates): +# beacon_rate=<legacy rate in 100 kbps> +# HT: +# beacon_rate=ht:<HT MCS> +# VHT: +# beacon_rate=vht:<VHT MCS> +# +# For example, beacon_rate=10 for 1 Mbps or beacon_rate=60 for 6 Mbps (OFDM). +#beacon_rate=10 + # Short Preamble # This parameter can be used to enable optional use of short preamble for # frames sent at 2 Mbps, 5.5 Mbps, and 11 Mbps to improve network performance. @@ -294,7 +329,7 @@ ignore_broadcast_ssid=0 # TX queue parameters (EDCF / bursting) # tx_queue_<queue name>_<param> -# queues: data0, data1, data2, data3, after_beacon, beacon +# queues: data0, data1, data2, data3 # (data0 is the highest priority queue) # parameters: # aifs: AIFS (default 2) @@ -476,12 +511,38 @@ wmm_ac_vo_acm=0 # Beacon and Probe Response frames. #bss_load_update_period=50 +# Channel utilization averaging period (in BUs) +# This field is used to enable and configure channel utilization average +# calculation with bss_load_update_period. This should be in multiples of +# bss_load_update_period for more accurate calculation. +#chan_util_avg_period=600 + # Fixed BSS Load value for testing purposes # This field can be used to configure hostapd to add a fixed BSS Load element # into Beacon and Probe Response frames for testing purposes. The format is # <station count>:<channel utilization>:<available admission capacity> #bss_load_test=12:80:20000 +# Multicast to unicast conversion +# Request that the AP will do multicast-to-unicast conversion for ARP, IPv4, and +# IPv6 frames (possibly within 802.1Q). If enabled, such frames are to be sent +# to each station separately, with the DA replaced by their own MAC address +# rather than the group address. +# +# Note that this may break certain expectations of the receiver, such as the +# ability to drop unicast IP packets received within multicast L2 frames, or the +# ability to not send ICMP destination unreachable messages for packets received +# in L2 multicast (which is required, but the receiver can't tell the difference +# if this new option is enabled). +# +# This also doesn't implement the 802.11 DMS (directed multicast service). +# +#multicast_to_unicast=0 + +# Send broadcast Deauthentication frame on AP start/stop +# Default: 1 (enabled) +#broadcast_deauth=1 + ##### IEEE 802.11n related configuration ###################################### # ieee80211n: Whether IEEE 802.11n (HT) is enabled @@ -692,6 +753,47 @@ wmm_ac_vo_acm=0 # setting use_sta_nsts=1. #use_sta_nsts=0 +##### IEEE 802.11ax related configuration ##################################### + +#ieee80211ax: Whether IEEE 802.11ax (HE) is enabled +# 0 = disabled (default) +# 1 = enabled +#ieee80211ax=1 + +#he_su_beamformer: HE single user beamformer support +# 0 = not supported (default) +# 1 = supported +#he_su_beamformer=1 + +#he_su_beamformee: HE single user beamformee support +# 0 = not supported (default) +# 1 = supported +#he_su_beamformee=1 + +#he_mu_beamformer: HE multiple user beamformer support +# 0 = not supported (default) +# 1 = supported +#he_mu_beamformer=1 + +# he_bss_color: BSS color +# 0 = no BSS color (default) +# unsigned integer = BSS color +#he_bss_color=0 + +#he_default_pe_duration: The duration of PE field in an HE PPDU in us +# Possible values are 0 us (default), 4 us, 8 us, 12 us, and 16 us +#he_default_pe_duration=0 + +#he_twt_required: Whether TWT is required +# 0 = not required (default) +# 1 = required +#he_twt_required=0 + +#he_rts_threshold: Duration of STA transmission +# 0 = not set (default) +# unsigned integer = duration in units of 16 us +#he_rts_threshold=0 + ##### IEEE 802.1X-2004 related configuration ################################## # Require IEEE 802.1X authorization @@ -835,7 +937,8 @@ eap_server=0 # 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 hostapd is built to # use OpenSSL. @@ -1088,6 +1191,8 @@ own_ip_addr=127.0.0.1 #radius_das_port=3799 # # DAS client (the host that can send Disconnect/CoA requests) and shared secret +# Format: <IP address> <shared secret> +# IP address 0.0.0.0 can be used to allow requests from any address. #radius_das_client=192.168.1.123 shared secret here # # DAS Event-Timestamp time window in seconds @@ -1134,7 +1239,10 @@ own_ip_addr=127.0.0.1 # and/or WPA2 (full IEEE 802.11i/RSN): # bit0 = WPA # bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled) -#wpa=1 +# Note that WPA3 is also configured with bit1 since it uses RSN just like WPA2. +# In other words, for WPA3, wpa=2 is used the configuration (and +# wpa_key_mgmt=SAE for WPA3-Personal instead of wpa_key_mgmt=WPA-PSK). +#wpa=2 # WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit # secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase @@ -1163,31 +1271,73 @@ own_ip_addr=127.0.0.1 # Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The # entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be # added to enable SHA256-based stronger algorithms. +# WPA-PSK = WPA-Personal / WPA2-Personal +# WPA-PSK-SHA256 = WPA2-Personal using SHA256 +# WPA-EAP = WPA-Enterprise / WPA2-Enterprise +# WPA-EAP-SHA256 = WPA2-Enterprise using SHA256 +# SAE = SAE (WPA3-Personal) +# WPA-EAP-SUITE-B-192 = WPA3-Enterprise with 192-bit security/CNSA suite +# FT-PSK = FT with passphrase/PSK +# FT-EAP = FT with EAP +# FT-EAP-SHA384 = FT with EAP using SHA384 +# FT-SAE = FT with SAE +# 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 +# OSEN = Hotspot 2.0 online signup with encryption # (dot11RSNAConfigAuthenticationSuitesTable) #wpa_key_mgmt=WPA-PSK WPA-EAP # Set of accepted cipher suites (encryption algorithms) for pairwise keys # (unicast packets). This is a space separated list of algorithms: -# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0] -# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0] +# CCMP = AES in Counter mode with CBC-MAC (CCMP-128) +# TKIP = Temporal Key Integrity Protocol +# CCMP-256 = AES in Counter mode with CBC-MAC with 256-bit key +# GCMP = Galois/counter mode protocol (GCMP-128) +# GCMP-256 = Galois/counter mode protocol with 256-bit key # Group cipher suite (encryption algorithm for broadcast and multicast frames) # is automatically selected based on this configuration. If only CCMP is # allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise, -# TKIP will be used as the group cipher. +# TKIP will be used as the group cipher. The optional group_cipher parameter can +# be used to override this automatic selection. +# # (dot11RSNAConfigPairwiseCiphersTable) # Pairwise cipher for WPA (v1) (default: TKIP) #wpa_pairwise=TKIP CCMP # Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value) #rsn_pairwise=CCMP +# Optional override for automatic group cipher selection +# This can be used to select a specific group cipher regardless of which +# pairwise ciphers were enabled for WPA and RSN. It should be noted that +# overriding the group cipher with an unexpected value can result in +# interoperability issues and in general, this parameter is mainly used for +# testing purposes. +#group_cipher=CCMP + # Time interval for rekeying GTK (broadcast/multicast encryption keys) in # seconds. (dot11RSNAConfigGroupRekeyTime) -#wpa_group_rekey=600 +# This defaults to 86400 seconds (once per day) when using CCMP/GCMP as the +# group cipher and 600 seconds (once per 10 minutes) when using TKIP as the +# group cipher. +#wpa_group_rekey=86400 # Rekey GTK when any STA that possesses the current GTK is leaving the BSS. # (dot11RSNAConfigGroupRekeyStrict) #wpa_strict_rekey=1 +# The number of times EAPOL-Key Message 1/2 in the RSN Group Key Handshake is +#retried per GTK Handshake attempt. (dot11RSNAConfigGroupUpdateCount) +# This value should only be increased when stations are constantly +# deauthenticated during GTK rekeying with the log message +# "group key handshake failed...". +# You should consider to also increase wpa_pairwise_update_count then. +# Range 1..4294967295; default: 4 +#wpa_group_update_count=4 + # Time interval for rekeying GMK (master key used internally to generate GTKs # (in seconds). #wpa_gmk_rekey=86400 @@ -1196,6 +1346,36 @@ own_ip_addr=127.0.0.1 # PTK to mitigate some attacks against TKIP deficiencies. #wpa_ptk_rekey=600 +# The number of times EAPOL-Key Message 1/4 and Message 3/4 in the RSN 4-Way +# Handshake are retried per 4-Way Handshake attempt. +# (dot11RSNAConfigPairwiseUpdateCount) +# Range 1..4294967295; default: 4 +#wpa_pairwise_update_count=4 + +# Workaround for key reinstallation attacks +# +# This parameter can be used to disable retransmission of EAPOL-Key frames that +# are used to install keys (EAPOL-Key message 3/4 and group message 1/2). This +# is similar to setting wpa_group_update_count=1 and +# wpa_pairwise_update_count=1, but with no impact to message 1/4 and with +# extended timeout on the response to avoid causing issues with stations that +# may use aggressive power saving have very long time in replying to the +# EAPOL-Key messages. +# +# This option can be used to work around key reinstallation attacks on the +# station (supplicant) side in cases those station devices cannot be updated +# for some reason. By removing the retransmissions the attacker cannot cause +# key reinstallation with a delayed frame transmission. This is related to the +# station side vulnerabilities CVE-2017-13077, CVE-2017-13078, CVE-2017-13079, +# CVE-2017-13080, and CVE-2017-13081. +# +# This workaround might cause interoperability issues and reduced robustness of +# key negotiation especially in environments with heavy traffic load due to the +# number of attempts to perform the key exchange is reduced significantly. As +# such, this workaround is disabled by default (unless overridden in build +# configuration). To enable this, set the parameter to 1. +#wpa_disable_eapol_key_retries=1 + # Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up # roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN # authentication and key handshake before actually associating with a new AP. @@ -1211,12 +1391,6 @@ own_ip_addr=127.0.0.1 # one. #rsn_preauth_interfaces=eth0 -# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e) is -# allowed. This is only used with RSN/WPA2. -# 0 = disabled (default) -# 1 = enabled -#peerkey=1 - # ieee80211w: Whether management frame protection (MFP) is enabled # 0 = disabled (default) # 1 = optional @@ -1259,11 +1433,44 @@ own_ip_addr=127.0.0.1 # 1 = enabled #okc=1 +# SAE password +# This parameter can be used to set passwords for SAE. By default, the +# wpa_passphrase value is used if this separate parameter is not used, but +# wpa_passphrase follows the WPA-PSK constraints (8..63 characters) even though +# SAE passwords do not have such constraints. If the BSS enabled both SAE and +# WPA-PSK and both values are set, SAE uses the sae_password values and WPA-PSK +# uses the wpa_passphrase value. +# +# Each sae_password entry is added to a list of available passwords. This +# corresponds to the dot11RSNAConfigPasswordValueEntry. sae_password value +# starts with the password (dot11RSNAConfigPasswordCredential). That value can +# be followed by optional peer MAC address (dot11RSNAConfigPasswordPeerMac) and +# by optional password identifier (dot11RSNAConfigPasswordIdentifier). If the +# peer MAC address is not included or is set to the wildcard address +# (ff:ff:ff:ff:ff:ff), the entry is available for any station to use. If a +# specific peer MAC address is included, only a station with that MAC address +# is allowed to use the entry. If the password identifier (with non-zero length) +# is included, the entry is limited to be used only with that specified +# identifier. The last matching (based on peer MAC address and identifier) entry +# is used to select which password to use. Setting sae_password to an empty +# string has a special meaning of removing all previously added entries. +# sae_password uses the following encoding: +#<password/credential>[|mac=<peer mac>][|id=<identifier>] +# Examples: +#sae_password=secret +#sae_password=really secret|mac=ff:ff:ff:ff:ff:ff +#sae_password=example secret|mac=02:03:04:05:06:07|id=pw identifier + # SAE threshold for anti-clogging mechanism (dot11RSNASAEAntiCloggingThreshold) # This parameter defines how many open SAE instances can be in progress at the # same time before the anti-clogging mechanism is taken into use. #sae_anti_clogging_threshold=5 +# Maximum number of SAE synchronization errors (dot11RSNASAESync) +# The offending SAe peer will be disconnected if more than this many +# synchronization errors happen. +#sae_sync=5 + # Enabled SAE finite cyclic groups # SAE implementation are required to support group 19 (ECC group defined over a # 256-bit prime order field). All groups that are supported by the @@ -1273,6 +1480,75 @@ own_ip_addr=127.0.0.1 # http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9 #sae_groups=19 20 21 25 26 +# Require MFP for all associations using SAE +# This parameter can be used to enforce negotiation of MFP for all associations +# that negotiate use of SAE. This is used in cases where SAE-capable devices are +# known to be MFP-capable and the BSS is configured with optional MFP +# (ieee80211w=1) for legacy support. The non-SAE stations can connect without +# MFP while SAE stations are required to negotiate MFP if sae_require_mfp=1. +#sae_require_mfp=0 + +# FILS Cache Identifier (16-bit value in hexdump format) +#fils_cache_id=0011 + +# FILS Realm Information +# One or more FILS realms need to be configured when FILS is enabled. This list +# of realms is used to define which realms (used in keyName-NAI by the client) +# can be used with FILS shared key authentication for ERP. +#fils_realm=example.com +#fils_realm=example.org + +# FILS DH Group for PFS +# 0 = PFS disabled with FILS shared key authentication (default) +# 1-65535 DH Group to use for FILS PFS +#fils_dh_group=0 + +# OWE DH groups +# OWE implementations are required to support group 19 (NIST P-256). All groups +# that are supported by the implementation (e.g., groups 19, 20, and 21 when +# using OpenSSL) are enabled by default. This configuration parameter can be +# used to specify a limited set of allowed groups. The group values are listed +# in the IANA registry: +# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-10 +#owe_groups=19 20 21 + +# OWE transition mode configuration +# Pointer to the matching open/OWE BSS +#owe_transition_bssid=<bssid> +# SSID in same format as ssid2 described above. +#owe_transition_ssid=<SSID> +# Alternatively, OWE transition mode BSSID/SSID can be configured with a +# reference to a BSS operated by this hostapd process. +#owe_transition_ifname=<ifname> + +# DHCP server for FILS HLP +# If configured, hostapd will act as a DHCP relay for all FILS HLP requests +# that include a DHCPDISCOVER message and send them to the specific DHCP +# server for processing. hostapd will then wait for a response from that server +# before replying with (Re)Association Response frame that encapsulates this +# DHCP response. own_ip_addr is used as the local address for the communication +# with the DHCP server. +#dhcp_server=127.0.0.1 + +# DHCP server UDP port +# Default: 67 +#dhcp_server_port=67 + +# DHCP relay UDP port on the local device +# Default: 67; 0 means not to bind any specific port +#dhcp_relay_port=67 + +# DHCP rapid commit proxy +# If set to 1, this enables hostapd to act as a DHCP rapid commit proxy to +# allow the rapid commit options (two message DHCP exchange) to be used with a +# server that supports only the four message DHCP exchange. This is disabled by +# default (= 0) and can be enabled by setting this to 1. +#dhcp_rapid_commit_proxy=0 + +# Wait time for FILS HLP (dot11HLPWaitTime) in TUs +# default: 30 TUs (= 30.72 milliseconds) +#fils_hlp_wait_time=30 + ##### IEEE 802.11r configuration ############################################## # Mobility Domain identifier (dot11FTMobilityDomainID, MDID) @@ -1285,9 +1561,16 @@ own_ip_addr=127.0.0.1 # 1 to 48 octet identifier. # This is configured with nas_identifier (see RADIUS client section above). -# Default lifetime of the PMK-RO in minutes; range 1..65535 +# Default lifetime of the PMK-R0 in seconds; range 60..4294967295 +# (default: 14 days / 1209600 seconds; 0 = disable timeout) # (dot11FTR0KeyLifetime) -#r0_key_lifetime=10000 +#ft_r0_key_lifetime=1209600 + +# Maximum lifetime for PMK-R1; applied only if not zero +# PMK-R1 is removed at latest after this limit. +# Removing any PMK-R1 for expiry can be disabled by setting this to -1. +# (default: 0) +#r1_max_key_lifetime=0 # PMK-R1 Key Holder identifier (dot11FTR1KeyHolderID) # 6-octet identifier as a hex string. @@ -1299,22 +1582,52 @@ own_ip_addr=127.0.0.1 #reassociation_deadline=1000 # List of R0KHs in the same Mobility Domain -# format: <MAC address> <NAS Identifier> <128-bit key as hex string> +# format: <MAC address> <NAS Identifier> <256-bit key as hex string> # This list is used to map R0KH-ID (NAS Identifier) to a destination MAC # address when requesting PMK-R1 key from the R0KH that the STA used during the # Initial Mobility Domain Association. -#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f -#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff +#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f +#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff # And so on.. One line per R0KH. +# Wildcard entry: +# Upon receiving a response from R0KH, it will be added to this list, so +# subsequent requests won't be broadcast. If R0KH does not reply, it will be +# blacklisted. +#r0kh=ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff # List of R1KHs in the same Mobility Domain -# format: <MAC address> <R1KH-ID> <128-bit key as hex string> +# format: <MAC address> <R1KH-ID> <256-bit key as hex string> # This list is used to map R1KH-ID to a destination MAC address when sending # PMK-R1 key from the R0KH. This is also the list of authorized R1KHs in the MD # that can request PMK-R1 keys. -#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f -#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff +#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f +#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff # And so on.. One line per R1KH. +# Wildcard entry: +# Upon receiving a request from an R1KH not yet known, it will be added to this +# list and thus will receive push notifications. +#r1kh=00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff + +# Timeout (seconds) for newly discovered R0KH/R1KH (see wildcard entries above) +# Special values: 0 -> do not expire +# Warning: do not cache implies no sequence number validation with wildcards +#rkh_pos_timeout=86400 (default = 1 day) + +# Timeout (milliseconds) for requesting PMK-R1 from R0KH using PULL request +# and number of retries. +#rkh_pull_timeout=1000 (default = 1 second) +#rkh_pull_retries=4 (default) + +# Timeout (seconds) for non replying R0KH (see wildcard entries above) +# Special values: 0 -> do not cache +# default: 60 seconds +#rkh_neg_timeout=60 + +# Note: The R0KH/R1KH keys used to be 128-bit in length before the message +# format was changed. That shorter key length is still supported for backwards +# compatibility of the configuration files. If such a shorter key is used, a +# 256-bit key is derived from it. For new deployments, configuring the 256-bit +# key is recommended. # Whether PMK-R1 push is enabled at R0KH # 0 = do not push PMK-R1 to all configured R1KHs (default) @@ -1326,6 +1639,14 @@ own_ip_addr=127.0.0.1 # 1 = FT-over-DS enabled (default) #ft_over_ds=1 +# Whether to generate FT response locally for PSK networks +# This avoids use of PMK-R1 push/pull from other APs with FT-PSK networks as +# the required information (PSK and other session data) is already locally +# available. +# 0 = disabled (default) +# 1 = enabled +#ft_psk_generate_local=0 + ##### Neighbor table ########################################################## # Maximum number of entries kept in AP table (either for neigbor table or for # detecting Overlapping Legacy BSS Condition). The oldest entry will be @@ -1596,6 +1917,18 @@ own_ip_addr=127.0.0.1 # 1 = enabled (allow stations to use WNM-Sleep Mode) #wnm_sleep_mode=1 +# WNM-Sleep Mode GTK/IGTK workaround +# Normally, WNM-Sleep Mode exit with management frame protection negotiated +# would result in the current GTK/IGTK getting added into the WNM-Sleep Mode +# Response frame. Some station implementations may have a vulnerability that +# results in GTK/IGTK reinstallation based on this frame being replayed. This +# configuration parameter can be used to disable that behavior and use EAPOL-Key +# frames for GTK/IGTK update instead. This would likely be only used with +# wpa_disable_eapol_key_retries=1 that enables a workaround for similar issues +# with EAPOL-Key. This is related to station side vulnerabilities CVE-2017-13087 +# and CVE-2017-13088. To enable this AP-side workaround, set the parameter to 1. +#wnm_sleep_mode_no_keys=0 + # BSS Transition Management # 0 = disabled (default) # 1 = enabled @@ -1683,6 +2016,15 @@ own_ip_addr=127.0.0.1 # (double quoted string, printf-escaped string) #venue_name=P"eng:Example\nvenue" +# Venue URL information +# This parameter can be used to configure one or more Venue URL Duples to +# provide additional information corresponding to Venue Name information. +# Each entry has a Venue Number value separated by colon from the Venue URL +# string. Venue Number indicates the corresponding venue_name entry (1 = 1st +# venue_name, 2 = 2nd venue_name, and so on; 0 = no matching venue_name) +#venue_url=1:http://www.example.com/info-eng +#venue_url=2:http://www.example.com/info-fin + # Network Authentication Type # This parameter indicates what type of network authentication is used in the # network. @@ -1853,7 +2195,27 @@ own_ip_addr=127.0.0.1 # channels 36-48): #hs20_operating_class=5173 -# OSU icons +# Terms and Conditions information +# +# hs20_t_c_filename contains the Terms and Conditions filename that the AP +# indicates in RADIUS Access-Request messages. +#hs20_t_c_filename=terms-and-conditions +# +# hs20_t_c_timestamp contains the Terms and Conditions timestamp that the AP +# indicates in RADIUS Access-Request messages. Usually, this contains the number +# of seconds since January 1, 1970 00:00 UTC showing the time when the file was +# last modified. +#hs20_t_c_timestamp=1234567 +# +# hs20_t_c_server_url contains a template for the Terms and Conditions server +# URL. This template is used to generate the URL for a STA that needs to +# acknowledge Terms and Conditions. Unlike the other hs20_t_c_* parameters, this +# parameter is used on the authentication server, not the AP. +# Macros: +# @1@ = MAC address of the STA (colon separated hex octets) +#hs20_t_c_server_url=https://example.com/t_and_c?addr=@1@&ap=123 + +# OSU and Operator icons # <Icon Width>:<Icon Height>:<Language code>:<Icon Type>:<Name>:<file path> #hs20_icon=32:32:eng:image/png:icon32:/tmp/icon32.png #hs20_icon=64:64:eng:image/png:icon64:/tmp/icon64.png @@ -1865,12 +2227,15 @@ own_ip_addr=127.0.0.1 # OSU Providers # One or more sets of following parameter. Each OSU provider is started by the # mandatory osu_server_uri item. The other parameters add information for the -# last added OSU provider. +# last added OSU provider. osu_nai specifies the OSU_NAI value for OSEN +# authentication when using a standalone OSU BSS. osu_nai2 specifies the OSU_NAI +# value for OSEN authentication when using a shared BSS (Single SSID) for OSU. # #osu_server_uri=https://example.com/osu/ #osu_friendly_name=eng:Example operator #osu_friendly_name=fin:Esimerkkipalveluntarjoaja #osu_nai=anonymous@example.com +#osu_nai2=anonymous@example.com #osu_method_list=1 0 #osu_icon=icon32 #osu_icon=icon64 @@ -1879,6 +2244,35 @@ own_ip_addr=127.0.0.1 # #osu_server_uri=... +# Operator Icons +# Operator icons are specified using references to the hs20_icon entries +# (Name subfield). This information, if present, is advertsised in the +# Operator Icon Metadata ANQO-element. +#operator_icon=icon32 +#operator_icon=icon64 + +##### Multiband Operation (MBO) ############################################### +# +# MBO enabled +# 0 = disabled (default) +# 1 = enabled +#mbo=1 +# +# Cellular data connection preference +# 0 = Excluded - AP does not want STA to use the cellular data connection +# 1 = AP prefers the STA not to use cellular data connection +# 255 = AP prefers the STA to use cellular data connection +#mbo_cell_data_conn_pref=1 + +##### Optimized Connectivity Experience (OCE) ################################# +# +# Enable OCE specific features (bitmap) +# BIT(0) - Reserved +# Set BIT(1) (= 2) to enable OCE in STA-CFON mode +# Set BIT(2) (= 4) to enable OCE in AP mode +# Default is 0 = OCE disabled +#oce=0 + ##### Fast Session Transfer (FST) support ##################################### # # The options in this section are only available when the build configuration @@ -1916,6 +2310,9 @@ own_ip_addr=127.0.0.1 # Enable neighbor report via radio measurements #rrm_neighbor_report=1 +# Enable beacon report via radio measurements +#rrm_beacon_report=1 + # Publish fine timing measurement (FTM) responder functionality # This parameter only controls publishing via Extended Capabilities element. # Actual functionality is managed outside hostapd. @@ -1925,6 +2322,12 @@ own_ip_addr=127.0.0.1 # This parameter only controls publishing via Extended Capabilities element. # Actual functionality is managed outside hostapd. #ftm_initiator=0 +# +# Stationary AP config indicates that the AP doesn't move hence location data +# can be considered as always up to date. If configured, LCI data will be sent +# as a radio measurement even if the request doesn't contain a max age element +# that allows sending of such data. Default: 0. +#stationary_ap=0 ##### TESTING OPTIONS ######################################################### # diff --git a/hostapd/hostapd.eap_user_sqlite b/hostapd/hostapd.eap_user_sqlite index 826db349cfc2a..411b9eafa2643 100644 --- a/hostapd/hostapd.eap_user_sqlite +++ b/hostapd/hostapd.eap_user_sqlite @@ -3,7 +3,8 @@ CREATE TABLE users( methods TEXT, password TEXT, remediation TEXT, - phase2 INTEGER + phase2 INTEGER, + t_c_timestamp INTEGER ); CREATE TABLE wildcards( @@ -24,3 +25,18 @@ CREATE TABLE authlog( username TEXT, note TEXT ); + +CREATE TABLE pending_tc( + mac_addr TEXT PRIMARY KEY, + identity TEXT +); + +CREATE TABLE current_sessions( + mac_addr TEXT PRIMARY KEY, + identity TEXT, + start_time TEXT, + nas TEXT, + hs20_t_c_filtering BOOLEAN, + waiting_coa_ack BOOLEAN, + coa_ack_received BOOLEAN +); diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index 5e6254244b316..489da397c63d3 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -1,6 +1,6 @@ /* * hostapd - command line interface for hostapd 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. @@ -21,7 +21,7 @@ static const char *const hostapd_cli_version = "hostapd_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"; static struct wpa_ctrl *ctrl_conn; static int hostapd_cli_quit = 0; @@ -45,6 +45,8 @@ static DEFINE_DL_LIST(stations); /* struct cli_txt_entry */ static void print_help(FILE *stream, const char *cmd); static char ** list_cmd_list(void); static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx); +static void update_stations(struct wpa_ctrl *ctrl); +static void cli_event(const char *str); static void usage(void) @@ -147,13 +149,45 @@ static void hostapd_cli_close_connection(void) } +static int hostapd_cli_reconnect(const char *ifname) +{ + char *next_ctrl_ifname; + + hostapd_cli_close_connection(); + + if (!ifname) + return -1; + + next_ctrl_ifname = os_strdup(ifname); + os_free(ctrl_ifname); + ctrl_ifname = next_ctrl_ifname; + if (!ctrl_ifname) + return -1; + + ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); + if (!ctrl_conn) + return -1; + if (!interactive && !action_file) + return 0; + if (wpa_ctrl_attach(ctrl_conn) == 0) { + hostapd_cli_attached = 1; + register_event_handler(ctrl_conn); + update_stations(ctrl_conn); + } else { + printf("Warning: Failed to attach to hostapd.\n"); + } + return 0; +} + + static void hostapd_cli_msg_cb(char *msg, size_t len) { + cli_event(msg); printf("%s\n", msg); } -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; @@ -181,7 +215,7 @@ static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print) } -static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd) +static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd) { return _wpa_ctrl_command(ctrl, cmd, 1); } @@ -286,6 +320,21 @@ static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static char ** hostapd_complete_stations(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 hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -318,21 +367,6 @@ static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc, } -static char ** hostapd_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 hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -351,21 +385,6 @@ static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc, } -static char ** hostapd_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; -} - - #ifdef CONFIG_TAXONOMY static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc, char *argv[]) @@ -701,8 +720,8 @@ static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc, } -static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, - char *addr, size_t addr_len) +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; @@ -726,7 +745,8 @@ static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, buf[len] = '\0'; if (memcmp(buf, "FAIL", 4) == 0) return -1; - printf("%s", buf); + if (print) + printf("%s", buf); pos = buf; while (*pos != '\0' && *pos != '\n') @@ -742,16 +762,33 @@ static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, { 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 { 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 hostapd_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 hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) { print_help(stdout, argc > 0 ? argv[0] : NULL); @@ -888,6 +925,25 @@ static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static void update_stations(struct wpa_ctrl *ctrl) +{ + 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); +} + + static void hostapd_cli_get_interfaces(struct wpa_ctrl *ctrl, struct dl_list *interfaces) { @@ -940,23 +996,7 @@ static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, hostapd_cli_list_interfaces(ctrl); return 0; } - - hostapd_cli_close_connection(); - os_free(ctrl_ifname); - ctrl_ifname = os_strdup(argv[0]); - if (ctrl_ifname == NULL) - return -1; - - if (hostapd_cli_open_connection(ctrl_ifname)) { - printf("Connected to interface '%s.\n", ctrl_ifname); - if (wpa_ctrl_attach(ctrl_conn) == 0) { - hostapd_cli_attached = 1; - register_event_handler(ctrl_conn); - } else { - printf("Warning: Failed to attach to " - "hostapd.\n"); - } - } else { + if (hostapd_cli_reconnect(argv[0]) != 0) { printf("Could not connect to interface '%s' - re-trying\n", ctrl_ifname); } @@ -984,7 +1024,7 @@ static char ** hostapd_complete_interface(const char *str, int pos) static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char cmd[256]; + char cmd[2048]; int res; if (argc != 2) { @@ -1002,6 +1042,44 @@ static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static char ** hostapd_complete_set(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + const char *fields[] = { +#ifdef CONFIG_WPS_TESTING + "wps_version_number", "wps_testing_dummy_cred", + "wps_corrupt_pkhash", +#endif /* CONFIG_WPS_TESTING */ +#ifdef CONFIG_INTERWORKING + "gas_frag_limit", +#endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_TESTING_OPTIONS + "ext_mgmt_frame_handling", "ext_eapol_frame_io", +#endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_MBO + "mbo_assoc_disallow", +#endif /* CONFIG_MBO */ + "deny_mac_file", "accept_mac_file", + }; + int i, num_fields = ARRAY_SIZE(fields); + + if (arg == 1) { + char **res; + + res = os_calloc(num_fields + 1, sizeof(char *)); + if (!res) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(fields[i]); + if (!res[i]) + return res; + } + return res; + } + return NULL; +} + + static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[256]; @@ -1022,6 +1100,31 @@ static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static char ** hostapd_complete_get(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + const char *fields[] = { + "version", "tls_library", + }; + int i, num_fields = ARRAY_SIZE(fields); + + if (arg == 1) { + char **res; + + res = os_calloc(num_fields + 1, sizeof(char *)); + if (!res) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(fields[i]); + if (!res[i]) + return res; + } + return res; + } + return NULL; +} + + #ifdef CONFIG_FST static int hostapd_cli_cmd_fst(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -1185,14 +1288,14 @@ static int hostapd_cli_cmd_set_neighbor(struct wpa_ctrl *ctrl, int argc, char cmd[2048]; int res; - if (argc < 3 || argc > 5) { - printf("Invalid set_neighbor command: needs 3-5 arguments\n"); + if (argc < 3 || argc > 6) { + printf("Invalid set_neighbor command: needs 3-6 arguments\n"); return -1; } - res = os_snprintf(cmd, sizeof(cmd), "SET_NEIGHBOR %s %s %s %s %s", + res = os_snprintf(cmd, sizeof(cmd), "SET_NEIGHBOR %s %s %s %s %s %s", argv[0], argv[1], argv[2], argc >= 4 ? argv[3] : "", - argc == 5 ? argv[4] : ""); + argc >= 5 ? argv[4] : "", argc == 6 ? argv[5] : ""); if (os_snprintf_error(sizeof(cmd), res)) { printf("Too long SET_NEIGHBOR command.\n"); return -1; @@ -1261,6 +1364,122 @@ static int hostapd_cli_cmd_driver_flags(struct wpa_ctrl *ctrl, int argc, } +#ifdef CONFIG_DPP + +static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_QR_CODE", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_bootstrap_gen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GEN", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_bootstrap_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_REMOVE", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_bootstrap_get_uri(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GET_URI", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_bootstrap_info(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_INFO", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_auth_init(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_AUTH_INIT", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_listen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_LISTEN", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_stop_listen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DPP_STOP_LISTEN"); +} + + +static int hostapd_cli_cmd_dpp_configurator_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_ADD", 0, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_configurator_remove(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_REMOVE", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_configurator_get_key(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_GET_KEY", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_pkex_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_PKEX_ADD", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_pkex_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv); +} + +#endif /* CONFIG_DPP */ + + +static int hostapd_cli_cmd_accept_macacl(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "ACCEPT_ACL", 1, argc, argv); +} + + +static int hostapd_cli_cmd_deny_macacl(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DENY_ACL", 1, argc, argv); +} + + +static int hostapd_cli_cmd_poll_sta(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "POLL_STA", 1, argc, argv); +} + + struct hostapd_cli_cmd { const char *cmd; int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); @@ -1273,26 +1492,30 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = { "= pings hostapd" }, { "mib", hostapd_cli_cmd_mib, NULL, "= get MIB variables (dot1x, dot11, radius)" }, - { "relog", hostapd_cli_cmd_relog, NULL, NULL }, - { "status", hostapd_cli_cmd_status, NULL, NULL }, - { "sta", hostapd_cli_cmd_sta, NULL, + { "relog", hostapd_cli_cmd_relog, NULL, + "= reload/truncate debug log output file" }, + { "status", hostapd_cli_cmd_status, NULL, + "= show interface status info" }, + { "sta", hostapd_cli_cmd_sta, hostapd_complete_stations, "<addr> = get MIB variables for one station" }, { "all_sta", hostapd_cli_cmd_all_sta, NULL, "= get MIB variables for all stations" }, + { "list_sta", hostapd_cli_cmd_list_sta, NULL, + "= list all stations" }, { "new_sta", hostapd_cli_cmd_new_sta, NULL, "<addr> = add a new station" }, { "deauthenticate", hostapd_cli_cmd_deauthenticate, - hostapd_complete_deauthenticate, + hostapd_complete_stations, "<addr> = deauthenticate a station" }, { "disassociate", hostapd_cli_cmd_disassociate, - hostapd_complete_disassociate, + hostapd_complete_stations, "<addr> = disassociate a station" }, #ifdef CONFIG_TAXONOMY - { "signature", hostapd_cli_cmd_signature, NULL, + { "signature", hostapd_cli_cmd_signature, hostapd_complete_stations, "<addr> = get taxonomy signature for a station" }, #endif /* CONFIG_TAXONOMY */ #ifdef CONFIG_IEEE80211W - { "sa_query", hostapd_cli_cmd_sa_query, NULL, + { "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations, "<addr> = send SA Query to a station" }, #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WPS @@ -1321,9 +1544,12 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = { { "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL, "= show current WPS status" }, #endif /* CONFIG_WPS */ - { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL, NULL }, - { "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL, NULL }, - { "bss_tm_req", hostapd_cli_cmd_bss_tm_req, NULL, NULL }, + { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL, + "= send Disassociation Imminent notification" }, + { "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL, + "= send ESS Dissassociation Imminent notification" }, + { "bss_tm_req", hostapd_cli_cmd_bss_tm_req, NULL, + "= send BSS Transition Management Request" }, { "get_config", hostapd_cli_cmd_get_config, NULL, "= show current configuration" }, { "help", hostapd_cli_cmd_help, hostapd_cli_complete_help, @@ -1331,35 +1557,100 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = { { "interface", hostapd_cli_cmd_interface, hostapd_complete_interface, "[ifname] = show interfaces/select interface" }, #ifdef CONFIG_FST - { "fst", hostapd_cli_cmd_fst, NULL, NULL }, + { "fst", hostapd_cli_cmd_fst, NULL, + "<params...> = send FST-MANAGER control interface command" }, #endif /* CONFIG_FST */ - { "raw", hostapd_cli_cmd_raw, NULL, NULL }, + { "raw", hostapd_cli_cmd_raw, NULL, + "<params..> = send unprocessed command" }, { "level", hostapd_cli_cmd_level, NULL, "<debug level> = change debug level" }, { "license", hostapd_cli_cmd_license, NULL, "= show full hostapd_cli license" }, { "quit", hostapd_cli_cmd_quit, NULL, "= exit hostapd_cli" }, - { "set", hostapd_cli_cmd_set, NULL, NULL }, - { "get", hostapd_cli_cmd_get, NULL, NULL }, - { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set, NULL, NULL }, - { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf, NULL, NULL }, - { "chan_switch", hostapd_cli_cmd_chan_switch, NULL, NULL }, - { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL, NULL }, - { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req, NULL, NULL }, - { "vendor", hostapd_cli_cmd_vendor, NULL, NULL }, - { "enable", hostapd_cli_cmd_enable, NULL, NULL }, - { "reload", hostapd_cli_cmd_reload, NULL, NULL }, - { "disable", hostapd_cli_cmd_disable, NULL, NULL }, - { "erp_flush", hostapd_cli_cmd_erp_flush, NULL, NULL }, - { "log_level", hostapd_cli_cmd_log_level, NULL, NULL }, - { "pmksa", hostapd_cli_cmd_pmksa, NULL, NULL }, - { "pmksa_flush", hostapd_cli_cmd_pmksa_flush, NULL, NULL }, - { "set_neighbor", hostapd_cli_cmd_set_neighbor, NULL, NULL }, - { "remove_neighbor", hostapd_cli_cmd_remove_neighbor, NULL, NULL }, - { "req_lci", hostapd_cli_cmd_req_lci, NULL, NULL }, - { "req_range", hostapd_cli_cmd_req_range, NULL, NULL }, - { "driver_flags", hostapd_cli_cmd_driver_flags, NULL, NULL }, + { "set", hostapd_cli_cmd_set, hostapd_complete_set, + "<name> <value> = set runtime variables" }, + { "get", hostapd_cli_cmd_get, hostapd_complete_get, + "<name> = get runtime info" }, + { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set, NULL, + "<arg,arg,...> = set QoS Map set element" }, + { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf, + hostapd_complete_stations, + "<addr> = send QoS Map Configure frame" }, + { "chan_switch", hostapd_cli_cmd_chan_switch, NULL, + "<cs_count> <freq> [sec_channel_offset=] [center_freq1=]\n" + " [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n" + " = initiate channel switch announcement" }, + { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL, + "<addr> <url>\n" + " = send WNM-Notification Subscription Remediation Request" }, + { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req, NULL, + "<addr> <code (0/1)> <Re-auth-Delay(sec)> [url]\n" + " = send WNM-Notification imminent deauthentication indication" }, + { "vendor", hostapd_cli_cmd_vendor, NULL, + "<vendor id> <sub command id> [<hex formatted data>]\n" + " = send vendor driver command" }, + { "enable", hostapd_cli_cmd_enable, NULL, + "= enable hostapd on current interface" }, + { "reload", hostapd_cli_cmd_reload, NULL, + "= reload configuration for current interface" }, + { "disable", hostapd_cli_cmd_disable, NULL, + "= disable hostapd on current interface" }, + { "erp_flush", hostapd_cli_cmd_erp_flush, NULL, + "= drop all ERP keys"}, + { "log_level", hostapd_cli_cmd_log_level, NULL, + "[level] = show/change log verbosity level" }, + { "pmksa", hostapd_cli_cmd_pmksa, NULL, + " = show PMKSA cache entries" }, + { "pmksa_flush", hostapd_cli_cmd_pmksa_flush, NULL, + " = flush PMKSA cache" }, + { "set_neighbor", hostapd_cli_cmd_set_neighbor, NULL, + "<addr> <ssid=> <nr=> [lci=] [civic=] [stat]\n" + " = add AP to neighbor database" }, + { "remove_neighbor", hostapd_cli_cmd_remove_neighbor, NULL, + "<addr> <ssid=> = remove AP from neighbor database" }, + { "req_lci", hostapd_cli_cmd_req_lci, hostapd_complete_stations, + "<addr> = send LCI request to a station"}, + { "req_range", hostapd_cli_cmd_req_range, NULL, + " = send FTM range request"}, + { "driver_flags", hostapd_cli_cmd_driver_flags, NULL, + " = show supported driver flags"}, +#ifdef CONFIG_DPP + { "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL, + "report a scanned DPP URI from a QR Code" }, + { "dpp_bootstrap_gen", hostapd_cli_cmd_dpp_bootstrap_gen, NULL, + "type=<qrcode> [chan=..] [mac=..] [info=..] [curve=..] [key=..] = generate DPP bootstrap information" }, + { "dpp_bootstrap_remove", hostapd_cli_cmd_dpp_bootstrap_remove, NULL, + "*|<id> = remove DPP bootstrap information" }, + { "dpp_bootstrap_get_uri", hostapd_cli_cmd_dpp_bootstrap_get_uri, NULL, + "<id> = get DPP bootstrap URI" }, + { "dpp_bootstrap_info", hostapd_cli_cmd_dpp_bootstrap_info, NULL, + "<id> = show DPP bootstrap information" }, + { "dpp_auth_init", hostapd_cli_cmd_dpp_auth_init, NULL, + "peer=<id> [own=<id>] = initiate DPP bootstrapping" }, + { "dpp_listen", hostapd_cli_cmd_dpp_listen, NULL, + "<freq in MHz> = start DPP listen" }, + { "dpp_stop_listen", hostapd_cli_cmd_dpp_stop_listen, NULL, + "= stop DPP listen" }, + { "dpp_configurator_add", hostapd_cli_cmd_dpp_configurator_add, NULL, + "[curve=..] [key=..] = add DPP configurator" }, + { "dpp_configurator_remove", hostapd_cli_cmd_dpp_configurator_remove, + NULL, + "*|<id> = remove DPP configurator" }, + { "dpp_configurator_get_key", hostapd_cli_cmd_dpp_configurator_get_key, + NULL, + "<id> = Get DPP configurator's private key" }, + { "dpp_pkex_add", hostapd_cli_cmd_dpp_pkex_add, NULL, + "add PKEX code" }, + { "dpp_pkex_remove", hostapd_cli_cmd_dpp_pkex_remove, NULL, + "*|<id> = remove DPP pkex information" }, +#endif /* CONFIG_DPP */ + { "accept_acl", hostapd_cli_cmd_accept_macacl, NULL, + "=Add/Delete/Show/Clear accept MAC ACL" }, + { "deny_acl", hostapd_cli_cmd_deny_macacl, NULL, + "=Add/Delete/Show/Clear deny MAC ACL" }, + { "poll_sta", hostapd_cli_cmd_poll_sta, hostapd_complete_stations, + "<addr> = poll a STA to check connectivity with a QoS null frame" }, { NULL, NULL, NULL, NULL } }; @@ -1471,7 +1762,7 @@ static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read, if (ctrl_conn == NULL) return; while (wpa_ctrl_pending(ctrl)) { - char buf[256]; + char buf[4096]; size_t len = sizeof(buf) - 1; if (wpa_ctrl_recv(ctrl, buf, &len) == 0) { buf[len] = '\0'; @@ -1504,19 +1795,8 @@ static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx) printf("Connection to hostapd lost - trying to reconnect\n"); hostapd_cli_close_connection(); } - if (!ctrl_conn) { - ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); - if (ctrl_conn) { - printf("Connection to hostapd re-established\n"); - if (wpa_ctrl_attach(ctrl_conn) == 0) { - hostapd_cli_attached = 1; - register_event_handler(ctrl_conn); - } else { - printf("Warning: Failed to attach to " - "hostapd.\n"); - } - } - } + if (!ctrl_conn && hostapd_cli_reconnect(ctrl_ifname) == 0) + printf("Connection to hostapd re-established\n"); if (ctrl_conn) hostapd_cli_recv_pending(ctrl_conn, 1, 0); eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); @@ -1611,17 +1891,34 @@ static char ** hostapd_cli_edit_completion_cb(void *ctx, const char *str, static void hostapd_cli_interactive(void) { + char *hfile = NULL; + char *home; + printf("\nInteractive mode\n\n"); +#ifdef CONFIG_HOSTAPD_CLI_HISTORY_DIR + home = CONFIG_HOSTAPD_CLI_HISTORY_DIR; +#else /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */ + home = getenv("HOME"); +#endif /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */ + if (home) { + const char *fname = ".hostapd_cli_history"; + int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1; + hfile = os_malloc(hfile_len); + if (hfile) + os_snprintf(hfile, hfile_len, "%s/%s", home, fname); + } + eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL); edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb, - hostapd_cli_edit_completion_cb, NULL, NULL, NULL); + hostapd_cli_edit_completion_cb, NULL, hfile, NULL); eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); eloop_run(); cli_txt_list_flush(&stations); - edit_deinit(NULL, NULL); + edit_deinit(hfile, NULL); + os_free(hfile); eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL); } @@ -1748,7 +2045,7 @@ int main(int argc, char *argv[]) closedir(dir); } } - ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); + hostapd_cli_reconnect(ctrl_ifname); if (ctrl_conn) { if (warning_displayed) printf("Connection established.\n"); @@ -1769,17 +2066,8 @@ int main(int argc, char *argv[]) continue; } - if (interactive || action_file) { - if (wpa_ctrl_attach(ctrl_conn) == 0) { - hostapd_cli_attached = 1; - register_event_handler(ctrl_conn); - } else { - printf("Warning: Failed to attach to hostapd.\n"); - if (action_file) - return -1; - } - } - + if (action_file && !hostapd_cli_attached) + return -1; if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue()) return -1; diff --git a/hostapd/main.c b/hostapd/main.c index 2c8dbd30a274e..414dfe4241e08 100644 --- a/hostapd/main.c +++ b/hostapd/main.c @@ -1,6 +1,6 @@ /* * hostapd / main() - * Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -24,6 +24,7 @@ #include "ap/hostapd.h" #include "ap/ap_config.h" #include "ap/ap_drv_ops.h" +#include "ap/dpp_hostapd.h" #include "fst/fst.h" #include "config_file.h" #include "eap_register.h" @@ -108,6 +109,10 @@ static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module, module_str ? module_str : "", module_str ? ": " : "", txt); +#ifdef CONFIG_DEBUG_SYSLOG + if (wpa_debug_syslog) + conf_stdout = 0; +#endif /* CONFIG_DEBUG_SYSLOG */ if ((conf_stdout & module) && level >= conf_stdout_level) { wpa_debug_print_timestamp(); wpa_printf(MSG_INFO, "%s", format); @@ -248,7 +253,7 @@ static int hostapd_driver_init(struct hostapd_iface *iface) * * This function is used to parse configuration file for a full interface (one * or more BSSes sharing the same radio) and allocate memory for the BSS - * interfaces. No actiual driver operations are started. + * interfaces. No actual driver operations are started. */ static struct hostapd_iface * hostapd_interface_init(struct hapd_interfaces *interfaces, const char *if_name, @@ -451,7 +456,7 @@ static void show_version(void) "hostapd v" VERSION_STR "\n" "User space daemon for IEEE 802.11 AP management,\n" "IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n" - "Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi> " + "Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi> " "and contributors\n"); } @@ -480,10 +485,13 @@ static void usage(void) " -f log output to debug file instead of stdout\n" #endif /* CONFIG_DEBUG_FILE */ #ifdef CONFIG_DEBUG_LINUX_TRACING - " -T = record to Linux tracing in addition to logging\n" + " -T record to Linux tracing in addition to logging\n" " (records all messages regardless of debug verbosity)\n" #endif /* CONFIG_DEBUG_LINUX_TRACING */ " -i list of interface names to use\n" +#ifdef CONFIG_DEBUG_SYSLOG + " -s log output to syslog instead of stdout\n" +#endif /* CONFIG_DEBUG_SYSLOG */ " -S start all the interfaces synchronously\n" " -t include timestamps in some debug messages\n" " -v show hostapd version\n"); @@ -549,14 +557,14 @@ static int hostapd_get_ctrl_iface_group(struct hapd_interfaces *interfaces, static int hostapd_get_interface_names(char ***if_names, size_t *if_names_size, - char *optarg) + char *arg) { char *if_name, *tmp, **nnames; size_t i; - if (!optarg) + if (!arg) return -1; - if_name = strtok_r(optarg, ",", &tmp); + if_name = strtok_r(arg, ",", &tmp); while (if_name) { nnames = os_realloc_array(*if_names, 1 + *if_names_size, @@ -659,9 +667,15 @@ int main(int argc, char *argv[]) interfaces.global_iface_name = NULL; interfaces.global_ctrl_sock = -1; dl_list_init(&interfaces.global_ctrl_dst); +#ifdef CONFIG_ETH_P_OUI + dl_list_init(&interfaces.eth_p_oui); +#endif /* CONFIG_ETH_P_OUI */ +#ifdef CONFIG_DPP + hostapd_dpp_init_global(&interfaces); +#endif /* CONFIG_DPP */ for (;;) { - c = getopt(argc, argv, "b:Bde:f:hi:KP:STtu:vg:G:"); + c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:"); if (c < 0) break; switch (c) { @@ -718,6 +732,11 @@ int main(int argc, char *argv[]) bss_config = tmp_bss; bss_config[num_bss_configs++] = optarg; break; +#ifdef CONFIG_DEBUG_SYSLOG + case 's': + wpa_debug_syslog = 1; + break; +#endif /* CONFIG_DEBUG_SYSLOG */ case 'S': start_ifaces_in_sync = 1; break; @@ -746,6 +765,10 @@ int main(int argc, char *argv[]) wpa_debug_open_file(log_file); else wpa_debug_setup_stdout(); +#ifdef CONFIG_DEBUG_SYSLOG + if (wpa_debug_syslog) + wpa_debug_open_syslog(); +#endif /* CONFIG_DEBUG_SYSLOG */ #ifdef CONFIG_DEBUG_LINUX_TRACING if (enable_trace_dbg) { int tret = wpa_debug_open_linux_tracing(); @@ -877,11 +900,16 @@ int main(int argc, char *argv[]) } os_free(interfaces.iface); +#ifdef CONFIG_DPP + hostapd_dpp_deinit_global(&interfaces); +#endif /* CONFIG_DPP */ + if (interfaces.eloop_initialized) eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL); hostapd_global_deinit(pid_file, interfaces.eloop_initialized); os_free(pid_file); + wpa_debug_close_syslog(); if (log_file) wpa_debug_close_file(); wpa_debug_close_linux_tracing(); diff --git a/hs20/client/est.c b/hs20/client/est.c index 9f1519bf4e4e9..b1aacb8ffbbe5 100644 --- a/hs20/client/est.c +++ b/hs20/client/est.c @@ -666,7 +666,6 @@ int est_simple_enroll(struct hs20_osu_client *ctx, const char *url, char *buf, *resp, *req, *req2; size_t buflen, resp_len, len, pkcs7_len; unsigned char *pkcs7; - FILE *f; char client_cert_buf[200]; char client_key_buf[200]; const char *client_cert = NULL, *client_key = NULL; @@ -721,11 +720,6 @@ int est_simple_enroll(struct hs20_osu_client *ctx, const char *url, return -1; } wpa_printf(MSG_DEBUG, "EST simpleenroll response: %s", resp); - f = fopen("Cert/est-resp.raw", "w"); - if (f) { - fwrite(resp, resp_len, 1, f); - fclose(f); - } pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len); if (pkcs7 == NULL) { diff --git a/hs20/client/oma_dm_client.c b/hs20/client/oma_dm_client.c index 5854b726dba2b..d75c84562acad 100644 --- a/hs20/client/oma_dm_client.c +++ b/hs20/client/oma_dm_client.c @@ -111,6 +111,12 @@ static xml_node_t * oma_dm_build_hdr(struct hs20_osu_client *ctx, xml_node_t *syncml, *synchdr; xml_namespace_t *ns; + if (!ctx->devid) { + wpa_printf(MSG_ERROR, + "DevId from devinfo.xml is not available - cannot use OMA DM"); + return NULL; + } + syncml = xml_node_create_root(ctx->xml, "SYNCML:SYNCML1.2", NULL, &ns, "SyncML"); diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c index c05c57d44f896..636e10666f8b0 100644 --- a/hs20/client/osu_client.c +++ b/hs20/client/osu_client.c @@ -105,6 +105,35 @@ static int valid_fqdn(const char *fqdn) } +static int android_update_permission(const char *path, mode_t mode) +{ +#ifdef ANDROID + /* we need to change file/folder permission for Android */ + + if (!path) { + wpa_printf(MSG_ERROR, "file path null"); + return -1; + } + + /* Allow processes running with Group ID as AID_WIFI, + * to read files from SP, SP/<fqdn>, Cert and osu-info directories */ + if (chown(path, -1, AID_WIFI)) { + wpa_printf(MSG_INFO, "CTRL: Could not chown directory: %s", + strerror(errno)); + return -1; + } + + if (chmod(path, mode) < 0) { + wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s", + strerror(errno)); + return -1; + } +#endif /* ANDROID */ + + return 0; +} + + int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert) { xml_node_t *node; @@ -169,6 +198,8 @@ int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert) } mkdir("Cert", S_IRWXU); + android_update_permission("Cert", S_IRWXU | S_IRWXG); + if (est_load_cacerts(ctx, url) < 0 || est_build_csr(ctx, url) < 0 || est_simple_enroll(ctx, url, user, pw) < 0) @@ -262,7 +293,6 @@ static int process_est_cert(struct hs20_osu_client *ctx, xml_node_t *cert, unlink("Cert/est-req.b64"); unlink("Cert/est-req.pem"); - unlink("Cert/est-resp.raw"); rmdir("Cert"); return 0; @@ -406,7 +436,7 @@ static int cmd_dl_polupd_ca(struct hs20_osu_client *ctx, const char *pps_fname, if (node == NULL) { wpa_printf(MSG_INFO, "No Policy/PolicyUpdate/TrustRoot/CertURL found from PPS"); xml_node_free(ctx->xml, pps); - return -1; + return -2; } ret = download_cert(ctx, node, ca_fname); @@ -433,7 +463,7 @@ static int cmd_dl_aaa_ca(struct hs20_osu_client *ctx, const char *pps_fname, if (node == NULL) { wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS"); xml_node_free(ctx->xml, pps); - return -1; + return -2; } aaa = xml_node_first_child(ctx->xml, node); @@ -455,7 +485,7 @@ static int download_trust_roots(struct hs20_osu_client *ctx, { char *dir, *pos; char fname[300]; - int ret; + int ret, ret1; dir = os_strdup(pps_fname); if (dir == NULL) @@ -470,9 +500,13 @@ static int download_trust_roots(struct hs20_osu_client *ctx, snprintf(fname, sizeof(fname), "%s/ca.pem", dir); ret = cmd_dl_osu_ca(ctx, pps_fname, fname); snprintf(fname, sizeof(fname), "%s/polupd-ca.pem", dir); - cmd_dl_polupd_ca(ctx, pps_fname, fname); + ret1 = cmd_dl_polupd_ca(ctx, pps_fname, fname); + if (ret == 0 && ret1 == -1) + ret = -1; snprintf(fname, sizeof(fname), "%s/aaa-ca.pem", dir); - cmd_dl_aaa_ca(ctx, pps_fname, fname); + ret1 = cmd_dl_aaa_ca(ctx, pps_fname, fname); + if (ret == 0 && ret1 == -1) + ret = -1; os_free(dir); @@ -578,20 +612,8 @@ int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri, } } -#ifdef ANDROID - /* Allow processes running with Group ID as AID_WIFI, - * to read files from SP/<fqdn> directory */ - if (chown(fname, -1, AID_WIFI)) { - wpa_printf(MSG_INFO, "CTRL: Could not chown directory: %s", - strerror(errno)); - /* Try to continue anyway */ - } - if (chmod(fname, S_IRWXU | S_IRGRP | S_IXGRP) < 0) { - wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s", - strerror(errno)); - /* Try to continue anyway */ - } -#endif /* ANDROID */ + android_update_permission("SP", S_IRWXU | S_IRGRP | S_IXGRP); + android_update_permission(fname, S_IRWXU | S_IRGRP | S_IXGRP); snprintf(fname, fname_len, "SP/%s/pps.xml", fqdn); @@ -1213,8 +1235,7 @@ static void set_pps_cred_home_sp_oi(struct hs20_osu_client *ctx, int id, homeoi) < 0) wpa_printf(MSG_INFO, "Failed to set cred required_roaming_consortium"); } else { - if (set_cred_quoted(ctx->ifname, id, "roaming_consortium", - homeoi) < 0) + if (set_cred(ctx->ifname, id, "roaming_consortium", homeoi) < 0) wpa_printf(MSG_INFO, "Failed to set cred roaming_consortium"); } @@ -1289,7 +1310,9 @@ static void set_pps_cred_home_sp_roaming_consortium_oi( if (str == NULL) return; wpa_printf(MSG_INFO, "- HomeSP/RoamingConsortiumOI = %s", str); - /* TODO: Set to wpa_supplicant */ + if (set_cred_quoted(ctx->ifname, id, "roaming_consortiums", + str) < 0) + wpa_printf(MSG_INFO, "Failed to set cred roaming_consortiums"); xml_node_get_text_free(ctx->xml, str); } @@ -1442,10 +1465,92 @@ static void set_pps_cred_able_to_share(struct hs20_osu_client *ctx, int id, } +static void set_pps_cred_eap_method_eap_type(struct hs20_osu_client *ctx, + int id, xml_node_t *node) +{ + char *str = xml_node_get_text(ctx->xml, node); + int type; + const char *eap_method = NULL; + + if (!str) + return; + wpa_printf(MSG_INFO, + "- Credential/UsernamePassword/EAPMethod/EAPType = %s", str); + type = atoi(str); + switch (type) { + case EAP_TYPE_TLS: + eap_method = "TLS"; + break; + case EAP_TYPE_TTLS: + eap_method = "TTLS"; + break; + case EAP_TYPE_PEAP: + eap_method = "PEAP"; + break; + case EAP_TYPE_PWD: + eap_method = "PWD"; + break; + } + xml_node_get_text_free(ctx->xml, str); + if (!eap_method) { + wpa_printf(MSG_INFO, "Unknown EAPType value"); + return; + } + + if (set_cred(ctx->ifname, id, "eap", eap_method) < 0) + wpa_printf(MSG_INFO, "Failed to set cred eap"); +} + + +static void set_pps_cred_eap_method_inner_method(struct hs20_osu_client *ctx, + int id, xml_node_t *node) +{ + char *str = xml_node_get_text(ctx->xml, node); + const char *phase2 = NULL; + + if (!str) + return; + wpa_printf(MSG_INFO, + "- Credential/UsernamePassword/EAPMethod/InnerMethod = %s", + str); + if (os_strcmp(str, "PAP") == 0) + phase2 = "auth=PAP"; + else if (os_strcmp(str, "CHAP") == 0) + phase2 = "auth=CHAP"; + else if (os_strcmp(str, "MS-CHAP") == 0) + phase2 = "auth=MSCHAP"; + else if (os_strcmp(str, "MS-CHAP-V2") == 0) + phase2 = "auth=MSCHAPV2"; + xml_node_get_text_free(ctx->xml, str); + if (!phase2) { + wpa_printf(MSG_INFO, "Unknown InnerMethod value"); + return; + } + + if (set_cred_quoted(ctx->ifname, id, "phase2", phase2) < 0) + wpa_printf(MSG_INFO, "Failed to set cred phase2"); +} + + static void set_pps_cred_eap_method(struct hs20_osu_client *ctx, int id, xml_node_t *node) { - wpa_printf(MSG_INFO, "- Credential/UsernamePassword/EAPMethod - TODO"); + xml_node_t *child; + const char *name; + + wpa_printf(MSG_INFO, "- Credential/UsernamePassword/EAPMethod"); + + xml_node_for_each_child(ctx->xml, child, node) { + xml_node_for_each_check(ctx->xml, child); + name = xml_node_get_localname(ctx->xml, child); + if (os_strcasecmp(name, "EAPType") == 0) + set_pps_cred_eap_method_eap_type(ctx, id, child); + else if (os_strcasecmp(name, "InnerMethod") == 0) + set_pps_cred_eap_method_inner_method(ctx, id, child); + else + wpa_printf(MSG_INFO, "Unknown Credential/UsernamePassword/EAPMethod node '%s'", + name); + } } @@ -1884,7 +1989,9 @@ struct osu_data { char url[256]; unsigned int methods; char osu_ssid[33]; + char osu_ssid2[33]; char osu_nai[256]; + char osu_nai2[256]; struct osu_lang_text friendly_name[MAX_OSU_VALS]; size_t friendly_name_count; struct osu_lang_text serv_desc[MAX_OSU_VALS]; @@ -1943,12 +2050,24 @@ static struct osu_data * parse_osu_providers(const char *fname, size_t *count) continue; } + if (strncmp(buf, "osu_ssid2=", 10) == 0) { + snprintf(last->osu_ssid2, sizeof(last->osu_ssid2), + "%s", buf + 10); + continue; + } + if (os_strncmp(buf, "osu_nai=", 8) == 0) { os_snprintf(last->osu_nai, sizeof(last->osu_nai), "%s", buf + 8); continue; } + if (os_strncmp(buf, "osu_nai2=", 9) == 0) { + os_snprintf(last->osu_nai2, sizeof(last->osu_nai2), + "%s", buf + 9); + continue; + } + if (strncmp(buf, "friendly_name=", 14) == 0) { struct osu_lang_text *txt; if (last->friendly_name_count == MAX_OSU_VALS) @@ -2024,9 +2143,9 @@ static struct osu_data * parse_osu_providers(const char *fname, size_t *count) static int osu_connect(struct hs20_osu_client *ctx, const char *bssid, - const char *ssid, const char *url, + const char *ssid, const char *ssid2, const char *url, unsigned int methods, int no_prod_assoc, - const char *osu_nai) + const char *osu_nai, const char *osu_nai2) { int id; const char *ifname = ctx->ifname; @@ -2034,26 +2153,54 @@ static int osu_connect(struct hs20_osu_client *ctx, const char *bssid, struct wpa_ctrl *mon; int res; + if (ssid2 && ssid2[0] == '\0') + ssid2 = NULL; + + if (ctx->osu_ssid) { + if (os_strcmp(ssid, ctx->osu_ssid) == 0) { + wpa_printf(MSG_DEBUG, + "Enforced OSU SSID matches ANQP info"); + ssid2 = NULL; + } else if (ssid2 && os_strcmp(ssid2, ctx->osu_ssid) == 0) { + wpa_printf(MSG_DEBUG, + "Enforced OSU SSID matches RSN[OSEN] info"); + ssid = ssid2; + } else { + wpa_printf(MSG_INFO, "Enforced OSU SSID did not match"); + write_summary(ctx, "Enforced OSU SSID did not match"); + return -1; + } + } + id = add_network(ifname); if (id < 0) return -1; if (set_network_quoted(ifname, id, "ssid", ssid) < 0) return -1; + if (ssid2) + osu_nai = osu_nai2; if (osu_nai && os_strlen(osu_nai) > 0) { char dir[255], fname[300]; if (getcwd(dir, sizeof(dir)) == NULL) return -1; os_snprintf(fname, sizeof(fname), "%s/osu-ca.pem", dir); + if (ssid2 && set_network_quoted(ifname, id, "ssid", ssid2) < 0) + return -1; + if (set_network(ifname, id, "proto", "OSEN") < 0 || set_network(ifname, id, "key_mgmt", "OSEN") < 0 || set_network(ifname, id, "pairwise", "CCMP") < 0 || - set_network(ifname, id, "group", "GTK_NOT_USED") < 0 || + set_network(ifname, id, "group", "GTK_NOT_USED CCMP") < 0 || set_network(ifname, id, "eap", "WFA-UNAUTH-TLS") < 0 || set_network(ifname, id, "ocsp", "2") < 0 || set_network_quoted(ifname, id, "identity", osu_nai) < 0 || set_network_quoted(ifname, id, "ca_cert", fname) < 0) return -1; + } else if (ssid2) { + wpa_printf(MSG_INFO, "No OSU_NAI set for RSN[OSEN]"); + write_summary(ctx, "No OSU_NAI set for RSN[OSEN]"); + return -1; } else { if (set_network(ifname, id, "key_mgmt", "NONE") < 0) return -1; @@ -2134,7 +2281,7 @@ static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir, char fname[255]; FILE *f; struct osu_data *osu = NULL, *last = NULL; - size_t osu_count, i, j; + size_t osu_count = 0, i, j; int ret; write_summary(ctx, "OSU provider selection"); @@ -2229,8 +2376,12 @@ static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir, fprintf(f, "</table></a><br><small>BSSID: %s<br>\n" "SSID: %s<br>\n", last->bssid, last->osu_ssid); + if (last->osu_ssid2[0]) + fprintf(f, "SSID2: %s<br>\n", last->osu_ssid2); if (last->osu_nai[0]) fprintf(f, "NAI: %s<br>\n", last->osu_nai); + if (last->osu_nai2[0]) + fprintf(f, "NAI2: %s<br>\n", last->osu_nai2); fprintf(f, "URL: %s<br>\n" "methods:%s%s<br>\n" "</small></p>\n", @@ -2257,6 +2408,8 @@ selected: ret = 0; wpa_printf(MSG_INFO, "BSSID: %s", last->bssid); wpa_printf(MSG_INFO, "SSID: %s", last->osu_ssid); + if (last->osu_ssid2[0]) + wpa_printf(MSG_INFO, "SSID2: %s", last->osu_ssid2); wpa_printf(MSG_INFO, "URL: %s", last->url); write_summary(ctx, "Selected OSU provider id=%d BSSID=%s SSID=%s URL=%s", ret, last->bssid, last->osu_ssid, last->url); @@ -2311,10 +2464,13 @@ selected: "No supported OSU provisioning method"); ret = -1; } - } else if (connect) + } else if (connect) { ret = osu_connect(ctx, last->bssid, last->osu_ssid, + last->osu_ssid2, last->url, last->methods, - no_prod_assoc, last->osu_nai); + no_prod_assoc, last->osu_nai, + last->osu_nai2); + } } else ret = -1; @@ -2346,15 +2502,7 @@ static int cmd_signup(struct hs20_osu_client *ctx, int no_prod_assoc, return -1; } -#ifdef ANDROID - /* Allow processes running with Group ID as AID_WIFI - * to read/write files from osu-info directory - */ - if (chown(fname, -1, AID_WIFI)) { - wpa_printf(MSG_INFO, "Could not chown osu-info directory: %s", - strerror(errno)); - } -#endif /* ANDROID */ + android_update_permission(fname, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); snprintf(buf, sizeof(buf), "SET osu_dir %s", fname); if (wpa_command(ifname, buf) < 0) { @@ -2920,24 +3068,17 @@ static int init_ctx(struct hs20_osu_client *ctx) return -1; devinfo = node_from_file(ctx->xml, "devinfo.xml"); - if (!devinfo) { - wpa_printf(MSG_ERROR, "devinfo.xml not found"); - return -1; - } - - devid = get_node(ctx->xml, devinfo, "DevId"); - if (devid) { - char *tmp = xml_node_get_text(ctx->xml, devid); - if (tmp) { - ctx->devid = os_strdup(tmp); - xml_node_get_text_free(ctx->xml, tmp); + if (devinfo) { + devid = get_node(ctx->xml, devinfo, "DevId"); + if (devid) { + char *tmp = xml_node_get_text(ctx->xml, devid); + + if (tmp) { + ctx->devid = os_strdup(tmp); + xml_node_get_text_free(ctx->xml, tmp); + } } - } - xml_node_free(ctx->xml, devinfo); - - if (ctx->devid == NULL) { - wpa_printf(MSG_ERROR, "Could not fetch DevId from devinfo.xml"); - return -1; + xml_node_free(ctx->xml, devinfo); } ctx->http = http_init_ctx(ctx, ctx->xml); @@ -3040,7 +3181,7 @@ int main(int argc, char *argv[]) return -1; for (;;) { - c = getopt(argc, argv, "df:hKNO:qr:s:S:tw:x:"); + c = getopt(argc, argv, "df:hKNo:O:qr:s:S:tw:x:"); if (c < 0) break; switch (c) { @@ -3057,6 +3198,9 @@ int main(int argc, char *argv[]) case 'N': no_prod_assoc = 1; break; + case 'o': + ctx.osu_ssid = optarg; + break; case 'O': friendly_name = optarg; break; diff --git a/hs20/client/osu_client.h b/hs20/client/osu_client.h index 9a7059edfddb3..5c8e6d00b6bb8 100644 --- a/hs20/client/osu_client.h +++ b/hs20/client/osu_client.h @@ -47,6 +47,7 @@ struct hs20_osu_client { int client_cert_present; char **server_dnsname; size_t server_dnsname_count; + const char *osu_ssid; /* Enforced OSU_SSID for testing purposes */ #define WORKAROUND_OCSP_OPTIONAL 0x00000001 unsigned long int workarounds; }; diff --git a/src/ap/Makefile b/src/ap/Makefile index 98788fef797ed..9b07ee163419f 100644 --- a/src/ap/Makefile +++ b/src/ap/Makefile @@ -10,12 +10,15 @@ include ../lib.rules CFLAGS += -DHOSTAPD CFLAGS += -DNEED_AP_MLME +CFLAGS += -DCONFIG_ETH_P_OUI CFLAGS += -DCONFIG_HS20 CFLAGS += -DCONFIG_INTERWORKING CFLAGS += -DCONFIG_IEEE80211R +CFLAGS += -DCONFIG_IEEE80211R_AP CFLAGS += -DCONFIG_IEEE80211W CFLAGS += -DCONFIG_WPS CFLAGS += -DCONFIG_PROXYARP +CFLAGS += -DCONFIG_IPV6 CFLAGS += -DCONFIG_IAPP LIB_OBJS= \ @@ -32,6 +35,7 @@ LIB_OBJS= \ dhcp_snoop.o \ drv_callbacks.o \ eap_user_db.o \ + eth_p_oui.o \ gas_serv.o \ hostapd.o \ hs20.o \ @@ -43,14 +47,17 @@ LIB_OBJS= \ ieee802_11_shared.o \ ieee802_11_vht.o \ ieee802_1x.o \ + neighbor_db.o \ ndisc_snoop.o \ p2p_hostapd.o \ - peerkey_auth.o \ pmksa_cache_auth.o \ preauth_auth.o \ + rrm.o \ sta_info.o \ tkip_countermeasures.o \ utils.o \ + vlan.o \ + vlan_ifconfig.o \ vlan_init.o \ wmm.o \ wnm_ap.o \ diff --git a/src/ap/acs.c b/src/ap/acs.c index 5e8380535854f..6d4c0416dd425 100644 --- a/src/ap/acs.c +++ b/src/ap/acs.c @@ -260,7 +260,7 @@ static void acs_clean_chan_surveys(struct hostapd_channel_data *chan) } -static void acs_cleanup(struct hostapd_iface *iface) +void acs_cleanup(struct hostapd_iface *iface) { int i; struct hostapd_channel_data *chan; @@ -314,7 +314,7 @@ acs_survey_interference_factor(struct freq_survey *survey, s8 min_nf) /* TODO: figure out the best multiplier for noise floor base */ factor = pow(10, survey->nf / 5.0L) + - (busy / total) * + (total ? (busy / total) : 0) * pow(2, pow(10, (long double) survey->nf / 10.0L) - pow(10, (long double) min_nf / 10.0L)); @@ -331,10 +331,8 @@ acs_survey_chan_interference_factor(struct hostapd_iface *iface, long double int_factor = 0; unsigned count = 0; - if (dl_list_empty(&chan->survey_list)) - return; - - if (chan->flag & HOSTAPD_CHAN_DISABLED) + if (dl_list_empty(&chan->survey_list) || + (chan->flag & HOSTAPD_CHAN_DISABLED)) return; chan->interference_factor = 0; @@ -359,9 +357,8 @@ acs_survey_chan_interference_factor(struct hostapd_iface *iface, (unsigned long) survey->channel_time_rx); } - if (!count) - return; - chan->interference_factor /= count; + if (count) + chan->interference_factor /= count; } @@ -450,13 +447,9 @@ static int acs_surveys_are_sufficient(struct hostapd_iface *iface) for (i = 0; i < iface->current_mode->num_channels; i++) { chan = &iface->current_mode->channels[i]; - if (chan->flag & HOSTAPD_CHAN_DISABLED) - continue; - - if (!acs_survey_list_is_sufficient(chan)) - continue; - - valid++; + if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && + acs_survey_list_is_sufficient(chan)) + valid++; } /* We need at least survey data for one channel */ @@ -466,13 +459,9 @@ static int acs_surveys_are_sufficient(struct hostapd_iface *iface) static int acs_usable_chan(struct hostapd_channel_data *chan) { - if (dl_list_empty(&chan->survey_list)) - return 0; - if (chan->flag & HOSTAPD_CHAN_DISABLED) - return 0; - if (!acs_survey_list_is_sufficient(chan)) - return 0; - return 1; + return !dl_list_empty(&chan->survey_list) && + !(chan->flag & HOSTAPD_CHAN_DISABLED) && + acs_survey_list_is_sufficient(chan); } @@ -788,10 +777,7 @@ static int acs_study_survey_based(struct hostapd_iface *iface) static int acs_study_options(struct hostapd_iface *iface) { - int err; - - err = acs_study_survey_based(iface); - if (err == 0) + if (acs_study_survey_based(iface) == 0) return 0; /* TODO: If no surveys are available/sufficient this is a good @@ -920,14 +906,11 @@ static int acs_request_scan(struct hostapd_iface *iface) enum hostapd_chan_status acs_init(struct hostapd_iface *iface) { - int err; - wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit"); if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) { wpa_printf(MSG_INFO, "ACS: Offloading to driver"); - err = hostapd_drv_do_acs(iface->bss[0]); - if (err) + if (hostapd_drv_do_acs(iface->bss[0])) return HOSTAPD_CHAN_INVALID; return HOSTAPD_CHAN_ACS; } @@ -937,8 +920,7 @@ enum hostapd_chan_status acs_init(struct hostapd_iface *iface) acs_cleanup(iface); - err = acs_request_scan(iface); - if (err < 0) + if (acs_request_scan(iface) < 0) return HOSTAPD_CHAN_INVALID; hostapd_set_state(iface, HAPD_IFACE_ACS); diff --git a/src/ap/acs.h b/src/ap/acs.h index fc85259e85d53..ec84f0ee97f34 100644 --- a/src/ap/acs.h +++ b/src/ap/acs.h @@ -13,6 +13,7 @@ #ifdef CONFIG_ACS enum hostapd_chan_status acs_init(struct hostapd_iface *iface); +void acs_cleanup(struct hostapd_iface *iface); #else /* CONFIG_ACS */ @@ -22,6 +23,10 @@ static inline enum hostapd_chan_status acs_init(struct hostapd_iface *iface) return HOSTAPD_CHAN_INVALID; } +static inline void acs_cleanup(struct hostapd_iface *iface) +{ +} + #endif /* CONFIG_ACS */ #endif /* ACS_H */ diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 228de2baf9462..f9b6f295927f9 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -10,9 +10,11 @@ #include "utils/common.h" #include "crypto/sha1.h" +#include "crypto/tls.h" #include "radius/radius_client.h" #include "common/ieee802_11_defs.h" #include "common/eapol_common.h" +#include "common/dhcp.h" #include "eap_common/eap_wsc_common.h" #include "eap_server/eap.h" #include "wpa_auth.h" @@ -36,6 +38,10 @@ static void hostapd_config_free_vlan(struct hostapd_bss_config *bss) } +#ifndef DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES +#define DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES 0 +#endif /* DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES */ + void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) { dl_list_init(&bss->anqp_elem); @@ -55,6 +61,10 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) bss->wpa_group_rekey = 600; bss->wpa_gmk_rekey = 86400; + bss->wpa_group_update_count = 4; + bss->wpa_pairwise_update_count = 4; + bss->wpa_disable_eapol_key_retries = + DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES; bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; bss->wpa_pairwise = WPA_CIPHER_TKIP; bss->wpa_group = WPA_CIPHER_TKIP; @@ -88,13 +98,39 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) /* Set to -1 as defaults depends on HT in setup */ bss->wmm_enabled = -1; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP bss->ft_over_ds = 1; -#endif /* CONFIG_IEEE80211R */ + bss->rkh_pos_timeout = 86400; + bss->rkh_neg_timeout = 60; + bss->rkh_pull_timeout = 1000; + bss->rkh_pull_retries = 4; + bss->r0_key_lifetime = 1209600; +#endif /* CONFIG_IEEE80211R_AP */ bss->radius_das_time_window = 300; bss->sae_anti_clogging_threshold = 5; + bss->sae_sync = 5; + + bss->gas_frag_limit = 1400; + +#ifdef CONFIG_FILS + dl_list_init(&bss->fils_realms); + bss->fils_hlp_wait_time = 30; + bss->dhcp_server_port = DHCP_SERVER_PORT; + bss->dhcp_relay_port = DHCP_SERVER_PORT; +#endif /* CONFIG_FILS */ + + bss->broadcast_deauth = 1; + +#ifdef CONFIG_MBO + bss->mbo_cell_data_conn_pref = -1; +#endif /* CONFIG_MBO */ + + /* Disable TLS v1.3 by default for now to avoid interoperability issue. + * This can be enabled by default once the implementation has been fully + * completed and tested with other implementations. */ + bss->tls_flags = TLS_CONN_DISABLE_TLSv1_3; } @@ -192,6 +228,11 @@ struct hostapd_config * hostapd_config_defaults(void) conf->acs_num_scans = 5; #endif /* CONFIG_ACS */ + /* The third octet of the country string uses an ASCII space character + * by default to indicate that the regulations encompass all + * environments for the current frequency band in the country. */ + conf->country[2] = ' '; + return conf; } @@ -329,13 +370,7 @@ int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf) ssid->wpa_psk->group = 1; } - if (ssid->wpa_psk_file) { - if (hostapd_config_read_wpa_psk(ssid->wpa_psk_file, - &conf->ssid)) - return -1; - } - - return 0; + return hostapd_config_read_wpa_psk(ssid->wpa_psk_file, &conf->ssid); } @@ -380,10 +415,23 @@ void hostapd_config_free_eap_user(struct hostapd_eap_user *user) hostapd_config_free_radius_attr(user->accept_attr); os_free(user->identity); bin_clear_free(user->password, user->password_len); + bin_clear_free(user->salt, user->salt_len); os_free(user); } +void hostapd_config_free_eap_users(struct hostapd_eap_user *user) +{ + struct hostapd_eap_user *prev_user; + + while (user) { + prev_user = user; + user = user->next; + hostapd_config_free_eap_user(prev_user); + } +} + + static void hostapd_config_free_wep(struct hostapd_wep_keys *keys) { int i; @@ -420,10 +468,38 @@ static void hostapd_config_free_anqp_elem(struct hostapd_bss_config *conf) } -void hostapd_config_free_bss(struct hostapd_bss_config *conf) +static void hostapd_config_free_fils_realms(struct hostapd_bss_config *conf) { - struct hostapd_eap_user *user, *prev_user; +#ifdef CONFIG_FILS + struct fils_realm *realm; + while ((realm = dl_list_first(&conf->fils_realms, struct fils_realm, + list))) { + dl_list_del(&realm->list); + os_free(realm); + } +#endif /* CONFIG_FILS */ +} + + +static void hostapd_config_free_sae_passwords(struct hostapd_bss_config *conf) +{ + struct sae_password_entry *pw, *tmp; + + pw = conf->sae_passwords; + conf->sae_passwords = NULL; + while (pw) { + tmp = pw; + pw = pw->next; + str_clear_free(tmp->password); + os_free(tmp->identifier); + os_free(tmp); + } +} + + +void hostapd_config_free_bss(struct hostapd_bss_config *conf) +{ if (conf == NULL) return; @@ -436,12 +512,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->ssid.vlan_tagged_interface); #endif /* CONFIG_FULL_DYNAMIC_VLAN */ - user = conf->eap_user; - while (user) { - prev_user = user; - user = user->next; - hostapd_config_free_eap_user(prev_user); - } + hostapd_config_free_eap_users(conf->eap_user); os_free(conf->eap_user_sqlite); os_free(conf->eap_req_id_text); @@ -477,7 +548,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) hostapd_config_free_vlan(conf); os_free(conf->time_zone); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP { struct ft_remote_r0kh *r0kh, *r0kh_prev; struct ft_remote_r1kh *r1kh, *r1kh_prev; @@ -498,7 +569,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(r1kh_prev); } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_WPS os_free(conf->wps_pin_requests); @@ -530,6 +601,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(conf->roaming_consortium); os_free(conf->venue_name); + os_free(conf->venue_url); os_free(conf->nai_realm_data); os_free(conf->network_auth_type); os_free(conf->anqp_3gpp_cell_net); @@ -559,17 +631,30 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) os_free(p->icons[j]); os_free(p->icons); os_free(p->osu_nai); + os_free(p->osu_nai2); os_free(p->service_desc); } os_free(conf->hs20_osu_providers); } + if (conf->hs20_operator_icon) { + size_t i; + + for (i = 0; i < conf->hs20_operator_icon_count; i++) + os_free(conf->hs20_operator_icon[i]); + os_free(conf->hs20_operator_icon); + } os_free(conf->subscr_remediation_url); + os_free(conf->t_c_filename); + os_free(conf->t_c_server_url); #endif /* CONFIG_HS20 */ wpabuf_free(conf->vendor_elements); wpabuf_free(conf->assocresp_elements); os_free(conf->sae_groups); +#ifdef CONFIG_OWE + os_free(conf->owe_groups); +#endif /* CONFIG_OWE */ os_free(conf->wowlan_triggers); @@ -577,11 +662,22 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf) #ifdef CONFIG_TESTING_OPTIONS wpabuf_free(conf->own_ie_override); + wpabuf_free(conf->sae_commit_override); #endif /* CONFIG_TESTING_OPTIONS */ os_free(conf->no_probe_resp_if_seen_on); os_free(conf->no_auth_if_seen_on); + hostapd_config_free_fils_realms(conf); + +#ifdef CONFIG_DPP + os_free(conf->dpp_connector); + wpabuf_free(conf->dpp_netaccesskey); + wpabuf_free(conf->dpp_csign); +#endif /* CONFIG_DPP */ + + hostapd_config_free_sae_passwords(conf); + os_free(conf); } @@ -802,7 +898,7 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, } } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) && (bss->nas_identifier == NULL || os_strlen(bss->nas_identifier) < 1 || @@ -812,7 +908,7 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, "string"); return -1; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211N if (full_config && conf->ieee80211n && @@ -848,6 +944,16 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, wpa_printf(MSG_ERROR, "VHT (IEEE 802.11ac) with WEP is not allowed, disabling VHT capabilities"); } + + if (full_config && conf->ieee80211ac && bss->wpa && + !(bss->wpa_pairwise & WPA_CIPHER_CCMP) && + !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | + WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256))) + { + bss->disable_11ac = 1; + wpa_printf(MSG_ERROR, + "VHT (IEEE 802.11ac) with WPA/WPA2 requires CCMP/GCMP to be enabled, disabling VHT capabilities"); + } #endif /* CONFIG_IEEE80211AC */ #ifdef CONFIG_WPS @@ -866,7 +972,9 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss, if (full_config && bss->wps_state && bss->wpa && (!(bss->wpa & 2) || - !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)))) { + !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | + WPA_CIPHER_CCMP_256 | + WPA_CIPHER_GCMP_256)))) { wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without " "WPA2/CCMP/GCMP forced WPS to be disabled"); bss->wps_state = 0; @@ -976,8 +1084,15 @@ void hostapd_set_security_params(struct hostapd_bss_config *bss, if ((bss->wpa & 2) && bss->rsn_pairwise == 0) bss->rsn_pairwise = bss->wpa_pairwise; - bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise, - bss->rsn_pairwise); + if (bss->group_cipher) + bss->wpa_group = bss->group_cipher; + else + bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, + bss->wpa_pairwise, + bss->rsn_pairwise); + if (!bss->wpa_group_rekey_set) + bss->wpa_group_rekey = bss->wpa_group == WPA_CIPHER_TKIP ? + 600 : 86400; if (full_config) { bss->radius->auth_server = bss->radius->auth_servers; diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 8c8f7e286bdae..778366d49afe0 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -160,6 +160,8 @@ struct hostapd_eap_user { } methods[EAP_MAX_METHODS]; u8 *password; size_t password_len; + u8 *salt; + size_t salt_len; /* non-zero when password is salted */ int phase2; int force_version; unsigned int wildcard_prefix:1; @@ -169,6 +171,7 @@ struct hostapd_eap_user { unsigned int macacl:1; int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */ struct hostapd_radius_attr *accept_attr; + u32 t_c_timestamp; }; struct hostapd_radius_attr { @@ -201,6 +204,12 @@ struct hostapd_lang_string { u8 name[252]; }; +struct hostapd_venue_url { + u8 venue_number; + u8 url_len; + u8 url[254]; +}; + #define MAX_NAI_REALMS 10 #define MAX_NAI_REALMLEN 255 #define MAX_NAI_EAP_METHODS 5 @@ -224,6 +233,18 @@ struct anqp_element { struct wpabuf *payload; }; +struct fils_realm { + struct dl_list list; + u8 hash[2]; + char realm[]; +}; + +struct sae_password_entry { + struct sae_password_entry *next; + char *password; + char *identifier; + u8 peer_addr[ETH_ALEN]; +}; /** * struct hostapd_bss_config - Per-BSS configuration @@ -242,7 +263,8 @@ struct hostapd_bss_config { int max_num_sta; /* maximum number of STAs in station table */ int dtim_period; - int bss_load_update_period; + unsigned int bss_load_update_period; + unsigned int chan_util_avg_period; int ieee802_1x; /* use IEEE 802.1X */ int eapol_version; @@ -287,7 +309,7 @@ struct hostapd_bss_config { char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast * frames */ - enum { + enum macaddr_acl { ACCEPT_UNLESS_DENIED = 0, DENY_UNLESS_ACCEPTED = 1, USE_EXTERNAL_RADIUS_AUTH = 2 @@ -319,27 +341,37 @@ struct hostapd_bss_config { PSK_RADIUS_REQUIRED = 2 } wpa_psk_radius; int wpa_pairwise; + int group_cipher; /* wpa_group value override from configuation */ int wpa_group; int wpa_group_rekey; + int wpa_group_rekey_set; int wpa_strict_rekey; int wpa_gmk_rekey; int wpa_ptk_rekey; + u32 wpa_group_update_count; + u32 wpa_pairwise_update_count; + int wpa_disable_eapol_key_retries; int rsn_pairwise; int rsn_preauth; char *rsn_preauth_interfaces; - int peerkey; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP /* IEEE 802.11r - Fast BSS Transition */ u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 r1_key_holder[FT_R1KH_ID_LEN]; - u32 r0_key_lifetime; + u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */ + int rkh_pos_timeout; + int rkh_neg_timeout; + int rkh_pull_timeout; /* ms */ + int rkh_pull_retries; u32 reassociation_deadline; struct ft_remote_r0kh *r0kh_list; struct ft_remote_r1kh *r1kh_list; int pmk_r1_push; int ft_over_ds; -#endif /* CONFIG_IEEE80211R */ + int ft_psk_generate_local; + int r1_max_key_lifetime; +#endif /* CONFIG_IEEE80211R_AP */ char *ctrl_interface; /* directory for UNIX domain sockets */ #ifndef CONFIG_NATIVE_WINDOWS @@ -353,6 +385,7 @@ struct hostapd_bss_config { char *private_key_passwd; int check_crl; unsigned int tls_session_lifetime; + unsigned int tls_flags; char *ocsp_stapling_response; char *ocsp_stapling_response_multi; char *dh_file; @@ -464,6 +497,7 @@ struct hostapd_bss_config { int time_advertisement; char *time_zone; int wnm_sleep_mode; + int wnm_sleep_mode_no_keys; int bss_transition; /* IEEE 802.11u - Interworking */ @@ -486,6 +520,10 @@ struct hostapd_bss_config { unsigned int venue_name_count; struct hostapd_lang_string *venue_name; + /* Venue URL duples */ + unsigned int venue_url_count; + struct hostapd_venue_url *venue_url; + /* IEEE 802.11u - Network Authentication Type */ u8 *network_auth_type; size_t network_auth_type_len; @@ -508,7 +546,7 @@ struct hostapd_bss_config { struct dl_list anqp_elem; /* list of struct anqp_element */ u16 gas_comeback_delay; - int gas_frag_limit; + size_t gas_frag_limit; int gas_address3; u8 qos_map_set[16 + 2 * 21]; @@ -547,13 +585,20 @@ struct hostapd_bss_config { char **icons; size_t icons_count; char *osu_nai; + char *osu_nai2; unsigned int service_desc_count; struct hostapd_lang_string *service_desc; } *hs20_osu_providers, *last_osu; size_t hs20_osu_providers_count; + size_t hs20_osu_providers_nai_count; + char **hs20_operator_icon; + size_t hs20_operator_icon_count; unsigned int hs20_deauth_req_timeout; char *subscr_remediation_url; u8 subscr_remediation_method; + char *t_c_filename; + u32 t_c_timestamp; + char *t_c_server_url; #endif /* CONFIG_HS20 */ u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */ @@ -566,7 +611,10 @@ struct hostapd_bss_config { struct wpabuf *assocresp_elements; unsigned int sae_anti_clogging_threshold; + unsigned int sae_sync; + int sae_require_mfp; int *sae_groups; + struct sae_password_entry *sae_passwords; char *wowlan_triggers; /* Wake-on-WLAN triggers */ @@ -574,6 +622,8 @@ struct hostapd_bss_config { u8 bss_load_test[5]; u8 bss_load_test_set; struct wpabuf *own_ie_override; + int sae_reflection_attack; + struct wpabuf *sae_commit_override; #endif /* CONFIG_TESTING_OPTIONS */ #define MESH_ENABLED BIT(0) @@ -591,12 +641,71 @@ struct hostapd_bss_config { #ifdef CONFIG_MBO int mbo_enabled; + /** + * oce - Enable OCE in AP and/or STA-CFON mode + * - BIT(0) is Reserved + * - Set BIT(1) to enable OCE in STA-CFON mode + * - Set BIT(2) to enable OCE in AP mode + */ + unsigned int oce; + int mbo_cell_data_conn_pref; #endif /* CONFIG_MBO */ int ftm_responder; int ftm_initiator; + +#ifdef CONFIG_FILS + u8 fils_cache_id[FILS_CACHE_ID_LEN]; + int fils_cache_id_set; + struct dl_list fils_realms; /* list of struct fils_realm */ + int fils_dh_group; + struct hostapd_ip_addr dhcp_server; + int dhcp_rapid_commit_proxy; + unsigned int fils_hlp_wait_time; + u16 dhcp_server_port; + u16 dhcp_relay_port; +#endif /* CONFIG_FILS */ + + int multicast_to_unicast; + + int broadcast_deauth; + +#ifdef CONFIG_DPP + char *dpp_connector; + struct wpabuf *dpp_netaccesskey; + unsigned int dpp_netaccesskey_expiry; + struct wpabuf *dpp_csign; +#endif /* CONFIG_DPP */ + +#ifdef CONFIG_OWE + macaddr owe_transition_bssid; + u8 owe_transition_ssid[SSID_MAX_LEN]; + size_t owe_transition_ssid_len; + char owe_transition_ifname[IFNAMSIZ + 1]; + int *owe_groups; +#endif /* CONFIG_OWE */ + + int coloc_intf_reporting; +}; + +/** + * struct he_phy_capabilities_info - HE PHY capabilities + */ +struct he_phy_capabilities_info { + Boolean he_su_beamformer; + Boolean he_su_beamformee; + Boolean he_mu_beamformer; }; +/** + * struct he_operation - HE operation + */ +struct he_operation { + u8 he_bss_color; + u8 he_default_pe_duration; + u8 he_twt_required; + u8 he_rts_threshold; +}; /** * struct hostapd_config - Per-radio interface configuration @@ -612,6 +721,7 @@ struct hostapd_config { u8 channel; u8 acs; struct wpa_freq_range_list acs_ch_list; + int acs_exclude_dfs; enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */ enum { LONG_PREAMBLE = 0, @@ -620,6 +730,8 @@ struct hostapd_config { int *supported_rates; int *basic_rates; + unsigned int beacon_rate; + enum beacon_rate_type rate_type; const struct wpa_driver_ops *driver; char *driver_params; @@ -635,6 +747,9 @@ struct hostapd_config { * ' ' (ascii 32): all environments * 'O': Outdoor environemnt only * 'I': Indoor environment only + * 'X': Used with noncountry entity ("XXX") + * 0x00..0x31: identifying IEEE 802.11 standard + * Annex E table (0x04 = global table) */ int ieee80211d; @@ -675,6 +790,7 @@ struct hostapd_config { u8 vht_oper_chwidth; u8 vht_oper_centr_freq_seg0_idx; u8 vht_oper_centr_freq_seg1_idx; + u8 ht40_plus_minus_allowed; /* Use driver-generated interface addresses when adding multiple BSSs */ u8 use_driver_iface_addr; @@ -707,6 +823,18 @@ struct hostapd_config { struct wpabuf *lci; struct wpabuf *civic; + int stationary_ap; + + int ieee80211ax; +#ifdef CONFIG_IEEE80211AX + struct he_phy_capabilities_info he_phy_capab; + struct he_operation he_op; +#endif /* CONFIG_IEEE80211AX */ + + /* VHT enable/disable config from CHAN_SWITCH */ +#define CH_SWITCH_VHT_ENABLED BIT(0) +#define CH_SWITCH_VHT_DISABLED BIT(1) + unsigned int ch_switch_vht_config; }; @@ -714,6 +842,7 @@ int hostapd_mac_comp(const void *a, const void *b); struct hostapd_config * hostapd_config_defaults(void); void hostapd_config_defaults_bss(struct hostapd_bss_config *bss); void hostapd_config_free_eap_user(struct hostapd_eap_user *user); +void hostapd_config_free_eap_users(struct hostapd_eap_user *user); void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p); void hostapd_config_free_bss(struct hostapd_bss_config *conf); void hostapd_config_free(struct hostapd_config *conf); diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c index f1394654d3a8e..067cf863e84c7 100644 --- a/src/ap/ap_drv_ops.c +++ b/src/ap/ap_drv_ops.c @@ -19,6 +19,7 @@ #include "ap_config.h" #include "p2p_hostapd.h" #include "hs20.h" +#include "wpa_auth.h" #include "ap_drv_ops.h" @@ -99,6 +100,13 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, goto fail; #endif /* CONFIG_FST */ +#ifdef CONFIG_FILS + pos = hostapd_eid_fils_indic(hapd, buf, 0); + if (add_buf_data(&beacon, buf, pos - buf) < 0 || + add_buf_data(&proberesp, buf, pos - buf) < 0) + goto fail; +#endif /* CONFIG_FILS */ + if (add_buf(&beacon, hapd->wps_beacon_ie) < 0 || add_buf(&proberesp, hapd->wps_probe_resp_ie) < 0) goto fail; @@ -168,7 +176,8 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, #endif /* CONFIG_HS20 */ #ifdef CONFIG_MBO - if (hapd->conf->mbo_enabled) { + if (hapd->conf->mbo_enabled || + OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) { pos = hostapd_eid_mbo(hapd, buf, sizeof(buf)); if (add_buf_data(&beacon, buf, pos - buf) < 0 || add_buf_data(&proberesp, buf, pos - buf) < 0 || @@ -177,6 +186,13 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd, } #endif /* CONFIG_MBO */ +#ifdef CONFIG_OWE + pos = hostapd_eid_owe_trans(hapd, buf, sizeof(buf)); + if (add_buf_data(&beacon, buf, pos - buf) < 0 || + add_buf_data(&proberesp, buf, pos - buf) < 0) + goto fail; +#endif /* CONFIG_OWE */ + add_buf(&beacon, hapd->conf->vendor_elements); add_buf(&proberesp, hapd->conf->vendor_elements); add_buf(&assocresp, hapd->conf->assocresp_elements); @@ -340,10 +356,44 @@ int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, u16 seq, u16 status, const u8 *ie, size_t len) { + struct wpa_driver_sta_auth_params params; +#ifdef CONFIG_FILS + struct sta_info *sta; +#endif /* CONFIG_FILS */ + if (hapd->driver == NULL || hapd->driver->sta_auth == NULL) return 0; - return hapd->driver->sta_auth(hapd->drv_priv, hapd->own_addr, addr, - seq, status, ie, len); + + os_memset(¶ms, 0, sizeof(params)); + +#ifdef CONFIG_FILS + sta = ap_get_sta(hapd, addr); + if (!sta) { + wpa_printf(MSG_DEBUG, "Station " MACSTR + " not found for sta_auth processing", + MAC2STR(addr)); + return 0; + } + + if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) { + params.fils_auth = 1; + wpa_auth_get_fils_aead_params(sta->wpa_sm, params.fils_anonce, + params.fils_snonce, + params.fils_kek, + ¶ms.fils_kek_len); + } +#endif /* CONFIG_FILS */ + + params.own_addr = hapd->own_addr; + params.addr = addr; + params.seq = seq; + params.status = status; + params.ie = ie; + params.len = len; + + return hapd->driver->sta_auth(hapd->drv_priv, ¶ms); } @@ -554,13 +604,13 @@ int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, struct hostapd_hw_modes * hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, - u16 *flags) + u16 *flags, u8 *dfs_domain) { if (hapd->driver == NULL || hapd->driver->get_hw_feature_data == NULL) return NULL; return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes, - flags); + flags, dfs_domain); } @@ -694,6 +744,15 @@ int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq, sta = ap_get_sta(hapd, dst); if (!sta || !(sta->flags & WLAN_STA_ASSOC)) bssid = wildcard_bssid; + } else if (is_broadcast_ether_addr(dst) && + len > 0 && data[0] == WLAN_ACTION_PUBLIC) { + /* + * The only current use case of Public Action frames with + * broadcast destination address is DPP PKEX. That case is + * directing all devices and not just the STAs within the BSS, + * so have to use the wildcard BSSID value. + */ + bssid = wildcard_bssid; } return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst, hapd->own_addr, bssid, data, len, 0); @@ -774,7 +833,9 @@ static void hostapd_get_hw_mode_any_channels(struct hostapd_data *hapd, if ((acs_ch_list_all || freq_range_list_includes(&hapd->iface->conf->acs_ch_list, chan->chan)) && - !(chan->flag & HOSTAPD_CHAN_DISABLED)) + !(chan->flag & HOSTAPD_CHAN_DISABLED) && + !(hapd->iface->conf->acs_exclude_dfs && + (chan->flag & HOSTAPD_CHAN_RADAR))) int_array_add_unique(freq_list, chan->freq); } } @@ -829,6 +890,9 @@ int hostapd_drv_do_acs(struct hostapd_data *hapd) &hapd->iface->conf->acs_ch_list, chan->chan)) continue; + if (hapd->iface->conf->acs_exclude_dfs && + (chan->flag & HOSTAPD_CHAN_RADAR)) + continue; if (!(chan->flag & HOSTAPD_CHAN_DISABLED)) { channels[num_channels++] = chan->chan; int_array_add_unique(&freq_list, chan->freq); diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h index 0bb7954ec061f..db93fde7d6e3a 100644 --- a/src/ap/ap_drv_ops.h +++ b/src/ap/ap_drv_ops.h @@ -72,7 +72,7 @@ int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, int cw_min, int cw_max, int burst_time); struct hostapd_hw_modes * hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, - u16 *flags); + u16 *flags, u8 *dfs_domain); int hostapd_driver_commit(struct hostapd_data *hapd); int hostapd_drv_none(struct hostapd_data *hapd); int hostapd_driver_scan(struct hostapd_data *hapd, @@ -103,6 +103,14 @@ int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *data, size_t len); +static inline void +hostapd_drv_send_action_cancel_wait(struct hostapd_data *hapd) +{ + if (!hapd->driver || !hapd->driver->send_action_cancel_wait || + !hapd->drv_priv) + return; + hapd->driver->send_action_cancel_wait(hapd->drv_priv); +} int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr, u16 auth_alg); int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr, @@ -274,8 +282,9 @@ static inline const char * hostapd_drv_get_radio_name(struct hostapd_data *hapd) static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd, struct csa_settings *settings) { - if (hapd->driver == NULL || hapd->driver->switch_channel == NULL) - return -ENOTSUP; + if (hapd->driver == NULL || hapd->driver->switch_channel == NULL || + hapd->drv_priv == NULL) + return -1; return hapd->driver->switch_channel(hapd->drv_priv, settings); } diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c index e7308a01d7432..db8a26759c286 100644 --- a/src/ap/ap_mlme.c +++ b/src/ap/ap_mlme.c @@ -57,7 +57,11 @@ void mlme_authenticate_indication(struct hostapd_data *hapd, HOSTAPD_LEVEL_DEBUG, "MLME-AUTHENTICATE.indication(" MACSTR ", %s)", MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg)); - if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP)) + if (sta->auth_alg != WLAN_AUTH_FT && + sta->auth_alg != WLAN_AUTH_FILS_SK && + sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && + sta->auth_alg != WLAN_AUTH_FILS_PK && + !(sta->flags & WLAN_STA_MFP)) mlme_deletekeys_request(hapd, sta); ap_sta_clear_disconnect_timeouts(hapd, sta); } @@ -105,7 +109,10 @@ void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta) HOSTAPD_LEVEL_DEBUG, "MLME-ASSOCIATE.indication(" MACSTR ")", MAC2STR(sta->addr)); - if (sta->auth_alg != WLAN_AUTH_FT) + if (sta->auth_alg != WLAN_AUTH_FT && + sta->auth_alg != WLAN_AUTH_FILS_SK && + sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && + sta->auth_alg != WLAN_AUTH_FILS_PK) mlme_deletekeys_request(hapd, sta); ap_sta_clear_disconnect_timeouts(hapd, sta); } @@ -130,7 +137,10 @@ void mlme_reassociate_indication(struct hostapd_data *hapd, HOSTAPD_LEVEL_DEBUG, "MLME-REASSOCIATE.indication(" MACSTR ")", MAC2STR(sta->addr)); - if (sta->auth_alg != WLAN_AUTH_FT) + if (sta->auth_alg != WLAN_AUTH_FT && + sta->auth_alg != WLAN_AUTH_FILS_SK && + sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && + sta->auth_alg != WLAN_AUTH_FILS_PK) mlme_deletekeys_request(hapd, sta); ap_sta_clear_disconnect_timeouts(hapd, sta); } diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c index cdb49cdd9d32f..95d004ed2b16a 100644 --- a/src/ap/authsrv.c +++ b/src/ap/authsrv.c @@ -71,19 +71,26 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, } if (eap_user->password) { - user->password = os_malloc(eap_user->password_len); + user->password = os_memdup(eap_user->password, + eap_user->password_len); if (user->password == NULL) goto out; - os_memcpy(user->password, eap_user->password, - eap_user->password_len); user->password_len = eap_user->password_len; user->password_hash = eap_user->password_hash; + if (eap_user->salt && eap_user->salt_len) { + user->salt = os_memdup(eap_user->salt, + eap_user->salt_len); + if (!user->salt) + goto out; + user->salt_len = eap_user->salt_len; + } } user->force_version = eap_user->force_version; user->macacl = eap_user->macacl; user->ttls_auth = eap_user->ttls_auth; user->remediation = eap_user->remediation; user->accept_attr = eap_user->accept_attr; + user->t_c_timestamp = eap_user->t_c_timestamp; rv = 0; out: @@ -129,10 +136,12 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) #ifdef CONFIG_HS20 srv.subscr_remediation_url = conf->subscr_remediation_url; srv.subscr_remediation_method = conf->subscr_remediation_method; + srv.t_c_server_url = conf->t_c_server_url; #endif /* CONFIG_HS20 */ srv.erp = conf->eap_server_erp; srv.erp_domain = conf->erp_domain; srv.tls_session_lifetime = conf->tls_session_lifetime; + srv.tls_flags = conf->tls_flags; hapd->radius_srv = radius_server_init(&srv); if (hapd->radius_srv == NULL) { @@ -146,6 +155,40 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd) #endif /* RADIUS_SERVER */ +#ifdef EAP_TLS_FUNCS +static void authsrv_tls_event(void *ctx, enum tls_event ev, + union tls_event_data *data) +{ + switch (ev) { + case TLS_CERT_CHAIN_SUCCESS: + wpa_printf(MSG_DEBUG, "authsrv: remote certificate verification success"); + break; + case TLS_CERT_CHAIN_FAILURE: + wpa_printf(MSG_INFO, "authsrv: certificate chain failure: reason=%d depth=%d subject='%s' err='%s'", + data->cert_fail.reason, + data->cert_fail.depth, + data->cert_fail.subject, + data->cert_fail.reason_txt); + break; + case TLS_PEER_CERTIFICATE: + wpa_printf(MSG_DEBUG, "authsrv: peer certificate: depth=%d serial_num=%s subject=%s", + data->peer_cert.depth, + data->peer_cert.serial_num ? data->peer_cert.serial_num : "N/A", + data->peer_cert.subject); + break; + case TLS_ALERT: + if (data->alert.is_local) + wpa_printf(MSG_DEBUG, "authsrv: local TLS alert: %s", + data->alert.description); + else + wpa_printf(MSG_DEBUG, "authsrv: remote TLS alert: %s", + data->alert.description); + break; + } +} +#endif /* EAP_TLS_FUNCS */ + + int authsrv_init(struct hostapd_data *hapd) { #ifdef EAP_TLS_FUNCS @@ -157,6 +200,9 @@ int authsrv_init(struct hostapd_data *hapd) os_memset(&conf, 0, sizeof(conf)); conf.tls_session_lifetime = hapd->conf->tls_session_lifetime; + conf.tls_flags = hapd->conf->tls_flags; + conf.event_cb = authsrv_tls_event; + conf.cb_ctx = hapd; hapd->ssl_ctx = tls_init(&conf); if (hapd->ssl_ctx == NULL) { wpa_printf(MSG_ERROR, "Failed to initialize TLS"); diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 233320d2e978c..59bd4af395d7a 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -16,6 +16,7 @@ #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" #include "common/hw_features_common.h" +#include "common/wpa_ctrl.h" #include "wps/wps_defs.h" #include "p2p/p2p.h" #include "hostapd.h" @@ -30,6 +31,7 @@ #include "hs20.h" #include "dfs.h" #include "taxonomy.h" +#include "ieee802_11_auth.h" #ifdef NEED_AP_MLME @@ -392,7 +394,15 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, 2 + sizeof(struct ieee80211_vht_operation); } +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax) { + buflen += 3 + sizeof(struct ieee80211_he_capabilities) + + 3 + sizeof(struct ieee80211_he_operation); + } +#endif /* CONFIG_IEEE80211AX */ + buflen += hostapd_mbo_ie_len(hapd); + buflen += hostapd_eid_owe_trans_len(hapd); resp = os_zalloc(buflen); if (resp == NULL) @@ -443,8 +453,9 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, /* Extended supported rates */ pos = hostapd_eid_ext_supp_rates(hapd, pos); - /* RSN, MDIE, WPA */ - pos = hostapd_eid_wpa(hapd, pos, epos - pos); + /* RSN, MDIE */ + if (hapd->conf->wpa != WPA_PROTO_WPA) + pos = hostapd_eid_wpa(hapd, pos, epos - pos); pos = hostapd_eid_bss_load(hapd, pos, epos - pos); @@ -491,10 +502,26 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, pos = hostapd_eid_txpower_envelope(hapd, pos); pos = hostapd_eid_wb_chsw_wrapper(hapd, pos); } +#endif /* CONFIG_IEEE80211AC */ + + pos = hostapd_eid_fils_indic(hapd, pos, 0); + +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax) { + pos = hostapd_eid_he_capab(hapd, pos); + pos = hostapd_eid_he_operation(hapd, pos); + } +#endif /* CONFIG_IEEE80211AX */ + +#ifdef CONFIG_IEEE80211AC if (hapd->conf->vendor_vht) pos = hostapd_eid_vendor_vht(hapd, pos); #endif /* CONFIG_IEEE80211AC */ + /* WPA */ + if (hapd->conf->wpa == WPA_PROTO_WPA) + pos = hostapd_eid_wpa(hapd, pos, epos - pos); + /* Wi-Fi Alliance WMM */ pos = hostapd_eid_wmm(hapd, pos); @@ -526,6 +553,7 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, #endif /* CONFIG_HS20 */ pos = hostapd_eid_mbo(hapd, pos, (u8 *) resp + buflen - pos); + pos = hostapd_eid_owe_trans(hapd, pos, (u8 *) resp + buflen - pos); if (hapd->conf->vendor_elements) { os_memcpy(pos, wpabuf_head(hapd->conf->vendor_elements), @@ -618,7 +646,7 @@ static struct hostapd_sta_info * sta_track_get(struct hostapd_iface *iface, } -void sta_track_add(struct hostapd_iface *iface, const u8 *addr) +void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal) { struct hostapd_sta_info *info; @@ -628,6 +656,7 @@ void sta_track_add(struct hostapd_iface *iface, const u8 *addr) dl_list_del(&info->list); dl_list_add_tail(&iface->sta_seen, &info->list); os_get_reltime(&info->last_seen); + info->ssi_signal = ssi_signal; return; } @@ -637,6 +666,7 @@ void sta_track_add(struct hostapd_iface *iface, const u8 *addr) return; os_memcpy(info->addr, addr, ETH_ALEN); os_get_reltime(&info->last_seen); + info->ssi_signal = ssi_signal; if (iface->num_sta_seen >= iface->conf->track_sta_max_num) { /* Expire oldest entry to make room for a new one */ @@ -707,14 +737,30 @@ void handle_probe_req(struct hostapd_data *hapd, int ret; u16 csa_offs[2]; size_t csa_offs_len; + u32 session_timeout, acct_interim_interval; + struct vlan_description vlan_id; + struct hostapd_sta_wpa_psk_short *psk = NULL; + char *identity = NULL; + char *radius_cui = NULL; if (len < IEEE80211_HDRLEN) return; ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN; if (hapd->iconf->track_sta_max_num) - sta_track_add(hapd->iface, mgmt->sa); + sta_track_add(hapd->iface, mgmt->sa, ssi_signal); ie_len = len - IEEE80211_HDRLEN; + ret = ieee802_11_allowed_address(hapd, mgmt->sa, (const u8 *) mgmt, len, + &session_timeout, + &acct_interim_interval, &vlan_id, + &psk, &identity, &radius_cui, 1); + if (ret == HOSTAPD_ACL_REJECT) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Ignore Probe Request frame from " MACSTR + " due to ACL reject ", MAC2STR(mgmt->sa)); + return; + } + for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, mgmt->sa, mgmt->da, mgmt->bssid, @@ -909,6 +955,9 @@ void handle_probe_req(struct hostapd_data *hapd, } #endif /* CONFIG_TESTING_OPTIONS */ + wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, RX_PROBE_REQUEST "sa=" MACSTR + " signal=%d", MAC2STR(mgmt->sa), ssi_signal); + resp = hostapd_gen_probe_resp(hapd, mgmt, elems.p2p != NULL, &resp_len); if (resp == NULL) @@ -1033,7 +1082,15 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, } #endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax) { + tail_len += 3 + sizeof(struct ieee80211_he_capabilities) + + 3 + sizeof(struct ieee80211_he_operation); + } +#endif /* CONFIG_IEEE80211AX */ + tail_len += hostapd_mbo_ie_len(hapd); + tail_len += hostapd_eid_owe_trans_len(hapd); tailpos = tail = os_malloc(tail_len); if (head == NULL || tail == NULL) { @@ -1100,9 +1157,11 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, /* Extended supported rates */ tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos); - /* RSN, MDIE, WPA */ - tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - - tailpos); + /* RSN, MDIE */ + if (hapd->conf->wpa != WPA_PROTO_WPA) + tailpos = hostapd_eid_wpa(hapd, tailpos, + tail + BEACON_TAIL_BUF_SIZE - + tailpos); tailpos = hostapd_eid_rm_enabled_capab(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - @@ -1155,10 +1214,28 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, tailpos = hostapd_eid_txpower_envelope(hapd, tailpos); tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos); } +#endif /* CONFIG_IEEE80211AC */ + + tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0); + +#ifdef CONFIG_IEEE80211AX + if (hapd->iconf->ieee80211ax) { + tailpos = hostapd_eid_he_capab(hapd, tailpos); + tailpos = hostapd_eid_he_operation(hapd, tailpos); + } +#endif /* CONFIG_IEEE80211AX */ + +#ifdef CONFIG_IEEE80211AC if (hapd->conf->vendor_vht) tailpos = hostapd_eid_vendor_vht(hapd, tailpos); #endif /* CONFIG_IEEE80211AC */ + /* WPA */ + if (hapd->conf->wpa == WPA_PROTO_WPA) + tailpos = hostapd_eid_wpa(hapd, tailpos, + tail + BEACON_TAIL_BUF_SIZE - + tailpos); + /* Wi-Fi Alliance WMM */ tailpos = hostapd_eid_wmm(hapd, tailpos); @@ -1189,6 +1266,8 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #endif /* CONFIG_HS20 */ tailpos = hostapd_eid_mbo(hapd, tailpos, tail + tail_len - tailpos); + tailpos = hostapd_eid_owe_trans(hapd, tailpos, + tail + tail_len - tailpos); if (hapd->conf->vendor_elements) { os_memcpy(tailpos, wpabuf_head(hapd->conf->vendor_elements), @@ -1211,6 +1290,8 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, params->dtim_period = hapd->conf->dtim_period; params->beacon_int = hapd->iconf->beacon_int; params->basic_rates = hapd->iface->basic_rates; + params->beacon_rate = hapd->iconf->beacon_rate; + params->rate_type = hapd->iconf->rate_type; params->ssid = hapd->conf->ssid.ssid; params->ssid_len = hapd->conf->ssid.ssid_len; if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) == @@ -1274,6 +1355,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, params->osen = 1; } #endif /* CONFIG_HS20 */ + params->multicast_to_unicast = hapd->conf->multicast_to_unicast; params->pbss = hapd->conf->pbss; return 0; } diff --git a/src/ap/beacon.h b/src/ap/beacon.h index fc711815cf65e..a26e30879cf53 100644 --- a/src/ap/beacon.h +++ b/src/ap/beacon.h @@ -21,7 +21,7 @@ int ieee802_11_update_beacons(struct hostapd_iface *iface); int ieee802_11_build_ap_params(struct hostapd_data *hapd, struct wpa_driver_ap_params *params); void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params); -void sta_track_add(struct hostapd_iface *iface, const u8 *addr); +void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal); void sta_track_del(struct hostapd_sta_info *info); void sta_track_expire(struct hostapd_iface *iface, int force); struct hostapd_data * diff --git a/src/ap/bss_load.c b/src/ap/bss_load.c index fb639423230c0..725d3cd3469ba 100644 --- a/src/ap/bss_load.c +++ b/src/ap/bss_load.c @@ -16,11 +16,35 @@ #include "beacon.h" +static int get_bss_load_update_timeout(struct hostapd_data *hapd, + unsigned int *sec, unsigned int *usec) +{ + unsigned int update_period = hapd->conf->bss_load_update_period; + unsigned int beacon_int = hapd->iconf->beacon_int; + unsigned int update_timeout; + + if (!update_period || !beacon_int) { + wpa_printf(MSG_ERROR, + "BSS Load: Invalid BSS load update configuration (period=%u beacon_int=%u)", + update_period, beacon_int); + return -1; + } + + update_timeout = update_period * beacon_int; + + *sec = ((update_timeout / 1000) * 1024) / 1000; + *usec = (update_timeout % 1000) * 1024; + + return 0; +} + + static void update_channel_utilization(void *eloop_data, void *user_data) { struct hostapd_data *hapd = eloop_data; unsigned int sec, usec; int err; + struct hostapd_iface *iface = hapd->iface; if (!(hapd->beacon_set_done && hapd->started)) return; @@ -33,8 +57,24 @@ static void update_channel_utilization(void *eloop_data, void *user_data) ieee802_11_set_beacon(hapd); - sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000; - usec = (hapd->bss_load_update_timeout % 1000) * 1024; + if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0) + return; + + if (hapd->conf->chan_util_avg_period) { + iface->chan_util_samples_sum += iface->channel_utilization; + iface->chan_util_num_sample_periods += + hapd->conf->bss_load_update_period; + if (iface->chan_util_num_sample_periods >= + hapd->conf->chan_util_avg_period) { + iface->chan_util_average = + iface->chan_util_samples_sum / + (iface->chan_util_num_sample_periods / + hapd->conf->bss_load_update_period); + iface->chan_util_samples_sum = 0; + iface->chan_util_num_sample_periods = 0; + } + } + eloop_register_timeout(sec, usec, update_channel_utilization, hapd, NULL); } @@ -42,17 +82,11 @@ static void update_channel_utilization(void *eloop_data, void *user_data) int bss_load_update_init(struct hostapd_data *hapd) { - struct hostapd_bss_config *conf = hapd->conf; - struct hostapd_config *iconf = hapd->iconf; unsigned int sec, usec; - if (!conf->bss_load_update_period || !iconf->beacon_int) + if (get_bss_load_update_timeout(hapd, &sec, &usec) < 0) return -1; - hapd->bss_load_update_timeout = conf->bss_load_update_period * - iconf->beacon_int; - sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000; - usec = (hapd->bss_load_update_timeout % 1000) * 1024; eloop_register_timeout(sec, usec, update_channel_utilization, hapd, NULL); return 0; diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c index 3680fda3153f5..21b813ee18d77 100644 --- a/src/ap/ctrl_iface_ap.c +++ b/src/ap/ctrl_iface_ap.c @@ -26,23 +26,141 @@ #include "taxonomy.h" +static size_t hostapd_write_ht_mcs_bitmask(char *buf, size_t buflen, + size_t curr_len, const u8 *mcs_set) +{ + int ret; + size_t len = curr_len; + + ret = os_snprintf(buf + len, buflen - len, + "ht_mcs_bitmask="); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + + /* 77 first bits (+ 3 reserved bits) */ + len += wpa_snprintf_hex(buf + len, buflen - len, mcs_set, 10); + + ret = os_snprintf(buf + len, buflen - len, "\n"); + if (os_snprintf_error(buflen - len, ret)) + return curr_len; + len += ret; + + return len; +} + + static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen) { struct hostap_sta_driver_data data; int ret; + int len = 0; if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0) return 0; ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n" - "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n", + "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n" + "signal=%d\n", data.rx_packets, data.tx_packets, - data.rx_bytes, data.tx_bytes, data.inactive_msec); + data.rx_bytes, data.tx_bytes, data.inactive_msec, + data.signal); if (os_snprintf_error(buflen, ret)) return 0; - return ret; + len += ret; + + ret = os_snprintf(buf + len, buflen - len, "rx_rate_info=%lu", + data.current_rx_rate); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + if (data.flags & STA_DRV_DATA_RX_MCS) { + ret = os_snprintf(buf + len, buflen - len, " mcs %u", + data.rx_mcs); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_RX_VHT_MCS) { + ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u", + data.rx_vhtmcs); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_RX_VHT_NSS) { + ret = os_snprintf(buf + len, buflen - len, " vhtnss %u", + data.rx_vht_nss); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_RX_SHORT_GI) { + ret = os_snprintf(buf + len, buflen - len, " shortGI"); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + ret = os_snprintf(buf + len, buflen - len, "\n"); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + + ret = os_snprintf(buf + len, buflen - len, "tx_rate_info=%lu", + data.current_tx_rate); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + if (data.flags & STA_DRV_DATA_TX_MCS) { + ret = os_snprintf(buf + len, buflen - len, " mcs %u", + data.tx_mcs); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_TX_VHT_MCS) { + ret = os_snprintf(buf + len, buflen - len, " vhtmcs %u", + data.tx_vhtmcs); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_TX_VHT_NSS) { + ret = os_snprintf(buf + len, buflen - len, " vhtnss %u", + data.tx_vht_nss); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + if (data.flags & STA_DRV_DATA_TX_SHORT_GI) { + ret = os_snprintf(buf + len, buflen - len, " shortGI"); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + ret = os_snprintf(buf + len, buflen - len, "\n"); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + + if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) { + ret = os_snprintf(buf + len, buflen - len, + "rx_vht_mcs_map=%04x\n" + "tx_vht_mcs_map=%04x\n", + le_to_host16(sta->vht_capabilities-> + vht_supported_mcs_set.rx_map), + le_to_host16(sta->vht_capabilities-> + vht_supported_mcs_set.tx_map)); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + + if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) { + len = hostapd_write_ht_mcs_bitmask(buf, buflen, len, + sta->ht_capabilities-> + supported_mcs_set); + } + + if (data.flags & STA_DRV_DATA_LAST_ACK_RSSI) { + ret = os_snprintf(buf + len, buflen - len, + "last_ack_signal=%d\n", data.last_ack_rssi); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + + return len; } @@ -176,6 +294,53 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, len += os_snprintf(buf + len, buflen - len, "\n"); } + if (sta->power_capab) { + ret = os_snprintf(buf + len, buflen - len, + "min_txpower=%d\n" + "max_txpower=%d\n", + sta->min_tx_power, sta->max_tx_power); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + +#ifdef CONFIG_IEEE80211AC + if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) { + res = os_snprintf(buf + len, buflen - len, + "vht_caps_info=0x%08x\n", + le_to_host32(sta->vht_capabilities-> + vht_capabilities_info)); + if (!os_snprintf_error(buflen - len, res)) + len += res; + } +#endif /* CONFIG_IEEE80211AC */ + +#ifdef CONFIG_IEEE80211N + if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) { + res = os_snprintf(buf + len, buflen - len, + "ht_caps_info=0x%04x\n", + le_to_host16(sta->ht_capabilities-> + ht_capabilities_info)); + if (!os_snprintf_error(buflen - len, res)) + len += res; + } +#endif /* CONFIG_IEEE80211N */ + + if (sta->ext_capability && + buflen - len > (unsigned) (11 + 2 * sta->ext_capability[0])) { + len += os_snprintf(buf + len, buflen - len, "ext_capab="); + len += wpa_snprintf_hex(buf + len, buflen - len, + sta->ext_capability + 1, + sta->ext_capability[0]); + len += os_snprintf(buf + len, buflen - len, "\n"); + } + + if (sta->flags & WLAN_STA_WDS && sta->ifname_wds) { + ret = os_snprintf(buf + len, buflen - len, + "wds_sta_ifname=%s\n", sta->ifname_wds); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + } + return len; } @@ -477,7 +642,8 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, size_t buflen) { struct hostapd_iface *iface = hapd->iface; - int len = 0, ret; + struct hostapd_hw_modes *mode = iface->current_mode; + int len = 0, ret, j; size_t i; ret = os_snprintf(buf + len, buflen - len, @@ -537,13 +703,17 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, "channel=%u\n" "secondary_channel=%d\n" "ieee80211n=%d\n" - "ieee80211ac=%d\n", + "ieee80211ac=%d\n" + "beacon_int=%u\n" + "dtim_period=%d\n", iface->conf->channel, iface->conf->ieee80211n && !hapd->conf->disable_11n ? iface->conf->secondary_channel : 0, iface->conf->ieee80211n && !hapd->conf->disable_11n, iface->conf->ieee80211ac && - !hapd->conf->disable_11ac); + !hapd->conf->disable_11ac, + iface->conf->beacon_int, + hapd->conf->dtim_period); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -551,15 +721,76 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, ret = os_snprintf(buf + len, buflen - len, "vht_oper_chwidth=%d\n" "vht_oper_centr_freq_seg0_idx=%d\n" - "vht_oper_centr_freq_seg1_idx=%d\n", + "vht_oper_centr_freq_seg1_idx=%d\n" + "vht_caps_info=%08x\n", iface->conf->vht_oper_chwidth, iface->conf->vht_oper_centr_freq_seg0_idx, - iface->conf->vht_oper_centr_freq_seg1_idx); + iface->conf->vht_oper_centr_freq_seg1_idx, + iface->conf->vht_capab); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + + if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac && mode) { + u16 rxmap = WPA_GET_LE16(&mode->vht_mcs_set[0]); + u16 txmap = WPA_GET_LE16(&mode->vht_mcs_set[4]); + + ret = os_snprintf(buf + len, buflen - len, + "rx_vht_mcs_map=%04x\n" + "tx_vht_mcs_map=%04x\n", + rxmap, txmap); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + + if (iface->conf->ieee80211n && !hapd->conf->disable_11n) { + ret = os_snprintf(buf + len, buflen - len, + "ht_caps_info=%04x\n", + hapd->iconf->ht_capab); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + + if (iface->conf->ieee80211n && !hapd->conf->disable_11n && mode) { + len = hostapd_write_ht_mcs_bitmask(buf, buflen, len, + mode->mcs_set); + } + + if (iface->current_rates && iface->num_rates) { + ret = os_snprintf(buf + len, buflen - len, "supported_rates="); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + + for (j = 0; j < iface->num_rates; j++) { + ret = os_snprintf(buf + len, buflen - len, "%s%02x", + j > 0 ? " " : "", + iface->current_rates[j].rate / 5); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + ret = os_snprintf(buf + len, buflen - len, "\n"); if (os_snprintf_error(buflen - len, ret)) return len; len += ret; } + for (j = 0; mode && j < mode->num_channels; j++) { + if (mode->channels[j].freq == iface->freq) { + ret = os_snprintf(buf + len, buflen - len, + "max_txpower=%u\n", + mode->channels[j].max_tx_power); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + break; + } + } + for (i = 0; i < iface->num_bss; i++) { struct hostapd_data *bss = iface->bss[i]; ret = os_snprintf(buf + len, buflen - len, @@ -578,6 +809,15 @@ int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf, len += ret; } + if (hapd->conf->chan_util_avg_period) { + ret = os_snprintf(buf + len, buflen - len, + "chan_util_avg=%u\n", + iface->chan_util_average); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + return len; } @@ -639,3 +879,108 @@ void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd) { wpa_auth_pmksa_flush(hapd->wpa_auth); } + + +int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd) +{ + u8 spa[ETH_ALEN]; + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN_MAX]; + size_t pmk_len; + char *pos, *pos2; + int akmp = 0, expiration = 0; + + /* + * Entry format: + * <STA addr> <PMKID> <PMK> <expiration in seconds> <akmp> + */ + + if (hwaddr_aton(cmd, spa)) + return -1; + + pos = os_strchr(cmd, ' '); + if (!pos) + return -1; + pos++; + + if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0) + return -1; + + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + + pos2 = os_strchr(pos, ' '); + if (!pos2) + return -1; + pmk_len = (pos2 - pos) / 2; + if (pmk_len < PMK_LEN || pmk_len > PMK_LEN_MAX || + hexstr2bin(pos, pmk, pmk_len) < 0) + return -1; + + pos = pos2 + 1; + + if (sscanf(pos, "%d %d", &expiration, &akmp) != 2) + return -1; + + return wpa_auth_pmksa_add2(hapd->wpa_auth, spa, pmk, pmk_len, + pmkid, expiration, akmp); +} + + +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL +#ifdef CONFIG_MESH + +int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd, + const u8 *addr, char *buf, size_t len) +{ + return wpa_auth_pmksa_list_mesh(hapd->wpa_auth, addr, buf, len); +} + + +void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd) +{ + u8 spa[ETH_ALEN]; + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN_MAX]; + char *pos; + int expiration; + + /* + * Entry format: + * <BSSID> <PMKID> <PMK> <expiration in seconds> + */ + + if (hwaddr_aton(cmd, spa)) + return NULL; + + pos = os_strchr(cmd, ' '); + if (!pos) + return NULL; + pos++; + + if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0) + return NULL; + + pos = os_strchr(pos, ' '); + if (!pos) + return NULL; + pos++; + + if (hexstr2bin(pos, pmk, PMK_LEN) < 0) + return NULL; + + pos = os_strchr(pos, ' '); + if (!pos) + return NULL; + pos++; + + if (sscanf(pos, "%d", &expiration) != 1) + return NULL; + + return wpa_auth_pmksa_create_entry(aa, spa, pmk, pmkid, expiration); +} + +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h index 4f996800f1326..d1dcebfb957df 100644 --- a/src/ap/ctrl_iface_ap.h +++ b/src/ap/ctrl_iface_ap.h @@ -32,5 +32,9 @@ int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd); int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf, size_t len); void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd); +int hostapd_ctrl_iface_pmksa_add(struct hostapd_data *hapd, char *cmd); +int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd, + const u8 *addr, char *buf, size_t len); +void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd); #endif /* CTRL_IFACE_AP_H */ diff --git a/src/ap/dfs.c b/src/ap/dfs.c index 47adba7ef7261..993dd19c2cb07 100644 --- a/src/ap/dfs.c +++ b/src/ap/dfs.c @@ -1,7 +1,7 @@ /* * DFS - Dynamic Frequency Selection * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> - * Copyright (c) 2013-2015, Qualcomm Atheros, Inc. + * Copyright (c) 2013-2017, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -747,6 +747,23 @@ int hostapd_handle_dfs(struct hostapd_iface *iface) } +static int hostapd_config_dfs_chan_available(struct hostapd_iface *iface) +{ + int n_chans, n_chans1, start_chan_idx, start_chan_idx1; + + /* Get the start (first) channel for current configuration */ + start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1); + if (start_chan_idx < 0) + return 0; + + /* Get the number of used channels, depending on width */ + n_chans = dfs_get_used_n_chans(iface, &n_chans1); + + /* Check if all channels are DFS available */ + return dfs_check_chans_available(iface, start_chan_idx, n_chans); +} + + int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2) @@ -767,8 +784,21 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, cf1, cf2, HOSTAPD_CHAN_DFS_AVAILABLE); - iface->cac_started = 0; - hostapd_setup_interface_complete(iface, 0); + /* + * Just mark the channel available when CAC completion + * event is received in enabled state. CAC result could + * have been propagated from another radio having the + * same regulatory configuration. When CAC completion is + * received during non-HAPD_IFACE_ENABLED state, make + * sure the configured channel is available because this + * CAC completion event could have been propagated from + * another radio. + */ + if (iface->state != HAPD_IFACE_ENABLED && + hostapd_config_dfs_chan_available(iface)) { + hostapd_setup_interface_complete(iface, 0); + iface->cac_started = 0; + } } } @@ -776,6 +806,25 @@ int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, } +int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2) +{ + wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_PRE_CAC_EXPIRED + "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d", + freq, ht_enabled, chan_offset, chan_width, cf1, cf2); + + /* Proceed only if DFS is not offloaded to the driver */ + if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) + return 0; + + set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width, + cf1, cf2, HOSTAPD_CHAN_DFS_USABLE); + + return 0; +} + + static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface) { struct hostapd_channel_data *channel; @@ -840,6 +889,13 @@ static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface) if (iface->cac_started) return hostapd_dfs_start_channel_switch_cac(iface); + /* + * Allow selection of DFS channel in ETSI to comply with + * uniform spreading. + */ + if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI) + skip_radar = 0; + /* Perform channel switch/CSA */ channel = dfs_get_valid_channel(iface, &secondary_channel, &vht_oper_centr_freq_seg0_idx, @@ -1055,7 +1111,8 @@ int hostapd_handle_dfs_offload(struct hostapd_iface *iface) return 1; } - if (ieee80211_is_dfs(iface->freq)) { + if (ieee80211_is_dfs(iface->freq, iface->hw_features, + iface->num_hw_features)) { wpa_printf(MSG_DEBUG, "%s: freq %d MHz requires DFS", __func__, iface->freq); return 0; diff --git a/src/ap/dfs.h b/src/ap/dfs.h index be8c0e6001c95..f0fa6f688037d 100644 --- a/src/ap/dfs.h +++ b/src/ap/dfs.h @@ -1,7 +1,7 @@ /* * DFS - Dynamic Frequency Selection * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> - * Copyright (c) 2013, Qualcomm Atheros, Inc. + * Copyright (c) 2013-2017, Qualcomm Atheros, Inc. * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,6 +14,9 @@ int hostapd_handle_dfs(struct hostapd_iface *iface); int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq, int ht_enabled, int chan_offset, int chan_width, int cf1, int cf2); +int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq, + int ht_enabled, int chan_offset, int chan_width, + int cf1, int cf2); int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq, int ht_enabled, int chan_offset, int chan_width, diff --git a/src/ap/dhcp_snoop.c b/src/ap/dhcp_snoop.c index f0212fb2a984b..6d8c2f4be0da9 100644 --- a/src/ap/dhcp_snoop.c +++ b/src/ap/dhcp_snoop.c @@ -7,10 +7,9 @@ */ #include "utils/includes.h" -#include <netinet/ip.h> -#include <netinet/udp.h> #include "utils/common.h" +#include "common/dhcp.h" #include "l2_packet/l2_packet.h" #include "hostapd.h" #include "sta_info.h" @@ -18,29 +17,6 @@ #include "x_snoop.h" #include "dhcp_snoop.h" -struct bootp_pkt { - struct iphdr iph; - struct udphdr udph; - u8 op; - u8 htype; - u8 hlen; - u8 hops; - be32 xid; - be16 secs; - be16 flags; - be32 client_ip; - be32 your_ip; - be32 server_ip; - be32 relay_ip; - u8 hw_addr[16]; - u8 serv_name[64]; - u8 boot_file[128]; - u8 exten[312]; -} STRUCT_PACKED; - -#define DHCPACK 5 -static const u8 ic_bootp_cookie[] = { 99, 130, 83, 99 }; - static const char * ipaddr_str(u32 addr) { @@ -74,24 +50,26 @@ static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf, if (tot_len > (unsigned int) (len - ETH_HLEN)) return; - if (os_memcmp(b->exten, ic_bootp_cookie, ARRAY_SIZE(ic_bootp_cookie))) + if (WPA_GET_BE32(b->exten) != DHCP_MAGIC) return; /* Parse DHCP options */ end = (const u8 *) b + tot_len; pos = &b->exten[4]; - while (pos < end && *pos != 0xff) { + while (pos < end && *pos != DHCP_OPT_END) { const u8 *opt = pos++; - if (*opt == 0) /* padding */ + if (*opt == DHCP_OPT_PAD) continue; + if (pos >= end || 1 + *pos > end - pos) + break; pos += *pos + 1; if (pos >= end) break; switch (*opt) { - case 1: /* subnet mask */ + case DHCP_OPT_SUBNET_MASK: if (opt[1] == 4) subnet_mask = WPA_GET_BE32(&opt[2]); if (subnet_mask == 0) @@ -101,7 +79,7 @@ static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf, prefixlen--; } break; - case 53: /* message type */ + case DHCP_OPT_MSG_TYPE: if (opt[1]) msgtype = opt[2]; break; @@ -176,4 +154,5 @@ int dhcp_snoop_init(struct hostapd_data *hapd) void dhcp_snoop_deinit(struct hostapd_data *hapd) { l2_packet_deinit(hapd->sock_dhcp); + hapd->sock_dhcp = NULL; } diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c new file mode 100644 index 0000000000000..149f389f789f1 --- /dev/null +++ b/src/ap/dpp_hostapd.c @@ -0,0 +1,2096 @@ +/* + * hostapd / DPP integration + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/dpp.h" +#include "common/gas.h" +#include "common/wpa_ctrl.h" +#include "hostapd.h" +#include "ap_drv_ops.h" +#include "gas_query_ap.h" +#include "wpa_auth.h" +#include "dpp_hostapd.h" + + +static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx); +static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator); +static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx); +static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd); + +static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + +static struct dpp_configurator * +hostapd_dpp_configurator_get_id(struct hostapd_data *hapd, unsigned int id) +{ + struct dpp_configurator *conf; + + dl_list_for_each(conf, &hapd->iface->interfaces->dpp_configurator, + struct dpp_configurator, list) { + if (conf->id == id) + return conf; + } + return NULL; +} + + +static unsigned int hapd_dpp_next_id(struct hostapd_data *hapd) +{ + struct dpp_bootstrap_info *bi; + unsigned int max_id = 0; + + dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap, + struct dpp_bootstrap_info, list) { + if (bi->id > max_id) + max_id = bi->id; + } + return max_id + 1; +} + + +/** + * hostapd_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code + * @hapd: Pointer to hostapd_data + * @cmd: DPP URI read from a QR Code + * Returns: Identifier of the stored info or -1 on failure + */ +int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd) +{ + struct dpp_bootstrap_info *bi; + struct dpp_authentication *auth = hapd->dpp_auth; + + bi = dpp_parse_qr_code(cmd); + if (!bi) + return -1; + + bi->id = hapd_dpp_next_id(hapd); + dl_list_add(&hapd->iface->interfaces->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(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", + MAC2STR(auth->peer_mac_addr), auth->curr_freq, + DPP_PA_AUTHENTICATION_RESP); + hostapd_drv_send_action(hapd, auth->curr_freq, 0, + auth->peer_mac_addr, + wpabuf_head(hapd->dpp_auth->resp_msg), + wpabuf_len(hapd->dpp_auth->resp_msg)); + } + + 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 hostapd_dpp_bootstrap_gen(struct hostapd_data *hapd, 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 = hapd_dpp_next_id(hapd); + dl_list_add(&hapd->iface->interfaces->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 hostapd_data *hapd, unsigned int id) +{ + struct dpp_bootstrap_info *bi; + + dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap, + struct dpp_bootstrap_info, list) { + if (bi->id == id) + return bi; + } + return NULL; +} + + +static int dpp_bootstrap_del(struct hapd_interfaces *ifaces, unsigned int id) +{ + struct dpp_bootstrap_info *bi, *tmp; + int found = 0; + + dl_list_for_each_safe(bi, tmp, &ifaces->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 hostapd_dpp_bootstrap_remove(struct hostapd_data *hapd, 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(hapd->iface->interfaces, id_val); +} + + +const char * hostapd_dpp_bootstrap_get_uri(struct hostapd_data *hapd, + unsigned int id) +{ + struct dpp_bootstrap_info *bi; + + bi = dpp_bootstrap_get_id(hapd, id); + if (!bi) + return NULL; + return bi->uri; +} + + +int hostapd_dpp_bootstrap_info(struct hostapd_data *hapd, int id, + char *reply, int reply_size) +{ + struct dpp_bootstrap_info *bi; + + bi = dpp_bootstrap_get_id(hapd, 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 hostapd_dpp_auth_resp_retry_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct dpp_authentication *auth = hapd->dpp_auth; + + if (!auth || !auth->resp_msg) + return; + + wpa_printf(MSG_DEBUG, + "DPP: Retry Authentication Response after timeout"); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", + MAC2STR(auth->peer_mac_addr), auth->curr_freq, + DPP_PA_AUTHENTICATION_RESP); + hostapd_drv_send_action(hapd, auth->curr_freq, 500, auth->peer_mac_addr, + wpabuf_head(auth->resp_msg), + wpabuf_len(auth->resp_msg)); +} + + +static void hostapd_dpp_auth_resp_retry(struct hostapd_data *hapd) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + unsigned int wait_time, max_tries; + + if (!auth || !auth->resp_msg) + return; + + if (hapd->dpp_resp_max_tries) + max_tries = hapd->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"); + hostapd_drv_send_action_cancel_wait(hapd); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + return; + } + + if (hapd->dpp_resp_retry_time) + wait_time = hapd->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(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); + eloop_register_timeout(wait_time / 1000, + (wait_time % 1000) * 1000, + hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); +} + + +void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst, + const u8 *data, size_t data_len, int ok) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + + wpa_printf(MSG_DEBUG, "DPP: TX status: dst=" MACSTR " ok=%d", + MAC2STR(dst), ok); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR + " result=%s", MAC2STR(dst), ok ? "SUCCESS" : "FAILED"); + + if (!hapd->dpp_auth) { + wpa_printf(MSG_DEBUG, + "DPP: Ignore TX status since there is no ongoing authentication exchange"); + return; + } + + if (hapd->dpp_auth->remove_on_tx_status) { + wpa_printf(MSG_DEBUG, + "DPP: Terminate authentication exchange due to an earlier error"); + eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, + hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, + NULL); + hostapd_drv_send_action_cancel_wait(hapd); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + return; + } + + if (hapd->dpp_auth_ok_on_ack) + hostapd_dpp_auth_success(hapd, 1); + + if (!is_broadcast_ether_addr(dst) && !ok) { + 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. */ + hostapd_drv_send_action_cancel_wait(hapd); + hostapd_dpp_auth_init_next(hapd); + return; + } + if (auth->waiting_auth_conf) { + hostapd_dpp_auth_resp_retry(hapd); + return; + } + } + + if (!is_broadcast_ether_addr(dst) && auth->waiting_auth_resp && ok) { + /* 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 (!hapd->dpp_auth_ok_on_ack && hapd->dpp_auth->neg_freq > 0 && + hapd->dpp_auth->curr_freq != hapd->dpp_auth->neg_freq) { + wpa_printf(MSG_DEBUG, + "DPP: Move from curr_freq %u MHz to neg_freq %u MHz for response", + hapd->dpp_auth->curr_freq, + hapd->dpp_auth->neg_freq); + hostapd_drv_send_action_cancel_wait(hapd); + + if (hapd->dpp_auth->neg_freq != + (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) { + /* TODO: Listen operation on non-operating channel */ + wpa_printf(MSG_INFO, + "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)", + hapd->dpp_auth->neg_freq, hapd->iface->freq); + } + } + + if (hapd->dpp_auth_ok_on_ack) + hapd->dpp_auth_ok_on_ack = 0; +} + + +static void hostapd_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct dpp_authentication *auth = hapd->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 = hapd->dpp_resp_wait_time ? + hapd->dpp_resp_wait_time : 2000; + os_get_reltime(&now); + os_reltime_sub(&now, &hapd->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(hapd->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_INIT_FAILED); + hostapd_drv_send_action_cancel_wait(hapd); + hostapd_dpp_listen_stop(hapd); + dpp_auth_deinit(auth); + hapd->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"); + hostapd_drv_send_action_cancel_wait(hapd); + hostapd_dpp_listen_stop(hapd); + hostapd_dpp_auth_init_next(hapd); + 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); + hapd->dpp_in_response_listen = 1; + + if (freq != (unsigned int) hapd->iface->freq && hapd->iface->freq > 0) { + /* TODO: Listen operation on non-operating channel */ + wpa_printf(MSG_INFO, + "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)", + freq, hapd->iface->freq); + } + + eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000, + hostapd_dpp_reply_wait_timeout, hapd, NULL); +} + + +static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd, + struct dpp_authentication *auth) +{ +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->dpp_config_obj_override) + auth->config_obj_override = + os_strdup(hapd->dpp_config_obj_override); + if (hapd->dpp_discovery_override) + auth->discovery_override = + os_strdup(hapd->dpp_discovery_override); + if (hapd->dpp_groups_override) + auth->groups_override = os_strdup(hapd->dpp_groups_override); + auth->ignore_netaccesskey_mismatch = + hapd->dpp_ignore_netaccesskey_mismatch; +#endif /* CONFIG_TESTING_OPTIONS */ +} + + +static int hostapd_dpp_set_configurator(struct hostapd_data *hapd, + 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 { + conf_sta->passphrase = os_strdup(pass); + if (!conf_sta->passphrase) + 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 if (pass_len > 0) { + conf_ap->passphrase = os_strdup(pass); + if (!conf_ap->passphrase) + goto fail; + } else { + 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) { + auth->configurator = 1; + pos += 14; + conf = hostapd_dpp_configurator_get_id(hapd, 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(hapd->msg_ctx, 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 hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + + if (!hapd->dpp_auth) + return; + wpa_printf(MSG_DEBUG, "DPP: Retry initiation after timeout"); + hostapd_dpp_auth_init_next(hapd); +} + + +static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + const u8 *dst; + unsigned int wait_time, max_wait_time, freq, max_tries, used; + struct os_reltime now, diff; + + if (!auth) + return -1; + + if (auth->freq_idx == 0) + os_get_reltime(&hapd->dpp_init_iter_start); + + if (auth->freq_idx >= auth->num_freq) { + auth->num_freq_iters++; + if (hapd->dpp_init_max_tries) + max_tries = hapd->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(hapd->msg_ctx, MSG_INFO, + DPP_EVENT_AUTH_INIT_FAILED); + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, + hapd, NULL); + hostapd_drv_send_action_cancel_wait(hapd); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + return -1; + } + auth->freq_idx = 0; + eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); + if (hapd->dpp_init_retry_time) + wait_time = hapd->dpp_init_retry_time; + else + wait_time = 10000; + os_get_reltime(&now); + os_reltime_sub(&now, &hapd->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, + hostapd_dpp_init_timeout, hapd, + 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; + hapd->dpp_auth_ok_on_ack = 0; + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); + wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */ + max_wait_time = hapd->dpp_resp_wait_time ? + hapd->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, + hostapd_dpp_reply_wait_timeout, hapd, 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(hapd->msg_ctx, 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(&hapd->dpp_last_init); + return hostapd_drv_send_action(hapd, freq, wait_time, + dst, + wpabuf_head(hapd->dpp_auth->req_msg), + wpabuf_len(hapd->dpp_auth->req_msg)); +} + + +int hostapd_dpp_auth_init(struct hostapd_data *hapd, 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; + + pos = os_strstr(cmd, " peer="); + if (!pos) + return -1; + pos += 6; + peer_bi = dpp_bootstrap_get_id(hapd, 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(hapd, 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, " neg_freq="); + if (pos) + neg_freq = atoi(pos + 10); + + if (hapd->dpp_auth) { + eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, + hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, + NULL); + hostapd_drv_send_action_cancel_wait(hapd); + dpp_auth_deinit(hapd->dpp_auth); + } + + hapd->dpp_auth = dpp_auth_init(hapd->msg_ctx, peer_bi, own_bi, + allowed_roles, neg_freq, + hapd->iface->hw_features, + hapd->iface->num_hw_features); + if (!hapd->dpp_auth) + goto fail; + hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth); + if (hostapd_dpp_set_configurator(hapd, hapd->dpp_auth, cmd) < 0) { + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + goto fail; + } + + hapd->dpp_auth->neg_freq = neg_freq; + + if (!is_zero_ether_addr(peer_bi->mac_addr)) + os_memcpy(hapd->dpp_auth->peer_mac_addr, peer_bi->mac_addr, + ETH_ALEN); + + return hostapd_dpp_auth_init_next(hapd); +fail: + return -1; +} + + +int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd) +{ + int freq; + + freq = atoi(cmd); + if (freq <= 0) + return -1; + + if (os_strstr(cmd, " role=configurator")) + hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR; + else if (os_strstr(cmd, " role=enrollee")) + hapd->dpp_allowed_roles = DPP_CAPAB_ENROLLEE; + else + hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | + DPP_CAPAB_ENROLLEE; + hapd->dpp_qr_mutual = os_strstr(cmd, " qr=mutual") != NULL; + + if (freq != hapd->iface->freq && hapd->iface->freq > 0) { + /* TODO: Listen operation on non-operating channel */ + wpa_printf(MSG_INFO, + "DPP: Listen operation on non-operating channel (%d MHz) is not yet supported (operating channel: %d MHz)", + freq, hapd->iface->freq); + return -1; + } + + return 0; +} + + +void hostapd_dpp_listen_stop(struct hostapd_data *hapd) +{ + /* TODO: Stop listen operation on non-operating channel */ +} + + +static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, 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(hapd->msg_ctx, 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(hapd->msg_ctx, 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, &hapd->iface->interfaces->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(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "No matching own bootstrapping key found - ignore message"); + return; + } + + if (hapd->dpp_auth) { + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Already in DPP authentication exchange - ignore new one"); + return; + } + + hapd->dpp_auth_ok_on_ack = 0; + hapd->dpp_auth = dpp_auth_req_rx(hapd->msg_ctx, hapd->dpp_allowed_roles, + hapd->dpp_qr_mutual, + peer_bi, own_bi, freq, hdr, buf, len); + if (!hapd->dpp_auth) { + wpa_printf(MSG_DEBUG, "DPP: No response generated"); + return; + } + hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth); + if (hostapd_dpp_set_configurator(hapd, hapd->dpp_auth, + hapd->dpp_configurator_params) < 0) { + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + return; + } + os_memcpy(hapd->dpp_auth->peer_mac_addr, src, ETH_ALEN); + + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", + MAC2STR(src), hapd->dpp_auth->curr_freq, + DPP_PA_AUTHENTICATION_RESP); + hostapd_drv_send_action(hapd, hapd->dpp_auth->curr_freq, 0, + src, wpabuf_head(hapd->dpp_auth->resp_msg), + wpabuf_len(hapd->dpp_auth->resp_msg)); +} + + +static void hostapd_dpp_handle_config_obj(struct hostapd_data *hapd, + struct dpp_authentication *auth) +{ + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_RECEIVED); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_AKM "%s", + dpp_akm_str(auth->akm)); + if (auth->ssid_len) + wpa_msg(hapd->msg_ctx, 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(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONNECTOR "%s", + auth->connector); + } else if (auth->passphrase[0]) { + char hex[64 * 2 + 1]; + + wpa_snprintf_hex(hex, sizeof(hex), + (const u8 *) auth->passphrase, + os_strlen(auth->passphrase)); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PASS "%s", + hex); + } else if (auth->psk_set) { + char hex[PMK_LEN * 2 + 1]; + + wpa_snprintf_hex(hex, sizeof(hex), auth->psk, PMK_LEN); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONFOBJ_PSK "%s", + hex); + } + 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(hapd->msg_ctx, 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(hapd->msg_ctx, MSG_INFO, + DPP_EVENT_NET_ACCESS_KEY "%s %lu", hex, + (unsigned long) + auth->net_access_key_expiry); + else + wpa_msg(hapd->msg_ctx, MSG_INFO, + DPP_EVENT_NET_ACCESS_KEY "%s", hex); + os_free(hex); + } + } +} + + +static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token, + enum gas_query_ap_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code) +{ + struct hostapd_data *hapd = ctx; + const u8 *pos; + struct dpp_authentication *auth = hapd->dpp_auth; + + if (!auth || !auth->auth_success) { + wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); + return; + } + if (!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; + } + + hostapd_dpp_handle_config_obj(hapd, auth); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + return; + +fail: + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; +} + + +static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + struct wpabuf *buf, *conf_req; + char json[100]; + int res; + int netrole_ap = 1; + + os_snprintf(json, sizeof(json), + "{\"name\":\"Test\"," + "\"wi-fi_tech\":\"infra\"," + "\"netRole\":\"%s\"}", + netrole_ap ? "ap" : "sta"); + wpa_printf(MSG_DEBUG, "DPP: GAS Config Attributes: %s", json); + + 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_ap_req(hapd->gas, auth->peer_mac_addr, auth->curr_freq, + buf, hostapd_dpp_gas_resp_cb, hapd); + if (res < 0) { + wpa_msg(hapd->msg_ctx, 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); + } +} + + +static void hostapd_dpp_auth_success(struct hostapd_data *hapd, int initiator) +{ + wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded"); + wpa_msg(hapd->msg_ctx, 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 (hapd->dpp_auth->configurator) { + /* Prevent GAS response */ + hapd->dpp_auth->auth_success = 0; + } + return; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (!hapd->dpp_auth->configurator) + hostapd_dpp_start_gas_client(hapd); +} + + +static void hostapd_dpp_rx_auth_resp(struct hostapd_data *hapd, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len, + unsigned int freq) +{ + struct dpp_authentication *auth = hapd->dpp_auth; + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "DPP: Authentication Response from " MACSTR, + MAC2STR(src)); + + 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(hostapd_dpp_reply_wait_timeout, hapd, 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(hostapd_dpp_init_timeout, hapd, 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: Wait for full response"); + return; + } + wpa_printf(MSG_DEBUG, "DPP: No confirm generated"); + return; + } + os_memcpy(auth->peer_mac_addr, src, ETH_ALEN); + + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", MAC2STR(src), auth->curr_freq, + DPP_PA_AUTHENTICATION_CONF); + hostapd_drv_send_action(hapd, auth->curr_freq, 0, src, + wpabuf_head(msg), wpabuf_len(msg)); + wpabuf_free(msg); + hapd->dpp_auth_ok_on_ack = 1; +} + + +static void hostapd_dpp_rx_auth_conf(struct hostapd_data *hapd, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len) +{ + struct dpp_authentication *auth = hapd->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; + } + + hostapd_dpp_auth_success(hapd, 0); +} + + +static void hostapd_dpp_send_peer_disc_resp(struct hostapd_data *hapd, + const u8 *src, unsigned int freq, + u8 trans_id, + enum dpp_status_error status) +{ + struct wpabuf *msg; + + msg = dpp_alloc_msg(DPP_PA_PEER_DISCOVERY_RESP, + 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector)); + if (!msg) + return; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Transaction ID"); + goto skip_trans_id; + } + if (dpp_test == DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Transaction ID"); + trans_id ^= 0x01; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* Transaction ID */ + wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, trans_id); + +#ifdef CONFIG_TESTING_OPTIONS +skip_trans_id: + if (dpp_test == DPP_TEST_NO_STATUS_PEER_DISC_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); + goto skip_status; + } + if (dpp_test == DPP_TEST_INVALID_STATUS_PEER_DISC_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); + status = 254; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* DPP Status */ + wpabuf_put_le16(msg, DPP_ATTR_STATUS); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, status); + +#ifdef CONFIG_TESTING_OPTIONS +skip_status: + if (dpp_test == DPP_TEST_NO_CONNECTOR_PEER_DISC_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Connector"); + goto skip_connector; + } + if (status == DPP_STATUS_OK && + dpp_test == DPP_TEST_INVALID_CONNECTOR_PEER_DISC_RESP) { + char *connector; + + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Connector"); + connector = dpp_corrupt_connector_signature( + hapd->conf->dpp_connector); + if (!connector) { + wpabuf_free(msg); + return; + } + 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 */ + if (status == DPP_STATUS_OK) { + wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR); + wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector)); + wpabuf_put_str(msg, hapd->conf->dpp_connector); + } + +#ifdef CONFIG_TESTING_OPTIONS +skip_connector: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_printf(MSG_DEBUG, "DPP: Send Peer Discovery Response to " MACSTR + " status=%d", MAC2STR(src), status); + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d status=%d", MAC2STR(src), freq, + DPP_PA_PEER_DISCOVERY_RESP, status); + hostapd_drv_send_action(hapd, freq, 0, src, + wpabuf_head(msg), wpabuf_len(msg)); + wpabuf_free(msg); +} + + +static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd, + const u8 *src, + const u8 *buf, size_t len, + unsigned int freq) +{ + const u8 *connector, *trans_id; + u16 connector_len, trans_id_len; + struct os_time now; + struct dpp_introduction intro; + os_time_t expire; + int expiration; + enum dpp_status_error res; + + wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Request from " MACSTR, + MAC2STR(src)); + if (!hapd->wpa_auth || + !(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) || + !(hapd->conf->wpa & WPA_PROTO_RSN)) { + wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use"); + return; + } + + if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey || + !hapd->conf->dpp_csign) { + wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set"); + return; + } + + os_get_time(&now); + + if (hapd->conf->dpp_netaccesskey_expiry && + (os_time_t) hapd->conf->dpp_netaccesskey_expiry < now.sec) { + wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired"); + 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"); + return; + } + + connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len); + if (!connector) { + wpa_printf(MSG_DEBUG, + "DPP: Peer did not include its Connector"); + return; + } + + res = dpp_peer_intro(&intro, hapd->conf->dpp_connector, + wpabuf_head(hapd->conf->dpp_netaccesskey), + wpabuf_len(hapd->conf->dpp_netaccesskey), + wpabuf_head(hapd->conf->dpp_csign), + wpabuf_len(hapd->conf->dpp_csign), + connector, connector_len, &expire); + if (res == 255) { + wpa_printf(MSG_INFO, + "DPP: Network Introduction protocol resulted in internal failure (peer " + MACSTR ")", MAC2STR(src)); + return; + } + if (res != DPP_STATUS_OK) { + wpa_printf(MSG_INFO, + "DPP: Network Introduction protocol resulted in failure (peer " + MACSTR " status %d)", MAC2STR(src), res); + hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0], + res); + return; + } + + if (!expire || (os_time_t) hapd->conf->dpp_netaccesskey_expiry < expire) + expire = hapd->conf->dpp_netaccesskey_expiry; + if (expire) + expiration = expire - now.sec; + else + expiration = 0; + + if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len, + intro.pmkid, expiration, + WPA_KEY_MGMT_DPP) < 0) { + wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry"); + return; + } + + hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0], + DPP_STATUS_OK); +} + + +static void +hostapd_dpp_rx_pkex_exchange_req(struct hostapd_data *hapd, const u8 *src, + const u8 *buf, size_t len, + unsigned int freq) +{ + struct wpabuf *msg; + + 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 (!hapd->dpp_pkex_code || !hapd->dpp_pkex_bi) { + wpa_printf(MSG_DEBUG, + "DPP: No PKEX code configured - ignore request"); + return; + } + + if (hapd->dpp_pkex) { + /* TODO: Support parallel operations */ + wpa_printf(MSG_DEBUG, + "DPP: Already in PKEX session - ignore new request"); + return; + } + + hapd->dpp_pkex = dpp_pkex_rx_exchange_req(hapd->msg_ctx, + hapd->dpp_pkex_bi, + hapd->own_addr, src, + hapd->dpp_pkex_identifier, + hapd->dpp_pkex_code, + buf, len); + if (!hapd->dpp_pkex) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to process the request - ignore it"); + return; + } + + msg = hapd->dpp_pkex->exchange_resp; + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", MAC2STR(src), freq, + DPP_PA_PKEX_EXCHANGE_RESP); + hostapd_drv_send_action(hapd, freq, 0, src, + wpabuf_head(msg), wpabuf_len(msg)); + if (hapd->dpp_pkex->failed) { + wpa_printf(MSG_DEBUG, + "DPP: Terminate PKEX exchange due to an earlier error"); + if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t) + hapd->dpp_pkex->own_bi->pkex_t = hapd->dpp_pkex->t; + dpp_pkex_free(hapd->dpp_pkex); + hapd->dpp_pkex = NULL; + } +} + + +static void +hostapd_dpp_rx_pkex_exchange_resp(struct hostapd_data *hapd, const u8 *src, + const u8 *buf, size_t len, unsigned int freq) +{ + struct wpabuf *msg; + + 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 (!hapd->dpp_pkex || !hapd->dpp_pkex->initiator || + hapd->dpp_pkex->exchange_done) { + wpa_printf(MSG_DEBUG, "DPP: No matching PKEX session"); + return; + } + + msg = dpp_pkex_rx_exchange_resp(hapd->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)); + + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", MAC2STR(src), freq, + DPP_PA_PKEX_COMMIT_REVEAL_REQ); + hostapd_drv_send_action(hapd, freq, 0, src, + wpabuf_head(msg), wpabuf_len(msg)); + wpabuf_free(msg); +} + + +static void +hostapd_dpp_rx_pkex_commit_reveal_req(struct hostapd_data *hapd, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len, + unsigned int freq) +{ + struct wpabuf *msg; + struct dpp_pkex *pkex = hapd->dpp_pkex; + struct dpp_bootstrap_info *bi; + + 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 (hapd->dpp_pkex->failed) { + wpa_printf(MSG_DEBUG, "DPP: Terminate PKEX exchange"); + if (hapd->dpp_pkex->t > hapd->dpp_pkex->own_bi->pkex_t) + hapd->dpp_pkex->own_bi->pkex_t = + hapd->dpp_pkex->t; + dpp_pkex_free(hapd->dpp_pkex); + hapd->dpp_pkex = NULL; + } + return; + } + + wpa_printf(MSG_DEBUG, "DPP: Send PKEX Commit-Reveal Response to " + MACSTR, MAC2STR(src)); + + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", MAC2STR(src), freq, + DPP_PA_PKEX_COMMIT_REVEAL_RESP); + hostapd_drv_send_action(hapd, freq, 0, src, + wpabuf_head(msg), wpabuf_len(msg)); + wpabuf_free(msg); + + bi = os_zalloc(sizeof(*bi)); + if (!bi) + return; + bi->id = hapd_dpp_next_id(hapd); + bi->type = DPP_BOOTSTRAP_PKEX; + os_memcpy(bi->mac_addr, src, 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); + hapd->dpp_pkex = NULL; + if (dpp_bootstrap_key_hash(bi) < 0) { + dpp_bootstrap_info_free(bi); + return; + } + dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list); +} + + +static void +hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src, + const u8 *hdr, const u8 *buf, size_t len, + unsigned int freq) +{ + int res; + struct dpp_bootstrap_info *bi, *own_bi; + struct dpp_pkex *pkex = hapd->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; + } + + own_bi = pkex->own_bi; + + bi = os_zalloc(sizeof(*bi)); + if (!bi) + return; + bi->id = hapd_dpp_next_id(hapd); + bi->type = DPP_BOOTSTRAP_PKEX; + os_memcpy(bi->mac_addr, src, ETH_ALEN); + bi->num_freq = 1; + bi->freq[0] = freq; + bi->curve = own_bi->curve; + bi->pubkey = pkex->peer_bootstrap_key; + pkex->peer_bootstrap_key = NULL; + dpp_pkex_free(pkex); + hapd->dpp_pkex = NULL; + if (dpp_bootstrap_key_hash(bi) < 0) { + dpp_bootstrap_info_free(bi); + return; + } + dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list); + + os_snprintf(cmd, sizeof(cmd), " peer=%u %s", + bi->id, + hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : ""); + wpa_printf(MSG_DEBUG, + "DPP: Start authentication after PKEX with parameters: %s", + cmd); + if (hostapd_dpp_auth_init(hapd, cmd) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Authentication initialization failed"); + return; + } +} + + +void hostapd_dpp_rx_action(struct hostapd_data *hapd, 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(hapd->msg_ctx, 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(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR + " freq=%u type=%d ignore=invalid-attributes", + MAC2STR(src), freq, type); + return; + } + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RX "src=" MACSTR + " freq=%u type=%d", MAC2STR(src), freq, type); + + switch (type) { + case DPP_PA_AUTHENTICATION_REQ: + hostapd_dpp_rx_auth_req(hapd, src, hdr, buf, len, freq); + break; + case DPP_PA_AUTHENTICATION_RESP: + hostapd_dpp_rx_auth_resp(hapd, src, hdr, buf, len, freq); + break; + case DPP_PA_AUTHENTICATION_CONF: + hostapd_dpp_rx_auth_conf(hapd, src, hdr, buf, len); + break; + case DPP_PA_PEER_DISCOVERY_REQ: + hostapd_dpp_rx_peer_disc_req(hapd, src, buf, len, freq); + break; + case DPP_PA_PKEX_EXCHANGE_REQ: + hostapd_dpp_rx_pkex_exchange_req(hapd, src, buf, len, freq); + break; + case DPP_PA_PKEX_EXCHANGE_RESP: + hostapd_dpp_rx_pkex_exchange_resp(hapd, src, buf, len, freq); + break; + case DPP_PA_PKEX_COMMIT_REVEAL_REQ: + hostapd_dpp_rx_pkex_commit_reveal_req(hapd, src, hdr, buf, len, + freq); + break; + case DPP_PA_PKEX_COMMIT_REVEAL_RESP: + hostapd_dpp_rx_pkex_commit_reveal_resp(hapd, src, hdr, buf, len, + freq); + break; + default: + wpa_printf(MSG_DEBUG, + "DPP: Ignored unsupported frame subtype %d", type); + break; + } + + if (hapd->dpp_pkex) + pkex_t = hapd->dpp_pkex->t; + else if (hapd->dpp_pkex_bi) + pkex_t = hapd->dpp_pkex_bi->pkex_t; + else + pkex_t = 0; + if (pkex_t >= PKEX_COUNTER_T_LIMIT) { + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PKEX_T_LIMIT "id=0"); + hostapd_dpp_pkex_remove(hapd, "*"); + } +} + + +struct wpabuf * +hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa, + const u8 *query, size_t query_len) +{ + struct dpp_authentication *auth = hapd->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(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_REQ_RX "src=" MACSTR, + MAC2STR(sa)); + resp = dpp_conf_req_rx(auth, query, query_len); + if (!resp) + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); + return resp; +} + + +void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok) +{ + if (!hapd->dpp_auth) + return; + + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); + hostapd_drv_send_action_cancel_wait(hapd); + + if (ok) + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT); + else + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; +} + + +static unsigned int hostapd_dpp_next_configurator_id(struct hostapd_data *hapd) +{ + struct dpp_configurator *conf; + unsigned int max_id = 0; + + dl_list_for_each(conf, &hapd->iface->interfaces->dpp_configurator, + struct dpp_configurator, list) { + if (conf->id > max_id) + max_id = conf->id; + } + return max_id + 1; +} + + +int hostapd_dpp_configurator_add(struct hostapd_data *hapd, 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 = hostapd_dpp_next_configurator_id(hapd); + dl_list_add(&hapd->iface->interfaces->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 hapd_interfaces *ifaces, unsigned int id) +{ + struct dpp_configurator *conf, *tmp; + int found = 0; + + dl_list_for_each_safe(conf, tmp, &ifaces->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 hostapd_dpp_configurator_remove(struct hostapd_data *hapd, 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(hapd->iface->interfaces, id_val); +} + + +int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, 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="); + hostapd_dpp_set_testing_options(hapd, auth); + if (hostapd_dpp_set_configurator(hapd, auth, cmd) == 0 && + dpp_configurator_own_config(auth, curve, 1) == 0) { + hostapd_dpp_handle_config_obj(hapd, auth); + ret = 0; + } + + dpp_auth_deinit(auth); + os_free(curve); + + return ret; +} + + +int hostapd_dpp_configurator_get_key(struct hostapd_data *hapd, unsigned int id, + char *buf, size_t buflen) +{ + struct dpp_configurator *conf; + + conf = hostapd_dpp_configurator_get_id(hapd, id); + if (!conf) + return -1; + + return dpp_configurator_get_key(conf, buf, buflen); +} + + +int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd) +{ + struct dpp_bootstrap_info *own_bi; + const char *pos, *end; + + pos = os_strstr(cmd, " own="); + if (!pos) + return -1; + pos += 5; + own_bi = dpp_bootstrap_get_id(hapd, 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; + } + hapd->dpp_pkex_bi = own_bi; + own_bi->pkex_t = 0; /* clear pending errors on new code */ + + os_free(hapd->dpp_pkex_identifier); + hapd->dpp_pkex_identifier = NULL; + pos = os_strstr(cmd, " identifier="); + if (pos) { + pos += 12; + end = os_strchr(pos, ' '); + if (!end) + return -1; + hapd->dpp_pkex_identifier = os_malloc(end - pos + 1); + if (!hapd->dpp_pkex_identifier) + return -1; + os_memcpy(hapd->dpp_pkex_identifier, pos, end - pos); + hapd->dpp_pkex_identifier[end - pos] = '\0'; + } + + pos = os_strstr(cmd, " code="); + if (!pos) + return -1; + os_free(hapd->dpp_pkex_code); + hapd->dpp_pkex_code = os_strdup(pos + 6); + if (!hapd->dpp_pkex_code) + return -1; + + if (os_strstr(cmd, " init=1")) { + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "DPP: Initiating PKEX"); + dpp_pkex_free(hapd->dpp_pkex); + hapd->dpp_pkex = dpp_pkex_init(hapd->msg_ctx, own_bi, + hapd->own_addr, + hapd->dpp_pkex_identifier, + hapd->dpp_pkex_code); + if (!hapd->dpp_pkex) + return -1; + + msg = hapd->dpp_pkex->exchange_req; + /* TODO: Which channel to use? */ + wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR + " freq=%u type=%d", MAC2STR(broadcast), 2437, + DPP_PA_PKEX_EXCHANGE_REQ); + hostapd_drv_send_action(hapd, 2437, 0, broadcast, + wpabuf_head(msg), wpabuf_len(msg)); + } + + /* TODO: Support multiple PKEX info entries */ + + os_free(hapd->dpp_pkex_auth_cmd); + hapd->dpp_pkex_auth_cmd = os_strdup(cmd); + + return 1; +} + + +int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, 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) || !hapd->dpp_pkex_code) + return -1; + + /* TODO: Support multiple PKEX entries */ + os_free(hapd->dpp_pkex_code); + hapd->dpp_pkex_code = NULL; + os_free(hapd->dpp_pkex_identifier); + hapd->dpp_pkex_identifier = NULL; + os_free(hapd->dpp_pkex_auth_cmd); + hapd->dpp_pkex_auth_cmd = NULL; + hapd->dpp_pkex_bi = NULL; + /* TODO: Remove dpp_pkex only if it is for the identified PKEX code */ + dpp_pkex_free(hapd->dpp_pkex); + hapd->dpp_pkex = NULL; + return 0; +} + + +void hostapd_dpp_stop(struct hostapd_data *hapd) +{ + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + dpp_pkex_free(hapd->dpp_pkex); + hapd->dpp_pkex = NULL; +} + + +int hostapd_dpp_init(struct hostapd_data *hapd) +{ + hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE; + hapd->dpp_init_done = 1; + return 0; +} + + +void hostapd_dpp_deinit(struct hostapd_data *hapd) +{ +#ifdef CONFIG_TESTING_OPTIONS + os_free(hapd->dpp_config_obj_override); + hapd->dpp_config_obj_override = NULL; + os_free(hapd->dpp_discovery_override); + hapd->dpp_discovery_override = NULL; + os_free(hapd->dpp_groups_override); + hapd->dpp_groups_override = NULL; + hapd->dpp_ignore_netaccesskey_mismatch = 0; +#endif /* CONFIG_TESTING_OPTIONS */ + if (!hapd->dpp_init_done) + return; + eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL); + eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL); + dpp_auth_deinit(hapd->dpp_auth); + hapd->dpp_auth = NULL; + hostapd_dpp_pkex_remove(hapd, "*"); + hapd->dpp_pkex = NULL; + os_free(hapd->dpp_configurator_params); + hapd->dpp_configurator_params = NULL; +} + + +void hostapd_dpp_init_global(struct hapd_interfaces *ifaces) +{ + dl_list_init(&ifaces->dpp_bootstrap); + dl_list_init(&ifaces->dpp_configurator); + ifaces->dpp_init_done = 1; +} + + +void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces) +{ + if (!ifaces->dpp_init_done) + return; + dpp_bootstrap_del(ifaces, 0); + dpp_configurator_del(ifaces, 0); +} diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h new file mode 100644 index 0000000000000..3ef7c14567e88 --- /dev/null +++ b/src/ap/dpp_hostapd.h @@ -0,0 +1,43 @@ +/* + * hostapd / DPP integration + * 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_HOSTAPD_H +#define DPP_HOSTAPD_H + +int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd); +int hostapd_dpp_bootstrap_gen(struct hostapd_data *hapd, const char *cmd); +int hostapd_dpp_bootstrap_remove(struct hostapd_data *hapd, const char *id); +const char * hostapd_dpp_bootstrap_get_uri(struct hostapd_data *hapd, + unsigned int id); +int hostapd_dpp_bootstrap_info(struct hostapd_data *hapd, int id, + char *reply, int reply_size); +int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd); +int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd); +void hostapd_dpp_listen_stop(struct hostapd_data *hapd); +void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src, + const u8 *buf, size_t len, unsigned int freq); +void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst, + const u8 *data, size_t data_len, int ok); +struct wpabuf * +hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa, + const u8 *query, size_t query_len); +void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok); +int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd); +int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id); +int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd); +int hostapd_dpp_configurator_get_key(struct hostapd_data *hapd, unsigned int id, + char *buf, size_t buflen); +int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd); +int hostapd_dpp_pkex_remove(struct hostapd_data *hapd, const char *id); +void hostapd_dpp_stop(struct hostapd_data *hapd); +int hostapd_dpp_init(struct hostapd_data *hapd); +void hostapd_dpp_deinit(struct hostapd_data *hapd); +void hostapd_dpp_init_global(struct hapd_interfaces *ifaces); +void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces); + +#endif /* DPP_HOSTAPD_H */ diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 3552b3e0d53b6..a726a6ff87e01 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -31,10 +31,74 @@ #include "wps_hostapd.h" #include "ap_drv_ops.h" #include "ap_config.h" +#include "ap_mlme.h" #include "hw_features.h" #include "dfs.h" #include "beacon.h" #include "mbo_ap.h" +#include "dpp_hostapd.h" +#include "fils_hlp.h" + + +#ifdef CONFIG_FILS +void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd, + struct sta_info *sta) +{ + u16 reply_res = WLAN_STATUS_SUCCESS; + struct ieee802_11_elems elems; + u8 buf[IEEE80211_MAX_MMPDU_SIZE], *p = buf; + int new_assoc; + + wpa_printf(MSG_DEBUG, "%s FILS: Finish association with " MACSTR, + __func__, MAC2STR(sta->addr)); + eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); + if (!sta->fils_pending_assoc_req) + return; + + ieee802_11_parse_elems(sta->fils_pending_assoc_req, + sta->fils_pending_assoc_req_len, &elems, 0); + if (!elems.fils_session) { + wpa_printf(MSG_DEBUG, "%s failed to find FILS Session element", + __func__); + return; + } + + p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p, + elems.fils_session, + sta->fils_hlp_resp); + + reply_res = hostapd_sta_assoc(hapd, sta->addr, + sta->fils_pending_assoc_is_reassoc, + WLAN_STATUS_SUCCESS, + buf, p - buf); + ap_sta_set_authorized(hapd, sta, 1); + new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; + hostapd_set_sta_flags(hapd, sta); + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS); + ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); + hostapd_new_assoc_sta(hapd, sta, !new_assoc); + os_free(sta->fils_pending_assoc_req); + sta->fils_pending_assoc_req = NULL; + sta->fils_pending_assoc_req_len = 0; + wpabuf_free(sta->fils_hlp_resp); + sta->fils_hlp_resp = NULL; + wpabuf_free(sta->hlp_dhcp_discover); + sta->hlp_dhcp_discover = NULL; + fils_hlp_deinit(hapd); + + /* + * Remove the station in case transmission of a success response fails + * (the STA was added associated to the driver) or if the station was + * previously added unassociated. + */ + if (reply_res != WLAN_STATUS_SUCCESS || sta->added_unassoc) { + hostapd_drv_sta_remove(hapd, sta->addr); + sta->added_unassoc = 0; + } +} +#endif /* CONFIG_FILS */ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, @@ -45,10 +109,10 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, struct ieee802_11_elems elems; const u8 *ie; size_t ielen; -#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) +#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) || defined(CONFIG_OWE) u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; u8 *p = buf; -#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ +#endif /* CONFIG_IEEE80211R_AP || CONFIG_IEEE80211W || CONFIG_FILS || CONFIG_OWE */ u16 reason = WLAN_REASON_UNSPECIFIED; u16 status = WLAN_STATUS_SUCCESS; const u8 *p2p_dev_addr = NULL; @@ -171,6 +235,14 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, elems.hs20_len - 4); } else sta->hs20_ie = NULL; + + wpabuf_free(sta->roaming_consortium); + if (elems.roaming_cons_sel) + sta->roaming_consortium = wpabuf_alloc_copy( + elems.roaming_cons_sel + 4, + elems.roaming_cons_sel_len - 4); + else + sta->roaming_consortium = NULL; #endif /* CONFIG_HS20 */ #ifdef CONFIG_FST @@ -198,7 +270,9 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, #endif /* CONFIG_WPS */ wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA"); - return -1; + reason = WLAN_REASON_INVALID_IE; + status = WLAN_STATUS_INVALID_IE; + goto fail; } #ifdef CONFIG_WPS if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 && @@ -231,7 +305,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, ie, ielen, - elems.mdie, elems.mdie_len); + elems.mdie, elems.mdie_len, + elems.owe_dh, elems.owe_dh_len); if (res != WPA_IE_OK) { wpa_printf(MSG_DEBUG, "WPA/RSN information element rejected? (res %u)", @@ -252,8 +327,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, reason = WLAN_REASON_INVALID_IE; status = WLAN_STATUS_INVALID_IE; } else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) { - reason = WLAN_REASON_GROUP_CIPHER_NOT_VALID; - status = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; + reason = WLAN_REASON_CIPHER_SUITE_REJECTED; + status = WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; } #endif /* CONFIG_IEEE80211W */ else { @@ -263,10 +338,14 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, goto fail; } #ifdef CONFIG_IEEE80211W - if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) == + (WLAN_STA_ASSOC | WLAN_STA_MFP) && + !sta->sa_query_timed_out && sta->sa_query_count > 0) ap_check_sa_query_timeout(hapd, sta); - if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) == + (WLAN_STA_ASSOC | WLAN_STA_MFP) && + !sta->sa_query_timed_out && (sta->auth_alg != WLAN_AUTH_FT)) { /* * STA has already been associated with MFP and SA @@ -293,7 +372,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, sta->flags &= ~WLAN_STA_MFP; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (sta->auth_alg == WLAN_AUTH_FT) { status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies, req_ies_len); @@ -307,7 +386,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, goto fail; } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ } else if (hapd->conf->wps_state) { #ifdef CONFIG_WPS struct wpabuf *wps; @@ -375,19 +454,128 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, skip_wpa_check: #endif /* CONFIG_WPS */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf), sta->auth_alg, req_ies, req_ies_len); + if (!p) { + wpa_printf(MSG_DEBUG, "FT: Failed to write AssocResp IEs"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } +#endif /* CONFIG_IEEE80211R_AP */ + +#ifdef CONFIG_FILS + if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) { + int delay_assoc = 0; + + if (!req_ies) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + if (!wpa_fils_validate_fils_session(sta->wpa_sm, req_ies, + req_ies_len, + sta->fils_session)) { + wpa_printf(MSG_DEBUG, + "FILS: Session validation failed"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + res = wpa_fils_validate_key_confirm(sta->wpa_sm, req_ies, + req_ies_len); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "FILS: Key Confirm validation failed"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (fils_process_hlp(hapd, sta, req_ies, req_ies_len) > 0) { + wpa_printf(MSG_DEBUG, + "FILS: Delaying Assoc Response (HLP)"); + delay_assoc = 1; + } else { + wpa_printf(MSG_DEBUG, + "FILS: Going ahead with Assoc Response (no HLP)"); + } + + if (sta) { + wpa_printf(MSG_DEBUG, "FILS: HLP callback cleanup"); + eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); + os_free(sta->fils_pending_assoc_req); + sta->fils_pending_assoc_req = NULL; + sta->fils_pending_assoc_req_len = 0; + wpabuf_free(sta->fils_hlp_resp); + sta->fils_hlp_resp = NULL; + sta->fils_drv_assoc_finish = 0; + } + + if (sta && delay_assoc && status == WLAN_STATUS_SUCCESS) { + u8 *req_tmp; + + req_tmp = os_malloc(req_ies_len); + if (!req_tmp) { + wpa_printf(MSG_DEBUG, + "FILS: buffer allocation failed for assoc req"); + goto fail; + } + os_memcpy(req_tmp, req_ies, req_ies_len); + sta->fils_pending_assoc_req = req_tmp; + sta->fils_pending_assoc_req_len = req_ies_len; + sta->fils_pending_assoc_is_reassoc = reassoc; + sta->fils_drv_assoc_finish = 1; + wpa_printf(MSG_DEBUG, + "FILS: Waiting for HLP processing before sending (Re)Association Response frame to " + MACSTR, MAC2STR(sta->addr)); + eloop_register_timeout( + 0, hapd->conf->fils_hlp_wait_time * 1024, + fils_hlp_timeout, hapd, sta); + return 0; + } + p = hostapd_eid_assoc_fils_session(sta->wpa_sm, p, + elems.fils_session, + sta->fils_hlp_resp); + wpa_hexdump(MSG_DEBUG, "FILS Assoc Resp BUF (IEs)", + buf, p - buf); + } +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE && + elems.owe_dh) { + u8 *npos; + + npos = owe_assoc_req_process(hapd, sta, + elems.owe_dh, elems.owe_dh_len, + p, sizeof(buf) - (p - buf), + &reason); + if (npos) + p = npos; + if (!npos && + reason == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) { + status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + hostapd_sta_assoc(hapd, addr, reassoc, status, buf, + p - buf); + return 0; + } + + if (!npos || reason != WLAN_STATUS_SUCCESS) + goto fail; + } +#endif /* CONFIG_OWE */ +#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_FILS) || defined(CONFIG_OWE) hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); - if (sta->auth_alg == WLAN_AUTH_FT) + if (sta->auth_alg == WLAN_AUTH_FT || + sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) ap_sta_set_authorized(hapd, sta, 1); -#else /* CONFIG_IEEE80211R */ +#else /* CONFIG_IEEE80211R_AP || CONFIG_FILS */ /* Keep compiler silent about unused variables */ if (status) { } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP || CONFIG_FILS */ new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; @@ -397,6 +585,12 @@ skip_wpa_check: if (reassoc && (sta->auth_alg == WLAN_AUTH_FT)) wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); +#ifdef CONFIG_FILS + else if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FILS); +#endif /* CONFIG_FILS */ else wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); @@ -414,9 +608,9 @@ skip_wpa_check: return 0; fail: -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf); -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ hostapd_drv_sta_disassoc(hapd, sta->addr, reason); ap_free_sta(hapd, sta); return -1; @@ -464,15 +658,81 @@ void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta = ap_get_sta(hapd, addr); - if (!sta || !hapd->conf->disassoc_low_ack) + if (!sta || !hapd->conf->disassoc_low_ack || sta->agreed_to_steer) return; hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disconnected due to excessive missing ACKs"); hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK); - if (sta) - ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK); + ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK); +} + + +void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr, + enum smps_mode smps_mode, + enum chan_width chan_width, u8 rx_nss) +{ + struct sta_info *sta = ap_get_sta(hapd, addr); + const char *txt; + + if (!sta) + return; + + switch (smps_mode) { + case SMPS_AUTOMATIC: + txt = "automatic"; + break; + case SMPS_OFF: + txt = "off"; + break; + case SMPS_DYNAMIC: + txt = "dynamic"; + break; + case SMPS_STATIC: + txt = "static"; + break; + default: + txt = NULL; + break; + } + if (txt) { + wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_SMPS_MODE_CHANGED + MACSTR " %s", MAC2STR(addr), txt); + } + + switch (chan_width) { + case CHAN_WIDTH_20_NOHT: + txt = "20(no-HT)"; + break; + case CHAN_WIDTH_20: + txt = "20"; + break; + case CHAN_WIDTH_40: + txt = "40"; + break; + case CHAN_WIDTH_80: + txt = "80"; + break; + case CHAN_WIDTH_80P80: + txt = "80+80"; + break; + case CHAN_WIDTH_160: + txt = "160"; + break; + default: + txt = NULL; + break; + } + if (txt) { + wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_MAX_BW_CHANGED + MACSTR " %s", MAC2STR(addr), txt); + } + + if (rx_nss != 0xff) { + wpa_msg(hapd->msg_ctx, MSG_INFO, STA_OPMODE_N_SS_CHANGED + MACSTR " %d", MAC2STR(addr), rx_nss); + } } @@ -485,9 +745,9 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, - "driver had channel switch: freq=%d, ht=%d, offset=%d, width=%d (%s), cf1=%d, cf2=%d", - freq, ht, offset, width, channel_width_to_string(width), - cf1, cf2); + "driver had channel switch: freq=%d, ht=%d, vht_ch=0x%x, offset=%d, width=%d (%s), cf1=%d, cf2=%d", + freq, ht, hapd->iconf->ch_switch_vht_config, offset, + width, channel_width_to_string(width), cf1, cf2); hapd->iface->freq = freq; @@ -532,14 +792,26 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht, hapd->iconf->channel = channel; hapd->iconf->ieee80211n = ht; - if (!ht) + if (!ht) { hapd->iconf->ieee80211ac = 0; + } else if (hapd->iconf->ch_switch_vht_config) { + /* CHAN_SWITCH VHT config */ + if (hapd->iconf->ch_switch_vht_config & + CH_SWITCH_VHT_ENABLED) + hapd->iconf->ieee80211ac = 1; + else if (hapd->iconf->ch_switch_vht_config & + CH_SWITCH_VHT_DISABLED) + hapd->iconf->ieee80211ac = 0; + } + hapd->iconf->ch_switch_vht_config = 0; + hapd->iconf->secondary_channel = offset; hapd->iconf->vht_oper_chwidth = chwidth; hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx; hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx; - is_dfs = ieee80211_is_dfs(freq); + is_dfs = ieee80211_is_dfs(freq, hapd->iface->hw_features, + hapd->iface->num_hw_features); if (hapd->csa_in_progress && freq == hapd->cs_freq_params.freq) { @@ -690,7 +962,7 @@ int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da, #ifdef HOSTAPD -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, u16 auth_transaction, u16 status, @@ -709,7 +981,33 @@ static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst, hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len); } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ + + +#ifdef CONFIG_FILS +static void hostapd_notify_auth_fils_finish(struct hostapd_data *hapd, + struct sta_info *sta, u16 resp, + struct wpabuf *data, int pub) +{ + if (resp == WLAN_STATUS_SUCCESS) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "authentication OK (FILS)"); + sta->flags |= WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->auth_alg = WLAN_AUTH_FILS_SK; + mlme_authenticate_indication(hapd, sta); + } else { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "authentication failed (FILS)"); + } + + hostapd_sta_auth(hapd, sta->addr, 2, resp, + data ? wpabuf_head(data) : NULL, + data ? wpabuf_len(data) : 0); + wpabuf_free(data); +} +#endif /* CONFIG_FILS */ static void hostapd_notif_auth(struct hostapd_data *hapd, @@ -730,7 +1028,7 @@ static void hostapd_notif_auth(struct hostapd_data *hapd, } sta->flags &= ~WLAN_STA_PREAUTH; ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) { sta->auth_alg = WLAN_AUTH_FT; if (sta->wpa_sm == NULL) @@ -748,7 +1046,19 @@ static void hostapd_notif_auth(struct hostapd_data *hapd, hostapd_notify_auth_ft_finish, hapd); return; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ + +#ifdef CONFIG_FILS + if (rx_auth->auth_type == WLAN_AUTH_FILS_SK) { + sta->auth_alg = WLAN_AUTH_FILS_SK; + handle_auth_fils(hapd, sta, rx_auth->ies, rx_auth->ies_len, + rx_auth->auth_type, rx_auth->auth_transaction, + rx_auth->status_code, + hostapd_notify_auth_fils_finish); + return; + } +#endif /* CONFIG_FILS */ + fail: hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1, status, resp_ies, resp_ies_len); @@ -762,32 +1072,36 @@ static void hostapd_action_rx(struct hostapd_data *hapd, struct sta_info *sta; size_t plen __maybe_unused; u16 fc; + u8 *action __maybe_unused; - if (drv_mgmt->frame_len < 24 + 1) + if (drv_mgmt->frame_len < IEEE80211_HDRLEN + 2 + 1) return; - plen = drv_mgmt->frame_len - 24 - 1; + plen = drv_mgmt->frame_len - IEEE80211_HDRLEN - 1; mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame; fc = le_to_host16(mgmt->frame_control); if (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION) return; /* handled by the driver */ - wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d", - mgmt->u.action.category, (int) plen); + action = (u8 *) &mgmt->u.action.u; + wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR + " da " MACSTR " plen %d", + mgmt->u.action.category, *action, + MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) plen); sta = ap_get_sta(hapd, mgmt->sa); if (sta == NULL) { wpa_printf(MSG_DEBUG, "%s: station not found", __func__); return; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (mgmt->u.action.category == WLAN_ACTION_FT) { const u8 *payload = drv_mgmt->frame + 24 + 1; wpa_ft_action_rx(sta->wpa_sm, payload, plen); } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY && plen >= 4) { ieee802_11_sa_query_action( @@ -796,18 +1110,34 @@ static void hostapd_action_rx(struct hostapd_data *hapd, mgmt->u.action.u.sa_query_resp.trans_id); } #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP if (mgmt->u.action.category == WLAN_ACTION_WNM) { ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len); } -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ #ifdef CONFIG_FST if (mgmt->u.action.category == WLAN_ACTION_FST && hapd->iface->fst) { fst_rx_action(hapd->iface->fst, mgmt, drv_mgmt->frame_len); return; } #endif /* CONFIG_FST */ - +#ifdef CONFIG_DPP + if (plen >= 1 + 4 && + mgmt->u.action.u.vs_public_action.action == + WLAN_PA_VENDOR_SPECIFIC && + WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) == + OUI_WFA && + mgmt->u.action.u.vs_public_action.variable[0] == + DPP_OUI_TYPE) { + const u8 *pos, *end; + + pos = mgmt->u.action.u.vs_public_action.oui; + end = drv_mgmt->frame + drv_mgmt->frame_len; + hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos, + drv_mgmt->freq); + return; + } +#endif /* CONFIG_DPP */ } @@ -891,6 +1221,7 @@ static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) } os_memset(&fi, 0, sizeof(fi)); + fi.freq = rx_mgmt->freq; fi.datarate = rx_mgmt->datarate; fi.ssi_signal = rx_mgmt->ssi_signal; @@ -1122,6 +1453,16 @@ static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd, } +static void hostapd_event_dfs_pre_cac_expired(struct hostapd_data *hapd, + struct dfs_event *radar) +{ + wpa_printf(MSG_DEBUG, "DFS Pre-CAC expired on %d MHz", radar->freq); + hostapd_dfs_pre_cac_expired(hapd->iface, radar->freq, radar->ht_enabled, + radar->chan_offset, radar->chan_width, + radar->cf1, radar->cf2); +} + + static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd, struct dfs_event *radar) { @@ -1164,6 +1505,28 @@ static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd, #endif /* NEED_AP_MLME */ +static void hostapd_event_wds_sta_interface_status(struct hostapd_data *hapd, + int istatus, + const char *ifname, + const u8 *addr) +{ + struct sta_info *sta = ap_get_sta(hapd, addr); + + if (sta) { + os_free(sta->ifname_wds); + if (istatus == INTERFACE_ADDED) + sta->ifname_wds = os_strdup(ifname); + else + sta->ifname_wds = NULL; + } + + wpa_msg(hapd->msg_ctx, MSG_INFO, "%sifname=%s sta_addr=" MACSTR, + istatus == INTERFACE_ADDED ? + WDS_STA_INTERFACE_ADDED : WDS_STA_INTERFACE_REMOVED, + ifname, MAC2STR(addr)); +} + + void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data) { @@ -1314,6 +1677,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, break; hostapd_event_dfs_radar_detected(hapd, &data->dfs_event); break; + case EVENT_DFS_PRE_CAC_EXPIRED: + if (!data) + break; + hostapd_event_dfs_pre_cac_expired(hapd, &data->dfs_event); + break; case EVENT_DFS_CAC_FINISHED: if (!data) break; @@ -1351,7 +1719,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, * Try to re-enable interface if the driver stopped it * when the interface got disabled. */ - wpa_auth_reconfig_group_keys(hapd->wpa_auth); + if (hapd->wpa_auth) + wpa_auth_reconfig_group_keys(hapd->wpa_auth); + else + hostapd_reconfig_encryption(hapd); hapd->reenable_beacon = 1; ieee802_11_set_beacon(hapd); } @@ -1367,6 +1738,18 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, &data->acs_selected_channels); break; #endif /* CONFIG_ACS */ + case EVENT_STATION_OPMODE_CHANGED: + hostapd_event_sta_opmode_changed(hapd, data->sta_opmode.addr, + data->sta_opmode.smps_mode, + data->sta_opmode.chan_width, + data->sta_opmode.rx_nss); + break; + case EVENT_WDS_STA_INTERFACE_STATUS: + hostapd_event_wds_sta_interface_status( + hapd, data->wds_sta_interface.istatus, + data->wds_sta_interface.ifname, + data->wds_sta_interface.sta_addr); + break; default: wpa_printf(MSG_DEBUG, "Unknown event %d", event); break; diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c index 082d0f53175e1..296d5c2ddf311 100644 --- a/src/ap/eap_user_db.c +++ b/src/ap/eap_user_db.c @@ -91,6 +91,8 @@ static int get_user_cb(void *ctx, int argc, char *argv[], char *col[]) set_user_methods(user, argv[i]); } else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) { user->remediation = strlen(argv[i]) > 0; + } else if (os_strcmp(col[i], "t_c_timestamp") == 0 && argv[i]) { + user->t_c_timestamp = strtol(argv[i], NULL, 10); } } diff --git a/src/ap/eth_p_oui.c b/src/ap/eth_p_oui.c new file mode 100644 index 0000000000000..aba901e3fa750 --- /dev/null +++ b/src/ap/eth_p_oui.c @@ -0,0 +1,191 @@ +/* + * hostapd / IEEE 802 OUI Extended EtherType 88-B7 + * Copyright (c) 2016, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "l2_packet/l2_packet.h" +#include "hostapd.h" +#include "eth_p_oui.h" + +/* + * See IEEE Std 802-2014, Clause 9.2.4 for the definition of the OUI Extended + * EtherType 88-B7. This file implements this with OUI 00:13:74 and + * vendor-specific subtype 0x0001. + */ +static const u8 global_oui[] = { 0x00, 0x13, 0x74, 0x00, 0x01 }; + +struct eth_p_oui_iface { + struct dl_list list; + char ifname[IFNAMSIZ + 1]; + struct l2_packet_data *l2; + struct dl_list receiver; +}; + +struct eth_p_oui_ctx { + struct dl_list list; + struct eth_p_oui_iface *iface; + /* all data needed to deliver and unregister */ + u8 oui_suffix; /* last byte of OUI */ + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *dst_addr, u8 oui_suffix, + const u8 *buf, size_t len); + void *rx_callback_ctx; +}; + + +void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr, + const u8 *dst_addr, const u8 *buf, size_t len) +{ + ctx->rx_callback(ctx->rx_callback_ctx, src_addr, dst_addr, + ctx->oui_suffix, buf, len); +} + + +static void eth_p_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct eth_p_oui_iface *iface = ctx; + struct eth_p_oui_ctx *receiver; + const struct l2_ethhdr *ethhdr; + + if (len < sizeof(*ethhdr) + sizeof(global_oui) + 1) { + /* too short packet */ + return; + } + + ethhdr = (struct l2_ethhdr *) buf; + /* trim eth_hdr from buf and len */ + buf += sizeof(*ethhdr); + len -= sizeof(*ethhdr); + + /* verify OUI and vendor-specific subtype match */ + if (os_memcmp(buf, global_oui, sizeof(global_oui)) != 0) + return; + buf += sizeof(global_oui); + len -= sizeof(global_oui); + + dl_list_for_each(receiver, &iface->receiver, + struct eth_p_oui_ctx, list) { + if (buf[0] != receiver->oui_suffix) + continue; + + eth_p_oui_deliver(receiver, ethhdr->h_source, ethhdr->h_dest, + buf + 1, len - 1); + } +} + + +struct eth_p_oui_ctx * +eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *dst_addr, u8 oui_suffix, + const u8 *buf, size_t len), + void *rx_callback_ctx) +{ + struct eth_p_oui_iface *iface; + struct eth_p_oui_ctx *receiver; + int found = 0; + struct hapd_interfaces *interfaces; + + receiver = os_zalloc(sizeof(*receiver)); + if (!receiver) + goto err; + + receiver->oui_suffix = oui_suffix; + receiver->rx_callback = rx_callback; + receiver->rx_callback_ctx = rx_callback_ctx; + + interfaces = hapd->iface->interfaces; + + dl_list_for_each(iface, &interfaces->eth_p_oui, struct eth_p_oui_iface, + list) { + if (os_strcmp(iface->ifname, ifname) != 0) + continue; + found = 1; + break; + } + + if (!found) { + iface = os_zalloc(sizeof(*iface)); + if (!iface) + goto err; + + os_strlcpy(iface->ifname, ifname, sizeof(iface->ifname)); + iface->l2 = l2_packet_init(ifname, NULL, ETH_P_OUI, eth_p_rx, + iface, 1); + if (!iface->l2) { + os_free(iface); + goto err; + } + dl_list_init(&iface->receiver); + + dl_list_add_tail(&interfaces->eth_p_oui, &iface->list); + } + + dl_list_add_tail(&iface->receiver, &receiver->list); + receiver->iface = iface; + + return receiver; +err: + os_free(receiver); + return NULL; +} + + +void eth_p_oui_unregister(struct eth_p_oui_ctx *ctx) +{ + struct eth_p_oui_iface *iface; + + if (!ctx) + return; + + iface = ctx->iface; + + dl_list_del(&ctx->list); + os_free(ctx); + + if (dl_list_empty(&iface->receiver)) { + dl_list_del(&iface->list); + l2_packet_deinit(iface->l2); + os_free(iface); + } +} + + +int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr, + const u8 *dst_addr, const u8 *buf, size_t len) +{ + struct eth_p_oui_iface *iface = ctx->iface; + u8 *packet, *p; + size_t packet_len; + int ret; + struct l2_ethhdr *ethhdr; + + packet_len = sizeof(*ethhdr) + sizeof(global_oui) + 1 + len; + packet = os_zalloc(packet_len); + if (!packet) + return -1; + p = packet; + + ethhdr = (struct l2_ethhdr *) packet; + os_memcpy(ethhdr->h_source, src_addr, ETH_ALEN); + os_memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN); + ethhdr->h_proto = host_to_be16(ETH_P_OUI); + p += sizeof(*ethhdr); + + os_memcpy(p, global_oui, sizeof(global_oui)); + p[sizeof(global_oui)] = ctx->oui_suffix; + p += sizeof(global_oui) + 1; + + os_memcpy(p, buf, len); + + ret = l2_packet_send(iface->l2, NULL, 0, packet, packet_len); + os_free(packet); + return ret; +} diff --git a/src/ap/eth_p_oui.h b/src/ap/eth_p_oui.h new file mode 100644 index 0000000000000..466fdc39c6f86 --- /dev/null +++ b/src/ap/eth_p_oui.h @@ -0,0 +1,28 @@ +/* + * hostapd / IEEE 802 OUI Extended Ethertype + * Copyright (c) 2016, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef ETH_P_OUI_H +#define ETH_P_OUI_H + +struct eth_p_oui_ctx; +struct hostapd_data; + +/* rx_callback only gets payload after OUI passed as buf */ +struct eth_p_oui_ctx * +eth_p_oui_register(struct hostapd_data *hapd, const char *ifname, u8 oui_suffix, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *dst_addr, u8 oui_suffix, + const u8 *buf, size_t len), + void *rx_callback_ctx); +void eth_p_oui_unregister(struct eth_p_oui_ctx *eth_p_oui); +int eth_p_oui_send(struct eth_p_oui_ctx *ctx, const u8 *src_addr, + const u8 *dst_addr, const u8 *buf, size_t len); +void eth_p_oui_deliver(struct eth_p_oui_ctx *ctx, const u8 *src_addr, + const u8 *dst_addr, const u8 *buf, size_t len); + +#endif /* ETH_P_OUI_H */ diff --git a/src/ap/fils_hlp.c b/src/ap/fils_hlp.c new file mode 100644 index 0000000000000..2a359ab03c818 --- /dev/null +++ b/src/ap/fils_hlp.c @@ -0,0 +1,641 @@ +/* + * FILS HLP request processing + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/dhcp.h" +#include "hostapd.h" +#include "sta_info.h" +#include "ieee802_11.h" +#include "fils_hlp.h" + + +static be16 ip_checksum(const void *buf, size_t len) +{ + u32 sum = 0; + const u16 *pos; + + for (pos = buf; len >= 2; len -= 2) + sum += ntohs(*pos++); + if (len) + sum += ntohs(*pos << 8); + + sum = (sum >> 16) + (sum & 0xffff); + sum += sum >> 16; + return htons(~sum); +} + + +static int fils_dhcp_request(struct hostapd_data *hapd, struct sta_info *sta, + struct dhcp_data *dhcpoffer, u8 *dhcpofferend) +{ + u8 *pos, *end; + struct dhcp_data *dhcp; + struct sockaddr_in addr; + ssize_t res; + const u8 *server_id = NULL; + + if (!sta->hlp_dhcp_discover) { + wpa_printf(MSG_DEBUG, + "FILS: No pending HLP DHCPDISCOVER available"); + return -1; + } + + /* Convert to DHCPREQUEST, remove rapid commit option, replace requested + * IP address option with yiaddr. */ + pos = wpabuf_mhead(sta->hlp_dhcp_discover); + end = pos + wpabuf_len(sta->hlp_dhcp_discover); + dhcp = (struct dhcp_data *) pos; + pos = (u8 *) (dhcp + 1); + pos += 4; /* skip magic */ + while (pos < end && *pos != DHCP_OPT_END) { + u8 opt, olen; + + opt = *pos++; + if (opt == DHCP_OPT_PAD) + continue; + if (pos >= end) + break; + olen = *pos++; + if (olen > end - pos) + break; + + switch (opt) { + case DHCP_OPT_MSG_TYPE: + if (olen > 0) + *pos = DHCPREQUEST; + break; + case DHCP_OPT_RAPID_COMMIT: + case DHCP_OPT_REQUESTED_IP_ADDRESS: + case DHCP_OPT_SERVER_ID: + /* Remove option */ + pos -= 2; + os_memmove(pos, pos + 2 + olen, end - pos - 2 - olen); + end -= 2 + olen; + olen = 0; + break; + } + pos += olen; + } + if (pos >= end || *pos != DHCP_OPT_END) { + wpa_printf(MSG_DEBUG, "FILS: Could not update DHCPDISCOVER"); + return -1; + } + sta->hlp_dhcp_discover->used = pos - (u8 *) dhcp; + + /* Copy Server ID option from DHCPOFFER to DHCPREQUEST */ + pos = (u8 *) (dhcpoffer + 1); + end = dhcpofferend; + pos += 4; /* skip magic */ + while (pos < end && *pos != DHCP_OPT_END) { + u8 opt, olen; + + opt = *pos++; + if (opt == DHCP_OPT_PAD) + continue; + if (pos >= end) + break; + olen = *pos++; + if (olen > end - pos) + break; + + switch (opt) { + case DHCP_OPT_SERVER_ID: + server_id = pos - 2; + break; + } + pos += olen; + } + + if (wpabuf_resize(&sta->hlp_dhcp_discover, + 6 + 1 + (server_id ? 2 + server_id[1] : 0))) + return -1; + if (server_id) + wpabuf_put_data(sta->hlp_dhcp_discover, server_id, + 2 + server_id[1]); + wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_REQUESTED_IP_ADDRESS); + wpabuf_put_u8(sta->hlp_dhcp_discover, 4); + wpabuf_put_data(sta->hlp_dhcp_discover, &dhcpoffer->your_ip, 4); + wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_END); + + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr; + addr.sin_port = htons(hapd->conf->dhcp_server_port); + res = sendto(hapd->dhcp_sock, wpabuf_head(sta->hlp_dhcp_discover), + wpabuf_len(sta->hlp_dhcp_discover), 0, + (const struct sockaddr *) &addr, sizeof(addr)); + if (res < 0) { + wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s", + strerror(errno)); + return -1; + } + wpa_printf(MSG_DEBUG, + "FILS: Acting as DHCP rapid commit proxy for %s:%d", + inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + wpabuf_free(sta->hlp_dhcp_discover); + sta->hlp_dhcp_discover = NULL; + sta->fils_dhcp_rapid_commit_proxy = 1; + return 0; +} + + +static void fils_dhcp_handler(int sd, void *eloop_ctx, void *sock_ctx) +{ + struct hostapd_data *hapd = sock_ctx; + struct sta_info *sta; + u8 buf[1500], *pos, *end, *end_opt = NULL; + struct dhcp_data *dhcp; + struct sockaddr_in addr; + socklen_t addr_len; + ssize_t res; + u8 msgtype = 0; + int rapid_commit = 0; + struct iphdr *iph; + struct udphdr *udph; + struct wpabuf *resp; + const u8 *rpos; + size_t left, len; + + addr_len = sizeof(addr); + res = recvfrom(sd, buf, sizeof(buf), 0, + (struct sockaddr *) &addr, &addr_len); + if (res < 0) { + wpa_printf(MSG_DEBUG, "FILS: DHCP read failed: %s", + strerror(errno)); + return; + } + wpa_printf(MSG_DEBUG, "FILS: DHCP response from server %s:%d (len=%d)", + inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), (int) res); + wpa_hexdump(MSG_MSGDUMP, "FILS: HLP - DHCP server response", buf, res); + if ((size_t) res < sizeof(*dhcp)) + return; + dhcp = (struct dhcp_data *) buf; + if (dhcp->op != 2) + return; /* Not a BOOTREPLY */ + if (dhcp->relay_ip != hapd->conf->own_ip_addr.u.v4.s_addr) { + wpa_printf(MSG_DEBUG, + "FILS: HLP - DHCP response to unknown relay address 0x%x", + dhcp->relay_ip); + return; + } + dhcp->relay_ip = 0; + pos = (u8 *) (dhcp + 1); + end = &buf[res]; + + if (end - pos < 4 || WPA_GET_BE32(pos) != DHCP_MAGIC) { + wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic in response"); + return; + } + pos += 4; + + wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options in response", + pos, end - pos); + while (pos < end && *pos != DHCP_OPT_END) { + u8 opt, olen; + + opt = *pos++; + if (opt == DHCP_OPT_PAD) + continue; + if (pos >= end) + break; + olen = *pos++; + if (olen > end - pos) + break; + + switch (opt) { + case DHCP_OPT_MSG_TYPE: + if (olen > 0) + msgtype = pos[0]; + break; + case DHCP_OPT_RAPID_COMMIT: + rapid_commit = 1; + break; + } + pos += olen; + } + if (pos < end && *pos == DHCP_OPT_END) + end_opt = pos; + + wpa_printf(MSG_DEBUG, + "FILS: HLP - DHCP message type %u (rapid_commit=%d hw_addr=" + MACSTR ")", + msgtype, rapid_commit, MAC2STR(dhcp->hw_addr)); + + sta = ap_get_sta(hapd, dhcp->hw_addr); + if (!sta || !sta->fils_pending_assoc_req) { + wpa_printf(MSG_DEBUG, + "FILS: No pending HLP DHCP exchange with hw_addr " + MACSTR, MAC2STR(dhcp->hw_addr)); + return; + } + + if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPOFFER && + !rapid_commit) { + /* Use hostapd to take care of 4-message exchange and convert + * the final DHCPACK to rapid commit version. */ + if (fils_dhcp_request(hapd, sta, dhcp, end) == 0) + return; + /* failed, so send the server response as-is */ + } else if (msgtype != DHCPACK) { + wpa_printf(MSG_DEBUG, + "FILS: No DHCPACK available from the server and cannot do rapid commit proxying"); + } + + pos = buf; + resp = wpabuf_alloc(2 * ETH_ALEN + 6 + 2 + + sizeof(*iph) + sizeof(*udph) + (end - pos) + 2); + if (!resp) + return; + wpabuf_put_data(resp, sta->addr, ETH_ALEN); + wpabuf_put_data(resp, hapd->own_addr, ETH_ALEN); + wpabuf_put_data(resp, "\xaa\xaa\x03\x00\x00\x00", 6); + wpabuf_put_be16(resp, ETH_P_IP); + iph = wpabuf_put(resp, sizeof(*iph)); + iph->version = 4; + iph->ihl = sizeof(*iph) / 4; + iph->tot_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos)); + iph->ttl = 1; + iph->protocol = 17; /* UDP */ + iph->saddr = hapd->conf->dhcp_server.u.v4.s_addr; + iph->daddr = dhcp->client_ip; + iph->check = ip_checksum(iph, sizeof(*iph)); + udph = wpabuf_put(resp, sizeof(*udph)); + udph->uh_sport = htons(DHCP_SERVER_PORT); + udph->uh_dport = htons(DHCP_CLIENT_PORT); + udph->uh_ulen = htons(sizeof(*udph) + (end - pos)); + udph->uh_sum = htons(0x0000); /* TODO: calculate checksum */ + if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPACK && + !rapid_commit && sta->fils_dhcp_rapid_commit_proxy && end_opt) { + /* Add rapid commit option */ + wpabuf_put_data(resp, pos, end_opt - pos); + wpabuf_put_u8(resp, DHCP_OPT_RAPID_COMMIT); + wpabuf_put_u8(resp, 0); + wpabuf_put_data(resp, end_opt, end - end_opt); + } else { + wpabuf_put_data(resp, pos, end - pos); + } + if (wpabuf_resize(&sta->fils_hlp_resp, wpabuf_len(resp) + + 2 * wpabuf_len(resp) / 255 + 100)) { + wpabuf_free(resp); + return; + } + + rpos = wpabuf_head(resp); + left = wpabuf_len(resp); + + wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXTENSION); /* Element ID */ + if (left <= 254) + len = 1 + left; + else + len = 255; + wpabuf_put_u8(sta->fils_hlp_resp, len); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXT_FILS_HLP_CONTAINER); + /* Destination MAC Address, Source MAC Address, HLP Packet. + * HLP Packet is in MSDU format (i.e., including the LLC/SNAP header + * when LPD is used). */ + wpabuf_put_data(sta->fils_hlp_resp, rpos, len - 1); + rpos += len - 1; + left -= len - 1; + while (left) { + wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_FRAGMENT); + len = left > 255 ? 255 : left; + wpabuf_put_u8(sta->fils_hlp_resp, len); + wpabuf_put_data(sta->fils_hlp_resp, rpos, len); + rpos += len; + left -= len; + } + wpabuf_free(resp); + + if (sta->fils_drv_assoc_finish) + hostapd_notify_assoc_fils_finish(hapd, sta); + else + fils_hlp_finish_assoc(hapd, sta); +} + + +static int fils_process_hlp_dhcp(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *msg, size_t len) +{ + const struct dhcp_data *dhcp; + struct wpabuf *dhcp_buf; + struct dhcp_data *dhcp_msg; + u8 msgtype = 0; + int rapid_commit = 0; + const u8 *pos = msg, *end; + struct sockaddr_in addr; + ssize_t res; + + if (len < sizeof(*dhcp)) + return 0; + dhcp = (const struct dhcp_data *) pos; + end = pos + len; + wpa_printf(MSG_DEBUG, + "FILS: HLP request DHCP: op=%u htype=%u hlen=%u hops=%u xid=0x%x", + dhcp->op, dhcp->htype, dhcp->hlen, dhcp->hops, + ntohl(dhcp->xid)); + pos += sizeof(*dhcp); + if (dhcp->op != 1) + return 0; /* Not a BOOTREQUEST */ + + if (end - pos < 4) + return 0; + if (WPA_GET_BE32(pos) != DHCP_MAGIC) { + wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic"); + return 0; + } + pos += 4; + + wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options", pos, end - pos); + while (pos < end && *pos != DHCP_OPT_END) { + u8 opt, olen; + + opt = *pos++; + if (opt == DHCP_OPT_PAD) + continue; + if (pos >= end) + break; + olen = *pos++; + if (olen > end - pos) + break; + + switch (opt) { + case DHCP_OPT_MSG_TYPE: + if (olen > 0) + msgtype = pos[0]; + break; + case DHCP_OPT_RAPID_COMMIT: + rapid_commit = 1; + break; + } + pos += olen; + } + + wpa_printf(MSG_DEBUG, "FILS: HLP - DHCP message type %u", msgtype); + if (msgtype != DHCPDISCOVER) + return 0; + + if (hapd->conf->dhcp_server.af != AF_INET || + hapd->conf->dhcp_server.u.v4.s_addr == 0) { + wpa_printf(MSG_DEBUG, + "FILS: HLP - no DHCPv4 server configured - drop request"); + return 0; + } + + if (hapd->conf->own_ip_addr.af != AF_INET || + hapd->conf->own_ip_addr.u.v4.s_addr == 0) { + wpa_printf(MSG_DEBUG, + "FILS: HLP - no IPv4 own_ip_addr configured - drop request"); + return 0; + } + + if (hapd->dhcp_sock < 0) { + int s; + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, + "FILS: Failed to open DHCP socket: %s", + strerror(errno)); + return 0; + } + + if (hapd->conf->dhcp_relay_port) { + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = + hapd->conf->own_ip_addr.u.v4.s_addr; + addr.sin_port = htons(hapd->conf->dhcp_relay_port); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr))) { + wpa_printf(MSG_ERROR, + "FILS: Failed to bind DHCP socket: %s", + strerror(errno)); + close(s); + return 0; + } + } + if (eloop_register_sock(s, EVENT_TYPE_READ, + fils_dhcp_handler, NULL, hapd)) { + close(s); + return 0; + } + + hapd->dhcp_sock = s; + } + + dhcp_buf = wpabuf_alloc(len); + if (!dhcp_buf) + return 0; + dhcp_msg = wpabuf_put(dhcp_buf, len); + os_memcpy(dhcp_msg, msg, len); + dhcp_msg->relay_ip = hapd->conf->own_ip_addr.u.v4.s_addr; + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr; + addr.sin_port = htons(hapd->conf->dhcp_server_port); + res = sendto(hapd->dhcp_sock, dhcp_msg, len, 0, + (const struct sockaddr *) &addr, sizeof(addr)); + if (res < 0) { + wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s", + strerror(errno)); + wpabuf_free(dhcp_buf); + /* Close the socket to try to recover from error */ + eloop_unregister_read_sock(hapd->dhcp_sock); + close(hapd->dhcp_sock); + hapd->dhcp_sock = -1; + return 0; + } + + wpa_printf(MSG_DEBUG, + "FILS: HLP relayed DHCP request to server %s:%d (rapid_commit=%d)", + inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), + rapid_commit); + if (hapd->conf->dhcp_rapid_commit_proxy && rapid_commit) { + /* Store a copy of the DHCPDISCOVER for rapid commit proxying + * purposes if the server does not support the rapid commit + * option. */ + wpa_printf(MSG_DEBUG, + "FILS: Store DHCPDISCOVER for rapid commit proxy"); + wpabuf_free(sta->hlp_dhcp_discover); + sta->hlp_dhcp_discover = dhcp_buf; + } else { + wpabuf_free(dhcp_buf); + } + + return 1; +} + + +static int fils_process_hlp_udp(struct hostapd_data *hapd, + struct sta_info *sta, const u8 *dst, + const u8 *pos, size_t len) +{ + const struct iphdr *iph; + const struct udphdr *udph; + u16 sport, dport, ulen; + + if (len < sizeof(*iph) + sizeof(*udph)) + return 0; + iph = (const struct iphdr *) pos; + udph = (const struct udphdr *) (iph + 1); + sport = ntohs(udph->uh_sport); + dport = ntohs(udph->uh_dport); + ulen = ntohs(udph->uh_ulen); + wpa_printf(MSG_DEBUG, + "FILS: HLP request UDP: sport=%u dport=%u ulen=%u sum=0x%x", + sport, dport, ulen, ntohs(udph->uh_sum)); + /* TODO: Check UDP checksum */ + if (ulen < sizeof(*udph) || ulen > len - sizeof(*iph)) + return 0; + + if (dport == DHCP_SERVER_PORT && sport == DHCP_CLIENT_PORT) { + return fils_process_hlp_dhcp(hapd, sta, (const u8 *) (udph + 1), + ulen - sizeof(*udph)); + } + + return 0; +} + + +static int fils_process_hlp_ip(struct hostapd_data *hapd, + struct sta_info *sta, const u8 *dst, + const u8 *pos, size_t len) +{ + const struct iphdr *iph; + u16 tot_len; + + if (len < sizeof(*iph)) + return 0; + iph = (const struct iphdr *) pos; + if (ip_checksum(iph, sizeof(*iph)) != 0) { + wpa_printf(MSG_DEBUG, + "FILS: HLP request IPv4 packet had invalid header checksum - dropped"); + return 0; + } + tot_len = ntohs(iph->tot_len); + if (tot_len > len) + return 0; + wpa_printf(MSG_DEBUG, + "FILS: HLP request IPv4: saddr=%08x daddr=%08x protocol=%u", + iph->saddr, iph->daddr, iph->protocol); + switch (iph->protocol) { + case 17: + return fils_process_hlp_udp(hapd, sta, dst, pos, len); + } + + return 0; +} + + +static int fils_process_hlp_req(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *pos, size_t len) +{ + const u8 *pkt, *end; + + wpa_printf(MSG_DEBUG, "FILS: HLP request from " MACSTR " (dst=" MACSTR + " src=" MACSTR " len=%u)", + MAC2STR(sta->addr), MAC2STR(pos), MAC2STR(pos + ETH_ALEN), + (unsigned int) len); + if (os_memcmp(sta->addr, pos + ETH_ALEN, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, + "FILS: Ignore HLP request with unexpected source address" + MACSTR, MAC2STR(pos + ETH_ALEN)); + return 0; + } + + end = pos + len; + pkt = pos + 2 * ETH_ALEN; + if (end - pkt >= 6 && + os_memcmp(pkt, "\xaa\xaa\x03\x00\x00\x00", 6) == 0) + pkt += 6; /* Remove SNAP/LLC header */ + wpa_hexdump(MSG_MSGDUMP, "FILS: HLP request packet", pkt, end - pkt); + + if (end - pkt < 2) + return 0; + + switch (WPA_GET_BE16(pkt)) { + case ETH_P_IP: + return fils_process_hlp_ip(hapd, sta, pos, pkt + 2, + end - pkt - 2); + } + + return 0; +} + + +int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *pos, int left) +{ + const u8 *end = pos + left; + u8 *tmp, *tmp_pos; + int ret = 0; + + /* Old DHCPDISCOVER is not needed anymore, if it was still pending */ + wpabuf_free(sta->hlp_dhcp_discover); + sta->hlp_dhcp_discover = NULL; + sta->fils_dhcp_rapid_commit_proxy = 0; + + /* Check if there are any FILS HLP Container elements */ + while (end - pos >= 2) { + if (2 + pos[1] > end - pos) + return 0; + if (pos[0] == WLAN_EID_EXTENSION && + pos[1] >= 1 + 2 * ETH_ALEN && + pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER) + break; + pos += 2 + pos[1]; + } + if (end - pos < 2) + return 0; /* No FILS HLP Container elements */ + + tmp = os_malloc(end - pos); + if (!tmp) + return 0; + + while (end - pos >= 2) { + if (2 + pos[1] > end - pos || + pos[0] != WLAN_EID_EXTENSION || + pos[1] < 1 + 2 * ETH_ALEN || + pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER) + break; + tmp_pos = tmp; + os_memcpy(tmp_pos, pos + 3, pos[1] - 1); + tmp_pos += pos[1] - 1; + pos += 2 + pos[1]; + + /* Add possible fragments */ + while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT && + 2 + pos[1] <= end - pos) { + os_memcpy(tmp_pos, pos + 2, pos[1]); + tmp_pos += pos[1]; + pos += 2 + pos[1]; + } + + if (fils_process_hlp_req(hapd, sta, tmp, tmp_pos - tmp) > 0) + ret = 1; + } + + os_free(tmp); + + return ret; +} + + +void fils_hlp_deinit(struct hostapd_data *hapd) +{ + if (hapd->dhcp_sock >= 0) { + eloop_unregister_read_sock(hapd->dhcp_sock); + close(hapd->dhcp_sock); + hapd->dhcp_sock = -1; + } +} diff --git a/src/ap/fils_hlp.h b/src/ap/fils_hlp.h new file mode 100644 index 0000000000000..e14a6bf65e04b --- /dev/null +++ b/src/ap/fils_hlp.h @@ -0,0 +1,27 @@ +/* + * FILS HLP request processing + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef FILS_HLP_H +#define FILS_HLP_H + +int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *pos, int left); + +#ifdef CONFIG_FILS + +void fils_hlp_deinit(struct hostapd_data *hapd); + +#else /* CONFIG_FILS */ + +static inline void fils_hlp_deinit(struct hostapd_data *hapd) +{ +} + +#endif /* CONFIG_FILS */ + +#endif /* FILS_HLP_H */ diff --git a/src/ap/gas_query_ap.c b/src/ap/gas_query_ap.c new file mode 100644 index 0000000000000..fdb3cad55adee --- /dev/null +++ b/src/ap/gas_query_ap.c @@ -0,0 +1,714 @@ +/* + * Generic advertisement service (GAS) query (hostapd) + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011-2017, Qualcomm Atheros, Inc. + * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "utils/eloop.h" +#include "utils/list.h" +#include "common/ieee802_11_defs.h" +#include "common/gas.h" +#include "common/wpa_ctrl.h" +#include "hostapd.h" +#include "sta_info.h" +#include "ap_drv_ops.h" +#include "gas_query_ap.h" + + +/** GAS query timeout in seconds */ +#define GAS_QUERY_TIMEOUT_PERIOD 2 + +/* GAS query wait-time / duration in ms */ +#define GAS_QUERY_WAIT_TIME_INITIAL 1000 +#define GAS_QUERY_WAIT_TIME_COMEBACK 150 + +/** + * struct gas_query_pending - Pending GAS query + */ +struct gas_query_pending { + struct dl_list list; + struct gas_query_ap *gas; + u8 addr[ETH_ALEN]; + u8 dialog_token; + u8 next_frag_id; + unsigned int wait_comeback:1; + unsigned int offchannel_tx_started:1; + unsigned int retry:1; + int freq; + u16 status_code; + struct wpabuf *req; + struct wpabuf *adv_proto; + struct wpabuf *resp; + struct os_reltime last_oper; + void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, + enum gas_query_ap_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code); + void *ctx; + u8 sa[ETH_ALEN]; +}; + +/** + * struct gas_query_ap - Internal GAS query data + */ +struct gas_query_ap { + struct hostapd_data *hapd; + void *msg_ctx; + struct dl_list pending; /* struct gas_query_pending */ + struct gas_query_pending *current; +}; + + +static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx); +static void gas_query_timeout(void *eloop_data, void *user_ctx); +static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx); +static void gas_query_tx_initial_req(struct gas_query_ap *gas, + struct gas_query_pending *query); +static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst); + + +static int ms_from_time(struct os_reltime *last) +{ + struct os_reltime now, res; + + os_get_reltime(&now); + os_reltime_sub(&now, last, &res); + return res.sec * 1000 + res.usec / 1000; +} + + +/** + * gas_query_ap_init - Initialize GAS query component + * @hapd: Pointer to hostapd data + * Returns: Pointer to GAS query data or %NULL on failure + */ +struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd, + void *msg_ctx) +{ + struct gas_query_ap *gas; + + gas = os_zalloc(sizeof(*gas)); + if (!gas) + return NULL; + + gas->hapd = hapd; + gas->msg_ctx = msg_ctx; + dl_list_init(&gas->pending); + + return gas; +} + + +static const char * gas_result_txt(enum gas_query_ap_result result) +{ + switch (result) { + case GAS_QUERY_AP_SUCCESS: + return "SUCCESS"; + case GAS_QUERY_AP_FAILURE: + return "FAILURE"; + case GAS_QUERY_AP_TIMEOUT: + return "TIMEOUT"; + case GAS_QUERY_AP_PEER_ERROR: + return "PEER_ERROR"; + case GAS_QUERY_AP_INTERNAL_ERROR: + return "INTERNAL_ERROR"; + case GAS_QUERY_AP_DELETED_AT_DEINIT: + return "DELETED_AT_DEINIT"; + } + + return "N/A"; +} + + +static void gas_query_free(struct gas_query_pending *query, int del_list) +{ + if (del_list) + dl_list_del(&query->list); + + wpabuf_free(query->req); + wpabuf_free(query->adv_proto); + wpabuf_free(query->resp); + os_free(query); +} + + +static void gas_query_done(struct gas_query_ap *gas, + struct gas_query_pending *query, + enum gas_query_ap_result result) +{ + wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR + " dialog_token=%u freq=%d status_code=%u result=%s", + MAC2STR(query->addr), query->dialog_token, query->freq, + query->status_code, gas_result_txt(result)); + if (gas->current == query) + gas->current = NULL; + eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); + eloop_cancel_timeout(gas_query_timeout, gas, query); + eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query); + dl_list_del(&query->list); + query->cb(query->ctx, query->addr, query->dialog_token, result, + query->adv_proto, query->resp, query->status_code); + gas_query_free(query, 0); +} + + +/** + * gas_query_ap_deinit - Deinitialize GAS query component + * @gas: GAS query data from gas_query_init() + */ +void gas_query_ap_deinit(struct gas_query_ap *gas) +{ + struct gas_query_pending *query, *next; + + if (gas == NULL) + return; + + dl_list_for_each_safe(query, next, &gas->pending, + struct gas_query_pending, list) + gas_query_done(gas, query, GAS_QUERY_AP_DELETED_AT_DEINIT); + + os_free(gas); +} + + +static struct gas_query_pending * +gas_query_get_pending(struct gas_query_ap *gas, const u8 *addr, u8 dialog_token) +{ + struct gas_query_pending *q; + dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) { + if (os_memcmp(q->addr, addr, ETH_ALEN) == 0 && + q->dialog_token == dialog_token) + return q; + } + return NULL; +} + + +static int gas_query_append(struct gas_query_pending *query, const u8 *data, + size_t len) +{ + if (wpabuf_resize(&query->resp, len) < 0) { + wpa_printf(MSG_DEBUG, "GAS: No memory to store the response"); + return -1; + } + wpabuf_put_data(query->resp, data, len); + return 0; +} + + +void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst, + const u8 *data, size_t data_len, int ok) +{ + struct gas_query_pending *query; + int dur; + + if (!gas || !gas->current) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: dst=" MACSTR + " ok=%d - no query in progress", MAC2STR(dst), ok); + return; + } + + query = gas->current; + + dur = ms_from_time(&query->last_oper); + wpa_printf(MSG_DEBUG, "GAS: TX status: dst=" MACSTR + " ok=%d query=%p dialog_token=%u dur=%d ms", + MAC2STR(dst), ok, query, query->dialog_token, dur); + if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination"); + return; + } + os_get_reltime(&query->last_oper); + + eloop_cancel_timeout(gas_query_timeout, gas, query); + if (!ok) { + 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); + eloop_register_timeout( + 0, (GAS_QUERY_WAIT_TIME_COMEBACK + 10) * 1000, + gas_query_rx_comeback_timeout, gas, query); + } +} + + +static int pmf_in_use(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + return sta && (sta->flags & WLAN_STA_MFP); +} + + +static int gas_query_tx(struct gas_query_ap *gas, + struct gas_query_pending *query, + struct wpabuf *req, unsigned int wait_time) +{ + int res, prot = pmf_in_use(gas->hapd, query->addr); + + wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u " + "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; + } + os_get_reltime(&query->last_oper); + res = hostapd_drv_send_action(gas->hapd, query->freq, wait_time, + query->addr, wpabuf_head(req), + wpabuf_len(req)); + return res; +} + + +static void gas_query_tx_comeback_req(struct gas_query_ap *gas, + struct gas_query_pending *query) +{ + struct wpabuf *req; + unsigned int wait_time; + + req = gas_build_comeback_req(query->dialog_token); + if (req == NULL) { + gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); + return; + } + + wait_time = (query->retry || !query->offchannel_tx_started) ? + GAS_QUERY_WAIT_TIME_INITIAL : GAS_QUERY_WAIT_TIME_COMEBACK; + + if (gas_query_tx(gas, query, req, wait_time) < 0) { + wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " + MACSTR, MAC2STR(query->addr)); + gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); + } + + wpabuf_free(req); +} + + +static void gas_query_rx_comeback_timeout(void *eloop_data, void *user_ctx) +{ + struct gas_query_ap *gas = eloop_data; + struct gas_query_pending *query = user_ctx; + int dialog_token; + + wpa_printf(MSG_DEBUG, + "GAS: No response to comeback request received (retry=%u)", + query->retry); + if (gas->current != query || query->retry) + return; + dialog_token = gas_query_new_dialog_token(gas, query->addr); + if (dialog_token < 0) + return; + wpa_printf(MSG_DEBUG, + "GAS: Retry GAS query due to comeback response timeout"); + query->retry = 1; + query->dialog_token = dialog_token; + *(wpabuf_mhead_u8(query->req) + 2) = dialog_token; + query->wait_comeback = 0; + query->next_frag_id = 0; + wpabuf_free(query->adv_proto); + query->adv_proto = NULL; + eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); + eloop_cancel_timeout(gas_query_timeout, gas, query); + gas_query_tx_initial_req(gas, query); +} + + +static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx) +{ + struct gas_query_ap *gas = eloop_data; + struct gas_query_pending *query = user_ctx; + + wpa_printf(MSG_DEBUG, "GAS: Comeback timeout for request to " MACSTR, + MAC2STR(query->addr)); + gas_query_tx_comeback_req(gas, query); +} + + +static void gas_query_tx_comeback_req_delay(struct gas_query_ap *gas, + struct gas_query_pending *query, + u16 comeback_delay) +{ + unsigned int secs, usecs; + + secs = (comeback_delay * 1024) / 1000000; + usecs = comeback_delay * 1024 - secs * 1000000; + wpa_printf(MSG_DEBUG, "GAS: Send comeback request to " MACSTR + " in %u secs %u usecs", MAC2STR(query->addr), secs, usecs); + eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query); + eloop_register_timeout(secs, usecs, gas_query_tx_comeback_timeout, + gas, query); +} + + +static void gas_query_rx_initial(struct gas_query_ap *gas, + struct gas_query_pending *query, + const u8 *adv_proto, const u8 *resp, + size_t len, u16 comeback_delay) +{ + wpa_printf(MSG_DEBUG, "GAS: Received initial response from " + MACSTR " (dialog_token=%u comeback_delay=%u)", + MAC2STR(query->addr), query->dialog_token, comeback_delay); + + query->adv_proto = wpabuf_alloc_copy(adv_proto, 2 + adv_proto[1]); + if (query->adv_proto == NULL) { + gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); + return; + } + + 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; + } + + /* Query was completed without comeback mechanism */ + if (gas_query_append(query, resp, len) < 0) { + gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); + return; + } + + gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS); +} + + +static void gas_query_rx_comeback(struct gas_query_ap *gas, + struct gas_query_pending *query, + const u8 *adv_proto, const u8 *resp, + size_t len, u8 frag_id, u8 more_frags, + u16 comeback_delay) +{ + wpa_printf(MSG_DEBUG, "GAS: Received comeback response from " + MACSTR " (dialog_token=%u frag_id=%u more_frags=%u " + "comeback_delay=%u)", + MAC2STR(query->addr), query->dialog_token, frag_id, + more_frags, comeback_delay); + eloop_cancel_timeout(gas_query_rx_comeback_timeout, gas, query); + + if ((size_t) 2 + adv_proto[1] != wpabuf_len(query->adv_proto) || + os_memcmp(adv_proto, wpabuf_head(query->adv_proto), + wpabuf_len(query->adv_proto)) != 0) { + wpa_printf(MSG_DEBUG, "GAS: Advertisement Protocol changed " + "between initial and comeback response from " + MACSTR, MAC2STR(query->addr)); + gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR); + return; + } + + if (comeback_delay) { + if (frag_id) { + wpa_printf(MSG_DEBUG, "GAS: Invalid comeback response " + "with non-zero frag_id and comeback_delay " + "from " MACSTR, MAC2STR(query->addr)); + gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR); + return; + } + gas_query_tx_comeback_req_delay(gas, query, comeback_delay); + return; + } + + if (frag_id != query->next_frag_id) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected frag_id in response " + "from " MACSTR, MAC2STR(query->addr)); + if (frag_id + 1 == query->next_frag_id) { + wpa_printf(MSG_DEBUG, "GAS: Drop frame as possible " + "retry of previous fragment"); + return; + } + gas_query_done(gas, query, GAS_QUERY_AP_PEER_ERROR); + return; + } + query->next_frag_id++; + + if (gas_query_append(query, resp, len) < 0) { + gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); + return; + } + + if (more_frags) { + gas_query_tx_comeback_req(gas, query); + return; + } + + gas_query_done(gas, query, GAS_QUERY_AP_SUCCESS); +} + + +/** + * gas_query_ap_rx - Indicate reception of a Public Action or Protected Dual + * frame + * @gas: GAS query data from gas_query_init() + * @sa: Source MAC address of the Action frame + * @categ: Category of the Action frame + * @data: Payload of the Action frame + * @len: Length of @data + * @freq: Frequency (in MHz) on which the frame was received + * Returns: 0 if the Public Action frame was a GAS frame or -1 if not + */ +int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ, + const u8 *data, size_t len, int freq) +{ + struct gas_query_pending *query; + u8 action, dialog_token, frag_id = 0, more_frags = 0; + u16 comeback_delay, resp_len; + const u8 *pos, *adv_proto; + int prot, pmf; + unsigned int left; + + if (!gas || len < 4) + return -1; + + pos = data; + action = *pos++; + dialog_token = *pos++; + + if (action != WLAN_PA_GAS_INITIAL_RESP && + action != WLAN_PA_GAS_COMEBACK_RESP) + return -1; /* Not a GAS response */ + + prot = categ == WLAN_ACTION_PROTECTED_DUAL; + pmf = pmf_in_use(gas->hapd, sa); + if (prot && !pmf) { + wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled"); + return 0; + } + if (!prot && pmf) { + wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled"); + return 0; + } + + query = gas_query_get_pending(gas, sa, dialog_token); + if (query == NULL) { + wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR + " dialog token %u", MAC2STR(sa), dialog_token); + return -1; + } + + wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR, + ms_from_time(&query->last_oper), MAC2STR(sa)); + + if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from " + MACSTR " dialog token %u when waiting for comeback " + "response", MAC2STR(sa), dialog_token); + return 0; + } + + if (!query->wait_comeback && action == WLAN_PA_GAS_COMEBACK_RESP) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected comeback response from " + MACSTR " dialog token %u when waiting for initial " + "response", MAC2STR(sa), dialog_token); + return 0; + } + + query->status_code = WPA_GET_LE16(pos); + pos += 2; + + if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING && + action == WLAN_PA_GAS_COMEBACK_RESP) { + wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response"); + } else if (query->status_code != WLAN_STATUS_SUCCESS) { + wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token " + "%u failed - status code %u", + MAC2STR(sa), dialog_token, query->status_code); + gas_query_done(gas, query, GAS_QUERY_AP_FAILURE); + return 0; + } + + if (action == WLAN_PA_GAS_COMEBACK_RESP) { + if (pos + 1 > data + len) + return 0; + frag_id = *pos & 0x7f; + more_frags = (*pos & 0x80) >> 7; + pos++; + } + + /* Comeback Delay */ + if (pos + 2 > data + len) + return 0; + comeback_delay = WPA_GET_LE16(pos); + pos += 2; + + /* Advertisement Protocol element */ + if (pos + 2 > data + len || pos + 2 + pos[1] > data + len) { + wpa_printf(MSG_DEBUG, "GAS: No room for Advertisement " + "Protocol element in the response from " MACSTR, + MAC2STR(sa)); + return 0; + } + + if (*pos != WLAN_EID_ADV_PROTO) { + wpa_printf(MSG_DEBUG, "GAS: Unexpected Advertisement " + "Protocol element ID %u in response from " MACSTR, + *pos, MAC2STR(sa)); + return 0; + } + + adv_proto = pos; + pos += 2 + pos[1]; + + /* Query Response Length */ + if (pos + 2 > data + len) { + wpa_printf(MSG_DEBUG, "GAS: No room for GAS Response Length"); + return 0; + } + resp_len = WPA_GET_LE16(pos); + pos += 2; + + left = data + len - pos; + if (resp_len > left) { + wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in " + "response from " MACSTR, MAC2STR(sa)); + return 0; + } + + if (resp_len < left) { + wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data " + "after Query Response from " MACSTR, + left - resp_len, MAC2STR(sa)); + } + + if (action == WLAN_PA_GAS_COMEBACK_RESP) + gas_query_rx_comeback(gas, query, adv_proto, pos, resp_len, + frag_id, more_frags, comeback_delay); + else + gas_query_rx_initial(gas, query, adv_proto, pos, resp_len, + comeback_delay); + + return 0; +} + + +static void gas_query_timeout(void *eloop_data, void *user_ctx) +{ + struct gas_query_ap *gas = eloop_data; + struct gas_query_pending *query = user_ctx; + + wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR + " dialog token %u", + MAC2STR(query->addr), query->dialog_token); + gas_query_done(gas, query, GAS_QUERY_AP_TIMEOUT); +} + + +static int gas_query_dialog_token_available(struct gas_query_ap *gas, + const u8 *dst, u8 dialog_token) +{ + struct gas_query_pending *q; + dl_list_for_each(q, &gas->pending, struct gas_query_pending, list) { + if (os_memcmp(dst, q->addr, ETH_ALEN) == 0 && + dialog_token == q->dialog_token) + return 0; + } + + return 1; +} + + +static void gas_query_tx_initial_req(struct gas_query_ap *gas, + struct gas_query_pending *query) +{ + if (gas_query_tx(gas, query, query->req, + GAS_QUERY_WAIT_TIME_INITIAL) < 0) { + wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to " + MACSTR, MAC2STR(query->addr)); + gas_query_done(gas, query, GAS_QUERY_AP_INTERNAL_ERROR); + return; + } + gas->current = query; + + wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u", + query->dialog_token); + eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, + gas_query_timeout, gas, query); +} + + +static int gas_query_new_dialog_token(struct gas_query_ap *gas, const u8 *dst) +{ + static int next_start = 0; + int dialog_token; + + for (dialog_token = 0; dialog_token < 256; dialog_token++) { + if (gas_query_dialog_token_available( + gas, dst, (next_start + dialog_token) % 256)) + break; + } + if (dialog_token == 256) + return -1; /* Too many pending queries */ + dialog_token = (next_start + dialog_token) % 256; + next_start = (dialog_token + 1) % 256; + return dialog_token; +} + + +/** + * gas_query_ap_req - Request a GAS query + * @gas: GAS query data from gas_query_init() + * @dst: Destination MAC address for the query + * @freq: Frequency (in MHz) for the channel on which to send the query + * @req: GAS query payload (to be freed by gas_query module in case of success + * return) + * @cb: Callback function for reporting GAS query result and response + * @ctx: Context pointer to use with the @cb call + * Returns: dialog token (>= 0) on success or -1 on failure + */ +int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq, + struct wpabuf *req, + void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, + enum gas_query_ap_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code), + void *ctx) +{ + struct gas_query_pending *query; + int dialog_token; + + if (!gas || wpabuf_len(req) < 3) + return -1; + + dialog_token = gas_query_new_dialog_token(gas, dst); + if (dialog_token < 0) + return -1; + + query = os_zalloc(sizeof(*query)); + if (query == NULL) + return -1; + + query->gas = gas; + os_memcpy(query->addr, dst, ETH_ALEN); + query->dialog_token = dialog_token; + query->freq = freq; + query->cb = cb; + query->ctx = ctx; + query->req = req; + dl_list_add(&gas->pending, &query->list); + + *(wpabuf_mhead_u8(req) + 2) = dialog_token; + + wpa_msg(gas->msg_ctx, MSG_INFO, GAS_QUERY_START "addr=" MACSTR + " dialog_token=%u freq=%d", + MAC2STR(query->addr), query->dialog_token, query->freq); + + gas_query_tx_initial_req(gas, query); + + return dialog_token; +} diff --git a/src/ap/gas_query_ap.h b/src/ap/gas_query_ap.h new file mode 100644 index 0000000000000..70f1f0537657f --- /dev/null +++ b/src/ap/gas_query_ap.h @@ -0,0 +1,43 @@ +/* + * Generic advertisement service (GAS) query + * Copyright (c) 2009, Atheros Communications + * Copyright (c) 2011-2017, Qualcomm Atheros + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef GAS_QUERY_AP_H +#define GAS_QUERY_AP_H + +struct gas_query_ap; + +struct gas_query_ap * gas_query_ap_init(struct hostapd_data *hapd, + void *msg_ctx); +void gas_query_ap_deinit(struct gas_query_ap *gas); +int gas_query_ap_rx(struct gas_query_ap *gas, const u8 *sa, u8 categ, + const u8 *data, size_t len, int freq); + +/** + * enum gas_query_ap_result - GAS query result + */ +enum gas_query_ap_result { + GAS_QUERY_AP_SUCCESS, + GAS_QUERY_AP_FAILURE, + GAS_QUERY_AP_TIMEOUT, + GAS_QUERY_AP_PEER_ERROR, + GAS_QUERY_AP_INTERNAL_ERROR, + GAS_QUERY_AP_DELETED_AT_DEINIT +}; + +int gas_query_ap_req(struct gas_query_ap *gas, const u8 *dst, int freq, + struct wpabuf *req, + void (*cb)(void *ctx, const u8 *dst, u8 dialog_token, + enum gas_query_ap_result result, + const struct wpabuf *adv_proto, + const struct wpabuf *resp, u16 status_code), + void *ctx); +void gas_query_ap_tx_status(struct gas_query_ap *gas, const u8 *dst, + const u8 *data, size_t data_len, int ok); + +#endif /* GAS_QUERY_AP_H */ diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c index 6ce178de3b294..a7df810324777 100644 --- a/src/ap/gas_serv.c +++ b/src/ap/gas_serv.c @@ -11,14 +11,31 @@ #include "common.h" #include "common/ieee802_11_defs.h" #include "common/gas.h" +#include "common/wpa_ctrl.h" #include "utils/eloop.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" +#include "dpp_hostapd.h" #include "sta_info.h" #include "gas_serv.h" +#ifdef CONFIG_DPP +static void gas_serv_write_dpp_adv_proto(struct wpabuf *buf) +{ + 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); +} +#endif /* CONFIG_DPP */ + + static void convert_to_protected_dual(struct wpabuf *msg) { u8 *categ = wpabuf_mhead_u8(msg); @@ -50,9 +67,12 @@ gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token) sta->flags |= WLAN_STA_GAS; /* * The default inactivity is 300 seconds. We don't need - * it to be that long. + * it to be that long. Use five second timeout and increase this + * with the comeback_delay for testing cases. */ - ap_sta_session_timeout(hapd, sta, 5); + ap_sta_session_timeout(hapd, sta, + hapd->conf->gas_comeback_delay / 1024 + + 5); } else { ap_sta_replenish_timeout(hapd, sta, 5); } @@ -161,8 +181,12 @@ static void anqp_add_hs_capab_list(struct hostapd_data *hapd, wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS); if (hapd->conf->hs20_osu_providers_count) wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST); + if (hapd->conf->hs20_osu_providers_nai_count) + wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST); if (hapd->conf->hs20_icons_count) wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST); + if (hapd->conf->hs20_operator_icon_count) + wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA); gas_anqp_set_element_len(buf, len); } #endif /* CONFIG_HS20 */ @@ -255,20 +279,29 @@ static void anqp_add_capab_list(struct hostapd_data *hapd, wpabuf_put_le16(buf, ANQP_DOMAIN_NAME); if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI)) wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI); + if (get_anqp_elem(hapd, ANQP_TDLS_CAPABILITY)) + wpabuf_put_le16(buf, ANQP_TDLS_CAPABILITY); if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI)) wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI); if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT)) wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT); - for (id = 273; id < 277; id++) { - if (get_anqp_elem(hapd, id)) - wpabuf_put_le16(buf, id); - } - if (get_anqp_elem(hapd, ANQP_VENUE_URL)) +#ifdef CONFIG_FILS + if (!dl_list_empty(&hapd->conf->fils_realms) || + get_anqp_elem(hapd, ANQP_FILS_REALM_INFO)) + wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO); +#endif /* CONFIG_FILS */ + if (get_anqp_elem(hapd, ANQP_CAG)) + wpabuf_put_le16(buf, ANQP_CAG); + if (hapd->conf->venue_url || get_anqp_elem(hapd, ANQP_VENUE_URL)) wpabuf_put_le16(buf, ANQP_VENUE_URL); if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE)) wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE); if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT)) wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT); + for (id = 280; id < 300; id++) { + if (get_anqp_elem(hapd, id)) + wpabuf_put_le16(buf, id); + } #ifdef CONFIG_HS20 anqp_add_hs_capab_list(hapd, buf); #endif /* CONFIG_HS20 */ @@ -299,6 +332,29 @@ static void anqp_add_venue_name(struct hostapd_data *hapd, struct wpabuf *buf) } +static void anqp_add_venue_url(struct hostapd_data *hapd, struct wpabuf *buf) +{ + if (anqp_add_override(hapd, buf, ANQP_VENUE_URL)) + return; + + if (hapd->conf->venue_url) { + u8 *len; + unsigned int i; + + len = gas_anqp_add_element(buf, ANQP_VENUE_URL); + for (i = 0; i < hapd->conf->venue_url_count; i++) { + struct hostapd_venue_url *url; + + url = &hapd->conf->venue_url[i]; + wpabuf_put_u8(buf, 1 + url->url_len); + wpabuf_put_u8(buf, url->venue_number); + wpabuf_put_data(buf, url->url, url->url_len); + } + gas_anqp_set_element_len(buf, len); + } +} + + static void anqp_add_network_auth_type(struct hostapd_data *hapd, struct wpabuf *buf) { @@ -548,6 +604,36 @@ static void anqp_add_domain_name(struct hostapd_data *hapd, struct wpabuf *buf) } +#ifdef CONFIG_FILS +static void anqp_add_fils_realm_info(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + size_t count; + + if (anqp_add_override(hapd, buf, ANQP_FILS_REALM_INFO)) + return; + + count = dl_list_len(&hapd->conf->fils_realms); + if (count > 10000) + count = 10000; + if (count) { + struct fils_realm *realm; + + wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO); + wpabuf_put_le16(buf, 2 * count); + + dl_list_for_each(realm, &hapd->conf->fils_realms, + struct fils_realm, list) { + if (count == 0) + break; + wpabuf_put_data(buf, realm->hash, 2); + count--; + } + } +} +#endif /* CONFIG_FILS */ + + #ifdef CONFIG_HS20 static void anqp_add_operator_friendly_name(struct hostapd_data *hapd, @@ -621,6 +707,29 @@ static void anqp_add_operating_class(struct hostapd_data *hapd, } +static void anqp_add_icon(struct wpabuf *buf, struct hostapd_bss_config *bss, + const char *name) +{ + size_t j; + struct hs20_icon *icon = NULL; + + for (j = 0; j < bss->hs20_icons_count && !icon; j++) { + if (os_strcmp(name, bss->hs20_icons[j].name) == 0) + icon = &bss->hs20_icons[j]; + } + if (!icon) + return; /* icon info not found */ + + wpabuf_put_le16(buf, icon->width); + wpabuf_put_le16(buf, icon->height); + wpabuf_put_data(buf, icon->language, 3); + wpabuf_put_u8(buf, os_strlen(icon->type)); + wpabuf_put_str(buf, icon->type); + wpabuf_put_u8(buf, os_strlen(icon->name)); + wpabuf_put_str(buf, icon->name); +} + + static void anqp_add_osu_provider(struct wpabuf *buf, struct hostapd_bss_config *bss, struct hs20_osu_provider *p) @@ -649,32 +758,14 @@ static void anqp_add_osu_provider(struct wpabuf *buf, /* OSU Method List */ count = wpabuf_put(buf, 1); - for (i = 0; p->method_list[i] >= 0; i++) + for (i = 0; p->method_list && p->method_list[i] >= 0; i++) wpabuf_put_u8(buf, p->method_list[i]); *count = i; /* Icons Available */ len2 = wpabuf_put(buf, 2); - for (i = 0; i < p->icons_count; i++) { - size_t j; - struct hs20_icon *icon = NULL; - - for (j = 0; j < bss->hs20_icons_count && !icon; j++) { - if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) == - 0) - icon = &bss->hs20_icons[j]; - } - if (!icon) - continue; /* icon info not found */ - - wpabuf_put_le16(buf, icon->width); - wpabuf_put_le16(buf, icon->height); - wpabuf_put_data(buf, icon->language, 3); - wpabuf_put_u8(buf, os_strlen(icon->type)); - wpabuf_put_str(buf, icon->type); - wpabuf_put_u8(buf, os_strlen(icon->name)); - wpabuf_put_str(buf, icon->name); - } + for (i = 0; i < p->icons_count; i++) + anqp_add_icon(buf, bss, p->icons[i]); WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2); /* OSU_NAI */ @@ -728,6 +819,40 @@ static void anqp_add_osu_providers_list(struct hostapd_data *hapd, } +static void anqp_add_osu_provider_nai(struct wpabuf *buf, + struct hs20_osu_provider *p) +{ + /* OSU_NAI for shared BSS (Single SSID) */ + if (p->osu_nai2) { + wpabuf_put_u8(buf, os_strlen(p->osu_nai2)); + wpabuf_put_str(buf, p->osu_nai2); + } else { + wpabuf_put_u8(buf, 0); + } +} + + +static void anqp_add_osu_providers_nai_list(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->hs20_osu_providers_nai_count) { + size_t i; + u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_NAI_LIST); + wpabuf_put_u8(buf, 0); /* Reserved */ + + for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) { + anqp_add_osu_provider_nai( + buf, &hapd->conf->hs20_osu_providers[i]); + } + + gas_anqp_set_element_len(buf, len); + } +} + + static void anqp_add_icon_binary_file(struct hostapd_data *hapd, struct wpabuf *buf, const u8 *name, size_t name_len) @@ -783,9 +908,49 @@ static void anqp_add_icon_binary_file(struct hostapd_data *hapd, gas_anqp_set_element_len(buf, len); } + +static void anqp_add_operator_icon_metadata(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + struct hostapd_bss_config *bss = hapd->conf; + size_t i; + u8 *len; + + if (!bss->hs20_operator_icon_count) + return; + + len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, HS20_STYPE_OPERATOR_ICON_METADATA); + wpabuf_put_u8(buf, 0); /* Reserved */ + + for (i = 0; i < bss->hs20_operator_icon_count; i++) + anqp_add_icon(buf, bss, bss->hs20_operator_icon[i]); + + gas_anqp_set_element_len(buf, len); +} + #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO +static void anqp_add_mbo_cell_data_conn_pref(struct hostapd_data *hapd, + struct wpabuf *buf) +{ + if (hapd->conf->mbo_cell_data_conn_pref >= 0) { + u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, MBO_ANQP_OUI_TYPE); + wpabuf_put_u8(buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF); + wpabuf_put_u8(buf, hapd->conf->mbo_cell_data_conn_pref); + gas_anqp_set_element_len(buf, len); + } +} +#endif /* CONFIG_MBO */ + + static size_t anqp_get_required_len(struct hostapd_data *hapd, const u16 *infoid, unsigned int num_infoid) @@ -821,6 +986,10 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, len += 1000; if (request & ANQP_REQ_ICON_REQUEST) len += 65536; +#ifdef CONFIG_FILS + if (request & ANQP_FILS_REALM_INFO) + len += 2 * dl_list_len(&hapd->conf->fils_realms); +#endif /* CONFIG_FILS */ len += anqp_get_required_len(hapd, extra_req, num_extra_req); buf = wpabuf_alloc(len); @@ -860,8 +1029,19 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, if (request & ANQP_REQ_EMERGENCY_NAI) anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI); - for (i = 0; i < num_extra_req; i++) + for (i = 0; i < num_extra_req; i++) { +#ifdef CONFIG_FILS + if (extra_req[i] == ANQP_FILS_REALM_INFO) { + anqp_add_fils_realm_info(hapd, buf); + continue; + } +#endif /* CONFIG_FILS */ + if (extra_req[i] == ANQP_VENUE_URL) { + anqp_add_venue_url(hapd, buf); + continue; + } anqp_add_elem(hapd, buf, extra_req[i]); + } #ifdef CONFIG_HS20 if (request & ANQP_REQ_HS_CAPABILITY_LIST) @@ -878,8 +1058,17 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd, anqp_add_osu_providers_list(hapd, buf); if (request & ANQP_REQ_ICON_REQUEST) anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len); + if (request & ANQP_REQ_OPERATOR_ICON_METADATA) + anqp_add_operator_icon_metadata(hapd, buf); + if (request & ANQP_REQ_OSU_PROVIDERS_NAI_LIST) + anqp_add_osu_providers_nai_list(hapd, buf); #endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + if (request & ANQP_REQ_MBO_CELL_DATA_CONN_PREF) + anqp_add_mbo_cell_data_conn_pref(hapd, buf); +#endif /* CONFIG_MBO */ + return buf; } @@ -984,7 +1173,17 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id, get_anqp_elem(hapd, info_id) != NULL, qi); break; default: - if (!get_anqp_elem(hapd, info_id)) { +#ifdef CONFIG_FILS + if (info_id == ANQP_FILS_REALM_INFO && + !dl_list_empty(&hapd->conf->fils_realms)) { + wpa_printf(MSG_DEBUG, + "ANQP: FILS Realm Information (local)"); + } else +#endif /* CONFIG_FILS */ + if (info_id == ANQP_VENUE_URL && hapd->conf->venue_url) { + wpa_printf(MSG_DEBUG, + "ANQP: Venue URL (local)"); + } else if (!get_anqp_elem(hapd, info_id)) { wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u", info_id); break; @@ -1050,6 +1249,16 @@ static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype, set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list", hapd->conf->hs20_osu_providers_count, qi); break; + case HS20_STYPE_OPERATOR_ICON_METADATA: + set_anqp_req(ANQP_REQ_OPERATOR_ICON_METADATA, + "Operator Icon Metadata", + hapd->conf->hs20_operator_icon_count, qi); + break; + case HS20_STYPE_OSU_PROVIDERS_NAI_LIST: + set_anqp_req(ANQP_REQ_OSU_PROVIDERS_NAI_LIST, + "OSU Providers NAI List", + hapd->conf->hs20_osu_providers_nai_count, qi); + break; default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u", subtype); @@ -1092,49 +1301,12 @@ static void rx_anqp_hs_icon_request(struct hostapd_data *hapd, } -static void rx_anqp_vendor_specific(struct hostapd_data *hapd, - const u8 *pos, const u8 *end, - struct anqp_query_info *qi) +static void rx_anqp_vendor_specific_hs20(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) { - u32 oui; u8 subtype; - if (end - pos < 4) { - wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP " - "Query element"); - return; - } - - oui = WPA_GET_BE24(pos); - pos += 3; - if (oui != OUI_WFA) { - wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x", - oui); - return; - } - -#ifdef CONFIG_P2P - if (*pos == P2P_OUI_TYPE) { - /* - * This is for P2P SD and will be taken care of by the P2P - * implementation. This query needs to be ignored in the generic - * GAS server to avoid duplicated response. - */ - wpa_printf(MSG_DEBUG, - "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server", - *pos); - qi->p2p_sd = 1; - return; - } -#endif /* CONFIG_P2P */ - - if (*pos != HS20_ANQP_OUI_TYPE) { - wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u", - *pos); - return; - } - pos++; - if (end - pos <= 1) return; @@ -1164,6 +1336,115 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd, #endif /* CONFIG_HS20 */ +#ifdef CONFIG_P2P +static void rx_anqp_vendor_specific_p2p(struct hostapd_data *hapd, + struct anqp_query_info *qi) +{ + /* + * This is for P2P SD and will be taken care of by the P2P + * implementation. This query needs to be ignored in the generic + * GAS server to avoid duplicated response. + */ + wpa_printf(MSG_DEBUG, + "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server", + P2P_OUI_TYPE); + qi->p2p_sd = 1; + return; +} +#endif /* CONFIG_P2P */ + + +#ifdef CONFIG_MBO + +static void rx_anqp_mbo_query_list(struct hostapd_data *hapd, u8 subtype, + struct anqp_query_info *qi) +{ + switch (subtype) { + case MBO_ANQP_SUBTYPE_CELL_CONN_PREF: + set_anqp_req(ANQP_REQ_MBO_CELL_DATA_CONN_PREF, + "Cellular Data Connection Preference", + hapd->conf->mbo_cell_data_conn_pref >= 0, qi); + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO subtype %u", + subtype); + break; + } +} + + +static void rx_anqp_vendor_specific_mbo(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) +{ + u8 subtype; + + if (end - pos < 1) + return; + + subtype = *pos++; + switch (subtype) { + case MBO_ANQP_SUBTYPE_QUERY_LIST: + wpa_printf(MSG_DEBUG, "ANQP: MBO Query List"); + while (pos < end) { + rx_anqp_mbo_query_list(hapd, *pos, qi); + pos++; + } + break; + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported MBO query subtype %u", + subtype); + break; + } +} + +#endif /* CONFIG_MBO */ + + +static void rx_anqp_vendor_specific(struct hostapd_data *hapd, + const u8 *pos, const u8 *end, + struct anqp_query_info *qi) +{ + u32 oui; + + if (end - pos < 4) { + wpa_printf(MSG_DEBUG, "ANQP: Too short vendor specific ANQP " + "Query element"); + return; + } + + oui = WPA_GET_BE24(pos); + pos += 3; + if (oui != OUI_WFA) { + wpa_printf(MSG_DEBUG, "ANQP: Unsupported vendor OUI %06x", + oui); + return; + } + + switch (*pos) { +#ifdef CONFIG_P2P + case P2P_OUI_TYPE: + rx_anqp_vendor_specific_p2p(hapd, qi); + break; +#endif /* CONFIG_P2P */ +#ifdef CONFIG_HS20 + case HS20_ANQP_OUI_TYPE: + rx_anqp_vendor_specific_hs20(hapd, pos + 1, end, qi); + break; +#endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + case MBO_ANQP_OUI_TYPE: + rx_anqp_vendor_specific_mbo(hapd, pos + 1, end, qi); + break; +#endif /* CONFIG_MBO */ + default: + wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u", + *pos); + break; + } +} + + static void gas_serv_req_local_processing(struct hostapd_data *hapd, const u8 *sa, u8 dialog_token, struct anqp_query_info *qi, int prot, @@ -1189,7 +1470,7 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd, } #endif /* CONFIG_P2P */ - if (wpabuf_len(buf) > hapd->gas_frag_limit || + if (wpabuf_len(buf) > hapd->conf->gas_frag_limit || hapd->conf->gas_comeback_delay) { struct gas_dialog_info *di; u16 comeback_delay = 1; @@ -1240,6 +1521,72 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd, } +#ifdef CONFIG_DPP +static void gas_serv_req_dpp_processing(struct hostapd_data *hapd, + const u8 *sa, u8 dialog_token, + int prot, struct wpabuf *buf) +{ + struct wpabuf *tx_buf; + + if (wpabuf_len(buf) > hapd->conf->gas_frag_limit || + hapd->conf->gas_comeback_delay) { + struct gas_dialog_info *di; + u16 comeback_delay = 1; + + if (hapd->conf->gas_comeback_delay) { + /* Testing - allow overriding of the delay value */ + comeback_delay = hapd->conf->gas_comeback_delay; + } + + wpa_printf(MSG_DEBUG, + "DPP: Too long response to fit in initial response - use GAS comeback"); + di = gas_dialog_create(hapd, sa, dialog_token); + if (!di) { + wpa_printf(MSG_INFO, "DPP: Could not create dialog for " + MACSTR " (dialog token %u)", + MAC2STR(sa), dialog_token); + wpabuf_free(buf); + tx_buf = gas_build_initial_resp( + dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE, + 0, 10); + if (tx_buf) + gas_serv_write_dpp_adv_proto(tx_buf); + } else { + di->prot = prot; + di->sd_resp = buf; + di->sd_resp_pos = 0; + tx_buf = gas_build_initial_resp( + dialog_token, WLAN_STATUS_SUCCESS, + comeback_delay, 10); + if (tx_buf) + gas_serv_write_dpp_adv_proto(tx_buf); + } + } else { + wpa_printf(MSG_DEBUG, + "DPP: GAS Initial response (no comeback)"); + tx_buf = gas_build_initial_resp( + dialog_token, WLAN_STATUS_SUCCESS, 0, + 10 + 2 + wpabuf_len(buf)); + if (tx_buf) { + gas_serv_write_dpp_adv_proto(tx_buf); + wpabuf_put_le16(tx_buf, wpabuf_len(buf)); + wpabuf_put_buf(tx_buf, buf); + hostapd_dpp_gas_status_handler(hapd, 1); + } + wpabuf_free(buf); + } + if (!tx_buf) + return; + if (prot) + convert_to_protected_dual(tx_buf); + hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa, + wpabuf_head(tx_buf), + wpabuf_len(tx_buf)); + wpabuf_free(tx_buf); +} +#endif /* CONFIG_DPP */ + + static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, const u8 *sa, const u8 *data, size_t len, int prot, @@ -1252,6 +1599,9 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, u16 slen; struct anqp_query_info qi; const u8 *adv_proto; +#ifdef CONFIG_DPP + int dpp = 0; +#endif /* CONFIG_DPP */ if (len < 1 + 2) return; @@ -1279,6 +1629,15 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, next = pos + slen; pos++; /* skip QueryRespLenLimit and PAME-BI */ +#ifdef CONFIG_DPP + if (slen == 8 && *pos == WLAN_EID_VENDOR_SPECIFIC && + pos[1] == 5 && WPA_GET_BE24(&pos[2]) == OUI_WFA && + pos[5] == DPP_OUI_TYPE && pos[6] == 0x01) { + wpa_printf(MSG_DEBUG, "DPP: Configuration Request"); + dpp = 1; + } else +#endif /* CONFIG_DPP */ + if (*pos != ACCESS_NETWORK_QUERY_PROTOCOL) { struct wpabuf *buf; wpa_msg(hapd->msg_ctx, MSG_DEBUG, @@ -1318,6 +1677,18 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, return; end = pos + slen; +#ifdef CONFIG_DPP + if (dpp) { + struct wpabuf *msg; + + msg = hostapd_dpp_gas_req_handler(hapd, sa, pos, slen); + if (!msg) + return; + gas_serv_req_dpp_processing(hapd, sa, dialog_token, prot, msg); + return; + } +#endif /* CONFIG_DPP */ + /* ANQP Query Request */ while (pos < end) { u16 info_id, elen; @@ -1339,11 +1710,9 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd, case ANQP_QUERY_LIST: rx_anqp_query_list(hapd, pos, pos + elen, &qi); break; -#ifdef CONFIG_HS20 case ANQP_VENDOR_SPECIFIC: rx_anqp_vendor_specific(hapd, pos, pos + elen, &qi); break; -#endif /* CONFIG_HS20 */ default: wpa_printf(MSG_DEBUG, "ANQP: Unsupported Query " "Request element %u", info_id); @@ -1393,8 +1762,8 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, } frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos; - if (frag_len > hapd->gas_frag_limit) { - frag_len = hapd->gas_frag_limit; + if (frag_len > hapd->conf->gas_frag_limit) { + frag_len = hapd->conf->gas_frag_limit; more = 1; } wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u", @@ -1407,6 +1776,18 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, gas_serv_dialog_clear(dialog); return; } +#ifdef CONFIG_DPP + if (dialog->dpp) { + tx_buf = gas_build_comeback_resp(dialog_token, + WLAN_STATUS_SUCCESS, + dialog->sd_frag_id, more, 0, + 10 + frag_len); + if (tx_buf) { + gas_serv_write_dpp_adv_proto(tx_buf); + wpabuf_put_buf(tx_buf, buf); + } + } else +#endif /* CONFIG_DPP */ tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token, WLAN_STATUS_SUCCESS, dialog->sd_frag_id, @@ -1430,6 +1811,10 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd, } else { wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: All fragments of " "SD response sent"); +#ifdef CONFIG_DPP + if (dialog->dpp) + hostapd_dpp_gas_status_handler(hapd, 1); +#endif /* CONFIG_DPP */ gas_serv_dialog_clear(dialog); gas_serv_free_dialogs(hapd, sa); } @@ -1495,9 +1880,6 @@ int gas_serv_init(struct hostapd_data *hapd) { hapd->public_action_cb2 = gas_serv_rx_public_action; hapd->public_action_cb2_ctx = hapd; - hapd->gas_frag_limit = 1400; - if (hapd->conf->gas_frag_limit > 0) - hapd->gas_frag_limit = hapd->conf->gas_frag_limit; return 0; } diff --git a/src/ap/gas_serv.h b/src/ap/gas_serv.h index 9051e4f905135..2cf1817298f7d 100644 --- a/src/ap/gas_serv.h +++ b/src/ap/gas_serv.h @@ -41,7 +41,7 @@ #define ANQP_REQ_EMERGENCY_NAI \ (1 << (ANQP_EMERGENCY_NAI - ANQP_QUERY_LIST)) /* - * First 16 Hotspot 2.0 vendor specific ANQP-elements can be included in the + * First 15 Hotspot 2.0 vendor specific ANQP-elements can be included in the * optimized bitmap. */ #define ANQP_REQ_HS_CAPABILITY_LIST \ @@ -60,6 +60,13 @@ (0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST) #define ANQP_REQ_ICON_REQUEST \ (0x10000 << HS20_STYPE_ICON_REQUEST) +#define ANQP_REQ_OPERATOR_ICON_METADATA \ + (0x10000 << HS20_STYPE_OPERATOR_ICON_METADATA) +#define ANQP_REQ_OSU_PROVIDERS_NAI_LIST \ + (0x10000 << HS20_STYPE_OSU_PROVIDERS_NAI_LIST) +/* The first MBO ANQP-element can be included in the optimized bitmap. */ +#define ANQP_REQ_MBO_CELL_DATA_CONN_PREF \ + (BIT(29) << MBO_ANQP_SUBTYPE_CELL_CONN_PREF) struct gas_dialog_info { u8 valid; @@ -68,6 +75,7 @@ struct gas_dialog_info { size_t sd_resp_pos; /* Offset in sd_resp */ u8 sd_frag_id; int prot; /* whether Protected Dual of Public Action frame is used */ + int dpp; /* whether this is a DPP Config Response */ }; struct hostapd_data; diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c index 9fafc7f457bb0..7501bac6e42a1 100644 --- a/src/ap/hostapd.c +++ b/src/ap/hostapd.c @@ -31,6 +31,8 @@ #include "vlan_init.h" #include "wpa_auth.h" #include "wps_hostapd.h" +#include "dpp_hostapd.h" +#include "gas_query_ap.h" #include "hw_features.h" #include "wpa_auth_glue.h" #include "ap_drv_ops.h" @@ -45,6 +47,9 @@ #include "ndisc_snoop.h" #include "neighbor_db.h" #include "rrm.h" +#include "fils_hlp.h" +#include "acs.h" +#include "hs20.h" static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason); @@ -52,6 +57,8 @@ static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd); static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd); static int setup_interface2(struct hostapd_iface *iface); static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx); +static void hostapd_interface_setup_failure_handler(void *eloop_ctx, + void *timeout_ctx); int hostapd_for_each_interface(struct hapd_interfaces *interfaces, @@ -71,10 +78,26 @@ int hostapd_for_each_interface(struct hapd_interfaces *interfaces, } +void hostapd_reconfig_encryption(struct hostapd_data *hapd) +{ + if (hapd->wpa_auth) + return; + + hostapd_set_privacy(hapd, 0); + hostapd_setup_encryption(hapd->conf->iface, hapd); +} + + static void hostapd_reload_bss(struct hostapd_data *hapd) { struct hostapd_ssid *ssid; + if (!hapd->started) + return; + + if (hapd->conf->wmm_enabled < 0) + hapd->conf->wmm_enabled = hapd->iconf->ieee80211n; + #ifndef CONFIG_NO_RADIUS radius_client_reconfig(hapd->radius, hapd->conf->radius); #endif /* CONFIG_NO_RADIUS */ @@ -153,8 +176,27 @@ static void hostapd_clear_old(struct hostapd_iface *iface) } +static int hostapd_iface_conf_changed(struct hostapd_config *newconf, + struct hostapd_config *oldconf) +{ + size_t i; + + if (newconf->num_bss != oldconf->num_bss) + return 1; + + for (i = 0; i < newconf->num_bss; i++) { + if (os_strcmp(newconf->bss[i]->iface, + oldconf->bss[i]->iface) != 0) + return 1; + } + + return 0; +} + + int hostapd_reload_config(struct hostapd_iface *iface) { + struct hapd_interfaces *interfaces = iface->interfaces; struct hostapd_data *hapd = iface->bss[0]; struct hostapd_config *newconf, *oldconf; size_t j; @@ -177,6 +219,35 @@ int hostapd_reload_config(struct hostapd_iface *iface) hostapd_clear_old(iface); oldconf = hapd->iconf; + if (hostapd_iface_conf_changed(newconf, oldconf)) { + char *fname; + int res; + + wpa_printf(MSG_DEBUG, + "Configuration changes include interface/BSS modification - force full disable+enable sequence"); + fname = os_strdup(iface->config_fname); + if (!fname) { + hostapd_config_free(newconf); + return -1; + } + hostapd_remove_iface(interfaces, hapd->conf->iface); + iface = hostapd_init(interfaces, fname); + os_free(fname); + hostapd_config_free(newconf); + if (!iface) { + wpa_printf(MSG_ERROR, + "Failed to initialize interface on config reload"); + return -1; + } + iface->interfaces = interfaces; + interfaces->iface[interfaces->count] = iface; + interfaces->count++; + res = hostapd_enable_iface(iface); + if (res < 0) + wpa_printf(MSG_ERROR, + "Failed to enable interface on config reload"); + return res; + } iface->conf = newconf; for (j = 0; j < iface->num_bss; j++) { @@ -210,7 +281,7 @@ static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd, { int i; - if (!ifname) + if (!ifname || !hapd->drv_priv) return; for (i = 0; i < NUM_WEP_KEYS; i++) { if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, @@ -297,6 +368,10 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) #endif /* CONFIG_NO_RADIUS */ hostapd_deinit_wps(hapd); +#ifdef CONFIG_DPP + hostapd_dpp_deinit(hapd); + gas_query_ap_deinit(hapd->gas); +#endif /* CONFIG_DPP */ authsrv_deinit(hapd); @@ -341,6 +416,7 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd) #endif /* CONFIG_MESH */ hostapd_clean_rrm(hapd); + fils_hlp_deinit(hapd); } @@ -357,8 +433,10 @@ static void hostapd_cleanup(struct hostapd_data *hapd) wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd, hapd->conf->iface); if (hapd->iface->interfaces && - hapd->iface->interfaces->ctrl_iface_deinit) + hapd->iface->interfaces->ctrl_iface_deinit) { + wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_TERMINATING); hapd->iface->interfaces->ctrl_iface_deinit(hapd); + } hostapd_free_hapd_data(hapd); } @@ -387,8 +465,11 @@ static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface) hostapd_stop_setup_timers(iface); #endif /* NEED_AP_MLME */ #endif /* CONFIG_IEEE80211N */ + if (iface->current_mode) + acs_cleanup(iface); hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); iface->hw_features = NULL; + iface->current_mode = NULL; os_free(iface->current_rates); iface->current_rates = NULL; os_free(iface->basic_rates); @@ -409,6 +490,8 @@ static void hostapd_cleanup_iface(struct hostapd_iface *iface) { wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface); eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); + eloop_cancel_timeout(hostapd_interface_setup_failure_handler, iface, + NULL); hostapd_cleanup_iface_partial(iface); hostapd_config_free(iface->conf); @@ -484,9 +567,12 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason) ret = -1; } } - wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations"); - os_memset(addr, 0xff, ETH_ALEN); - hostapd_drv_sta_deauth(hapd, addr, reason); + if (hapd->conf && hapd->conf->broadcast_deauth) { + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "Deauthenticate all stations"); + os_memset(addr, 0xff, ETH_ALEN); + hostapd_drv_sta_deauth(hapd, addr, reason); + } hostapd_free_stas(hapd); return ret; @@ -873,6 +959,48 @@ hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr) return RADIUS_DAS_SUCCESS; } + +#ifdef CONFIG_HS20 +static enum radius_das_res +hostapd_das_coa(void *ctx, struct radius_das_attrs *attr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + int multi; + + if (hostapd_das_nas_mismatch(hapd, attr)) + return RADIUS_DAS_NAS_MISMATCH; + + sta = hostapd_das_find_sta(hapd, attr, &multi); + if (!sta) { + if (multi) { + wpa_printf(MSG_DEBUG, + "RADIUS DAS: Multiple sessions match - not supported"); + return RADIUS_DAS_MULTI_SESSION_MATCH; + } + wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found"); + return RADIUS_DAS_SESSION_NOT_FOUND; + } + + wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR + " - CoA", MAC2STR(sta->addr)); + + if (attr->hs20_t_c_filtering) { + if (attr->hs20_t_c_filtering[0] & BIT(0)) { + wpa_printf(MSG_DEBUG, + "HS 2.0: Unexpected Terms and Conditions filtering required in CoA-Request"); + return RADIUS_DAS_COA_FAILED; + } + + hs20_t_c_filtering(hapd, sta, 0); + } + + return RADIUS_DAS_SUCCESS; +} +#else /* CONFIG_HS20 */ +#define hostapd_das_coa NULL +#endif /* CONFIG_HS20 */ + #endif /* CONFIG_NO_RADIUS */ @@ -956,13 +1084,13 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) if (conf->wmm_enabled < 0) conf->wmm_enabled = hapd->iconf->ieee80211n; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (is_zero_ether_addr(conf->r1_key_holder)) os_memcpy(conf->r1_key_holder, hapd->own_addr, ETH_ALEN); -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_MESH - if (hapd->iface->mconf == NULL) + if ((hapd->conf->mesh & MESH_ENABLED) && hapd->iface->mconf == NULL) flush_old_stations = 0; #endif /* CONFIG_MESH */ @@ -1047,6 +1175,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) conf->radius_das_require_message_authenticator; das_conf.ctx = hapd; das_conf.disconnect = hostapd_das_disconnect; + das_conf.coa = hostapd_das_coa; hapd->radius_das = radius_das_init(&das_conf); if (hapd->radius_das == NULL) { wpa_printf(MSG_ERROR, "RADIUS DAS initialization " @@ -1063,6 +1192,14 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first) if (hostapd_init_wps(hapd, conf)) return -1; +#ifdef CONFIG_DPP + hapd->gas = gas_query_ap_init(hapd, hapd->msg_ctx); + if (!hapd->gas) + return -1; + if (hostapd_dpp_init(hapd)) + return -1; +#endif /* CONFIG_DPP */ + if (authsrv_init(hapd) < 0) return -1; @@ -1150,7 +1287,7 @@ static void hostapd_tx_queue_params(struct hostapd_iface *iface) struct hostapd_tx_queue_params *p; #ifdef CONFIG_MESH - if (iface->mconf == NULL) + if ((hapd->conf->mesh & MESH_ENABLED) && iface->mconf == NULL) return; #endif /* CONFIG_MESH */ @@ -1561,7 +1698,7 @@ static void hostapd_set_own_neighbor_report(struct hostapd_data *hapd) int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac; struct wpa_ssid_value ssid; u8 channel, op_class; - int center_freq1 = 0, center_freq2 = 0; + u8 center_freq1_idx = 0, center_freq2_idx = 0; enum nr_chan_width width; u32 bssid_info; struct wpabuf *nr; @@ -1598,22 +1735,22 @@ static void hostapd_set_own_neighbor_report(struct hostapd_data *hapd) /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */ - ieee80211_freq_to_channel_ext(hapd->iface->freq, - hapd->iconf->secondary_channel, - hapd->iconf->vht_oper_chwidth, - &op_class, &channel); + if (ieee80211_freq_to_channel_ext(hapd->iface->freq, + hapd->iconf->secondary_channel, + hapd->iconf->vht_oper_chwidth, + &op_class, &channel) == + NUM_HOSTAPD_MODES) + return; width = hostapd_get_nr_chan_width(hapd, ht, vht); if (vht) { - center_freq1 = ieee80211_chan_to_freq( - NULL, op_class, - hapd->iconf->vht_oper_centr_freq_seg0_idx); + center_freq1_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx; if (width == NR_CHAN_WIDTH_80P80) - center_freq2 = ieee80211_chan_to_freq( - NULL, op_class, - hapd->iconf->vht_oper_centr_freq_seg1_idx); + center_freq2_idx = + hapd->iconf->vht_oper_centr_freq_seg1_idx; } else if (ht) { - center_freq1 = hapd->iface->freq + - 10 * hapd->iconf->secondary_channel; + ieee80211_freq_to_chan(hapd->iface->freq + + 10 * hapd->iconf->secondary_channel, + ¢er_freq1_idx); } ssid.ssid_len = hapd->conf->ssid.ssid_len; @@ -1641,17 +1778,127 @@ static void hostapd_set_own_neighbor_report(struct hostapd_data *hapd) wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN); wpabuf_put_u8(nr, 3); wpabuf_put_u8(nr, width); - wpabuf_put_u8(nr, center_freq1); - wpabuf_put_u8(nr, center_freq2); + wpabuf_put_u8(nr, center_freq1_idx); + wpabuf_put_u8(nr, center_freq2_idx); hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci, - hapd->iconf->civic); + hapd->iconf->civic, hapd->iconf->stationary_ap); wpabuf_free(nr); #endif /* NEED_AP_MLME */ } +#ifdef CONFIG_OWE + +static int hostapd_owe_iface_iter(struct hostapd_iface *iface, void *ctx) +{ + struct hostapd_data *hapd = ctx; + size_t i; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *bss = iface->bss[i]; + + if (os_strcmp(hapd->conf->owe_transition_ifname, + bss->conf->iface) != 0) + continue; + + wpa_printf(MSG_DEBUG, + "OWE: ifname=%s found transition mode ifname=%s BSSID " + MACSTR " SSID %s", + hapd->conf->iface, bss->conf->iface, + MAC2STR(bss->own_addr), + wpa_ssid_txt(bss->conf->ssid.ssid, + bss->conf->ssid.ssid_len)); + if (!bss->conf->ssid.ssid_set || !bss->conf->ssid.ssid_len || + is_zero_ether_addr(bss->own_addr)) + continue; + + os_memcpy(hapd->conf->owe_transition_bssid, bss->own_addr, + ETH_ALEN); + os_memcpy(hapd->conf->owe_transition_ssid, + bss->conf->ssid.ssid, bss->conf->ssid.ssid_len); + hapd->conf->owe_transition_ssid_len = bss->conf->ssid.ssid_len; + wpa_printf(MSG_DEBUG, + "OWE: Copied transition mode information"); + return 1; + } + + return 0; +} + + +int hostapd_owe_trans_get_info(struct hostapd_data *hapd) +{ + if (hapd->conf->owe_transition_ssid_len > 0 && + !is_zero_ether_addr(hapd->conf->owe_transition_bssid)) + return 0; + + /* Find transition mode SSID/BSSID information from a BSS operated by + * this hostapd instance. */ + if (!hapd->iface->interfaces || + !hapd->iface->interfaces->for_each_interface) + return hostapd_owe_iface_iter(hapd->iface, hapd); + else + return hapd->iface->interfaces->for_each_interface( + hapd->iface->interfaces, hostapd_owe_iface_iter, hapd); +} + + +static int hostapd_owe_iface_iter2(struct hostapd_iface *iface, void *ctx) +{ + size_t i; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *bss = iface->bss[i]; + int res; + + if (!bss->conf->owe_transition_ifname[0]) + continue; + res = hostapd_owe_trans_get_info(bss); + if (res == 0) + continue; + wpa_printf(MSG_DEBUG, + "OWE: Matching transition mode interface enabled - update beacon data for %s", + bss->conf->iface); + ieee802_11_set_beacon(bss); + } + + return 0; +} + +#endif /* CONFIG_OWE */ + + +static void hostapd_owe_update_trans(struct hostapd_iface *iface) +{ +#ifdef CONFIG_OWE + /* Check whether the enabled BSS can complete OWE transition mode + * configuration for any pending interface. */ + if (!iface->interfaces || + !iface->interfaces->for_each_interface) + hostapd_owe_iface_iter2(iface, NULL); + else + iface->interfaces->for_each_interface( + iface->interfaces, hostapd_owe_iface_iter2, NULL); +#endif /* CONFIG_OWE */ +} + + +static void hostapd_interface_setup_failure_handler(void *eloop_ctx, + void *timeout_ctx) +{ + struct hostapd_iface *iface = eloop_ctx; + struct hostapd_data *hapd; + + if (iface->num_bss < 1 || !iface->bss || !iface->bss[0]) + return; + hapd = iface->bss[0]; + if (hapd->setup_complete_cb) + hapd->setup_complete_cb(hapd->setup_complete_cb_ctx); +} + + static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface, int err) { @@ -1827,6 +2074,7 @@ dfs_offload: #endif /* CONFIG_FST */ hostapd_set_state(iface, HAPD_IFACE_ENABLED); + hostapd_owe_update_trans(iface); wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED); if (hapd->setup_complete_cb) hapd->setup_complete_cb(hapd->setup_complete_cb_ctx); @@ -1851,8 +2099,19 @@ fail: iface->fst = NULL; } #endif /* CONFIG_FST */ - if (iface->interfaces && iface->interfaces->terminate_on_error) + + if (iface->interfaces && iface->interfaces->terminate_on_error) { eloop_terminate(); + } else if (hapd->setup_complete_cb) { + /* + * Calling hapd->setup_complete_cb directly may cause iface + * deinitialization which may be accessed later by the caller. + */ + eloop_register_timeout(0, 0, + hostapd_interface_setup_failure_handler, + iface, NULL); + } + return -1; } @@ -1997,10 +2256,16 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, hapd->iconf = conf; hapd->conf = bss; hapd->iface = hapd_iface; - hapd->driver = hapd->iconf->driver; + if (conf) + hapd->driver = conf->driver; hapd->ctrl_sock = -1; dl_list_init(&hapd->ctrl_dst); dl_list_init(&hapd->nr_db); + hapd->dhcp_sock = -1; +#ifdef CONFIG_IEEE80211R_AP + dl_list_init(&hapd->l2_queue); + dl_list_init(&hapd->l2_oui_queue); +#endif /* CONFIG_IEEE80211R_AP */ return hapd; } @@ -2028,12 +2293,6 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) hostapd_set_state(iface, HAPD_IFACE_DISABLED); -#ifdef CONFIG_IEEE80211N -#ifdef NEED_AP_MLME - hostapd_stop_setup_timers(iface); - eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL); -#endif /* NEED_AP_MLME */ -#endif /* CONFIG_IEEE80211N */ eloop_cancel_timeout(channel_list_update_timeout, iface, NULL); iface->wait_channel_update = 0; @@ -2049,6 +2308,13 @@ void hostapd_interface_deinit(struct hostapd_iface *iface) break; hostapd_bss_deinit(iface->bss[j]); } + +#ifdef CONFIG_IEEE80211N +#ifdef NEED_AP_MLME + hostapd_stop_setup_timers(iface); + eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL); +#endif /* NEED_AP_MLME */ +#endif /* CONFIG_IEEE80211N */ } @@ -2402,6 +2668,11 @@ int hostapd_disable_iface(struct hostapd_iface *hapd_iface) !!(hapd_iface->drv_flags & WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT); +#ifdef NEED_AP_MLME + for (j = 0; j < hapd_iface->num_bss; j++) + hostapd_cleanup_cs_params(hapd_iface->bss[j]); +#endif /* NEED_AP_MLME */ + /* same as hostapd_interface_deinit without deinitializing ctrl-iface */ for (j = 0; j < hapd_iface->num_bss; j++) { struct hostapd_data *hapd = hapd_iface->bss[j]; @@ -2459,7 +2730,7 @@ hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname, if (conf == NULL) { wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for " "configuration", __func__); - return NULL; + return NULL; } if (driver) { @@ -2612,6 +2883,7 @@ int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf) return -1; } } + hostapd_owe_update_trans(hapd_iface); return 0; } @@ -2829,12 +3101,24 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, ieee802_1x_new_station(hapd, sta); if (reassoc) { if (sta->auth_alg != WLAN_AUTH_FT && + sta->auth_alg != WLAN_AUTH_FILS_SK && + sta->auth_alg != WLAN_AUTH_FILS_SK_PFS && + sta->auth_alg != WLAN_AUTH_FILS_PK && !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH); } else wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm); - if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { + if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) { + if (eloop_cancel_timeout(ap_handle_timer, hapd, sta) > 0) { + wpa_printf(MSG_DEBUG, + "%s: %s: canceled wired ap_handle_timer timeout for " + MACSTR, + hapd->conf->iface, __func__, + MAC2STR(sta->addr)); + } + } else if (!(hapd->iface->drv_flags & + WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) { wpa_printf(MSG_DEBUG, "%s: %s: reschedule ap_handle_timer timeout for " MACSTR " (%d seconds - ap_max_inactivity)", @@ -2928,60 +3212,52 @@ static int hostapd_build_beacon_data(struct hostapd_data *hapd, goto free_ap_params; ret = -1; - beacon->head = os_malloc(params.head_len); + beacon->head = os_memdup(params.head, params.head_len); if (!beacon->head) goto free_ap_extra_ies; - os_memcpy(beacon->head, params.head, params.head_len); beacon->head_len = params.head_len; - beacon->tail = os_malloc(params.tail_len); + beacon->tail = os_memdup(params.tail, params.tail_len); if (!beacon->tail) goto free_beacon; - os_memcpy(beacon->tail, params.tail, params.tail_len); beacon->tail_len = params.tail_len; if (params.proberesp != NULL) { - beacon->probe_resp = os_malloc(params.proberesp_len); + beacon->probe_resp = os_memdup(params.proberesp, + params.proberesp_len); if (!beacon->probe_resp) goto free_beacon; - os_memcpy(beacon->probe_resp, params.proberesp, - params.proberesp_len); beacon->probe_resp_len = params.proberesp_len; } /* copy the extra ies */ if (beacon_extra) { - beacon->beacon_ies = os_malloc(wpabuf_len(beacon_extra)); + beacon->beacon_ies = os_memdup(beacon_extra->buf, + wpabuf_len(beacon_extra)); if (!beacon->beacon_ies) goto free_beacon; - os_memcpy(beacon->beacon_ies, - beacon_extra->buf, wpabuf_len(beacon_extra)); beacon->beacon_ies_len = wpabuf_len(beacon_extra); } if (proberesp_extra) { - beacon->proberesp_ies = - os_malloc(wpabuf_len(proberesp_extra)); + beacon->proberesp_ies = os_memdup(proberesp_extra->buf, + wpabuf_len(proberesp_extra)); if (!beacon->proberesp_ies) goto free_beacon; - os_memcpy(beacon->proberesp_ies, proberesp_extra->buf, - wpabuf_len(proberesp_extra)); beacon->proberesp_ies_len = wpabuf_len(proberesp_extra); } if (assocresp_extra) { - beacon->assocresp_ies = - os_malloc(wpabuf_len(assocresp_extra)); + beacon->assocresp_ies = os_memdup(assocresp_extra->buf, + wpabuf_len(assocresp_extra)); if (!beacon->assocresp_ies) goto free_beacon; - os_memcpy(beacon->assocresp_ies, assocresp_extra->buf, - wpabuf_len(assocresp_extra)); beacon->assocresp_ies_len = wpabuf_len(assocresp_extra); } @@ -3158,6 +3434,19 @@ void hostapd_cleanup_cs_params(struct hostapd_data *hapd) } +void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled) +{ + if (vht_enabled) + hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_ENABLED; + else + hapd->iconf->ch_switch_vht_config |= CH_SWITCH_VHT_DISABLED; + + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "CHAN_SWITCH VHT CONFIG 0x%x", + hapd->iconf->ch_switch_vht_config); +} + + int hostapd_switch_channel(struct hostapd_data *hapd, struct csa_settings *settings) { @@ -3192,7 +3481,6 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface, const struct hostapd_freq_params *freq_params) { int vht_seg0_idx = 0, vht_seg1_idx = 0, vht_bw = VHT_CHANWIDTH_USE_HT; - unsigned int i; wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes"); @@ -3234,10 +3522,8 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface, /* * cs_params must not be cleared earlier because the freq_params * argument may actually point to one of these. + * These params will be cleared during interface disable below. */ - for (i = 0; i < iface->num_bss; i++) - hostapd_cleanup_cs_params(iface->bss[i]); - hostapd_disable_iface(iface); hostapd_enable_iface(iface); } diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index dec46f692206c..d304c1171810c 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -14,6 +14,13 @@ #include "ap_config.h" #include "drivers/driver.h" +#define OCE_STA_CFON_ENABLED(hapd) \ + ((hapd->conf->oce & OCE_STA_CFON) && \ + (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_STA_CFON)) +#define OCE_AP_ENABLED(hapd) \ + ((hapd->conf->oce & OCE_AP) && \ + (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_OCE_AP)) + struct wpa_ctrl_dst; struct radius_server_data; struct upnp_wps_device_sm; @@ -53,7 +60,16 @@ struct hapd_interfaces { #ifndef CONFIG_NO_VLAN struct dynamic_iface *vlan_priv; #endif /* CONFIG_NO_VLAN */ +#ifdef CONFIG_ETH_P_OUI + struct dl_list eth_p_oui; /* OUI Extended EtherType handlers */ +#endif /* CONFIG_ETH_P_OUI */ int eloop_initialized; + +#ifdef CONFIG_DPP + int dpp_init_done; + struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */ + struct dl_list dpp_configurator; /* struct dpp_configurator */ +#endif /* CONFIG_DPP */ }; enum hostapd_chan_status { @@ -76,6 +92,7 @@ struct hostapd_rate_data { }; struct hostapd_frame_info { + unsigned int freq; u32 channel; u32 datarate; int ssi_signal; /* dBm */ @@ -109,6 +126,7 @@ struct hostapd_neighbor_entry { struct wpabuf *civic; /* LCI update time */ struct os_time lci_date; + int stationary; }; /** @@ -184,6 +202,17 @@ struct hostapd_data { #endif /* CONFIG_FULL_DYNAMIC_VLAN */ struct l2_packet_data *l2; + +#ifdef CONFIG_IEEE80211R_AP + struct dl_list l2_queue; + struct dl_list l2_oui_queue; + struct eth_p_oui_ctx *oui_pull; + struct eth_p_oui_ctx *oui_resp; + struct eth_p_oui_ctx *oui_push; + struct eth_p_oui_ctx *oui_sreq; + struct eth_p_oui_ctx *oui_sresp; +#endif /* CONFIG_IEEE80211R_AP */ + struct wps_context *wps; int beacon_set_done; @@ -242,9 +271,6 @@ struct hostapd_data { unsigned int cs_c_off_ecsa_beacon; unsigned int cs_c_off_ecsa_proberesp; - /* BSS Load */ - unsigned int bss_load_update_timeout; - #ifdef CONFIG_P2P struct p2p_data *p2p; struct p2p_group *p2p_group; @@ -259,9 +285,6 @@ struct hostapd_data { int noa_start; int noa_duration; #endif /* CONFIG_P2P */ -#ifdef CONFIG_INTERWORKING - size_t gas_frag_limit; -#endif /* CONFIG_INTERWORKING */ #ifdef CONFIG_PROXYARP struct l2_packet_data *sock_dhcp; struct l2_packet_data *sock_ndisc; @@ -292,6 +315,18 @@ struct hostapd_data { unsigned int ext_eapol_frame_io:1; struct l2_packet_data *l2_test; + + enum wpa_alg last_gtk_alg; + int last_gtk_key_idx; + u8 last_gtk[WPA_GTK_MAX_LEN]; + size_t last_gtk_len; + +#ifdef CONFIG_IEEE80211W + enum wpa_alg last_igtk_alg; + int last_igtk_key_idx; + u8 last_igtk[WPA_IGTK_MAX_LEN]; + size_t last_igtk_len; +#endif /* CONFIG_IEEE80211W */ #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_MBO @@ -300,10 +335,42 @@ struct hostapd_data { struct dl_list nr_db; + u8 beacon_req_token; u8 lci_req_token; u8 range_req_token; unsigned int lci_req_active:1; unsigned int range_req_active:1; + + int dhcp_sock; /* UDP socket used with the DHCP server */ + +#ifdef CONFIG_DPP + int dpp_init_done; + struct dpp_authentication *dpp_auth; + u8 dpp_allowed_roles; + int dpp_qr_mutual; + int dpp_auth_ok_on_ack; + int dpp_in_response_listen; + struct gas_query_ap *gas; + 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 */ }; @@ -311,6 +378,7 @@ struct hostapd_sta_info { struct dl_list list; u8 addr[ETH_ALEN]; struct os_reltime last_seen; + int ssi_signal; #ifdef CONFIG_TAXONOMY struct wpabuf *probe_ie_taxonomy; #endif /* CONFIG_TAXONOMY */ @@ -440,6 +508,10 @@ struct hostapd_iface { u64 last_channel_time_busy; u8 channel_utilization; + unsigned int chan_util_samples_sum; + unsigned int chan_util_num_sample_periods; + unsigned int chan_util_average; + /* eCSA IE will be added only if operating class is specified */ u8 cs_oper_class; @@ -459,6 +531,8 @@ struct hostapd_iface { struct dl_list sta_seen; /* struct hostapd_sta_info */ unsigned int num_sta_seen; + + u8 dfs_domain; }; /* hostapd.c */ @@ -466,6 +540,7 @@ int hostapd_for_each_interface(struct hapd_interfaces *interfaces, int (*cb)(struct hostapd_iface *iface, void *ctx), void *ctx); int hostapd_reload_config(struct hostapd_iface *iface); +void hostapd_reconfig_encryption(struct hostapd_data *hapd); struct hostapd_data * hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, struct hostapd_config *conf, @@ -492,6 +567,7 @@ void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator); void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s); const char * hostapd_state_text(enum hostapd_iface_state s); int hostapd_csa_in_progress(struct hostapd_iface *iface); +void hostapd_chan_switch_vht_config(struct hostapd_data *hapd, int vht_enabled); int hostapd_switch_channel(struct hostapd_data *hapd, struct csa_settings *settings); void @@ -499,6 +575,7 @@ hostapd_switch_channel_fallback(struct hostapd_iface *iface, const struct hostapd_freq_params *freq_params); void hostapd_cleanup_cs_params(struct hostapd_data *hapd); void hostapd_periodic_iface(struct hostapd_iface *iface); +int hostapd_owe_trans_get_info(struct hostapd_data *hapd); /* utils.c */ int hostapd_register_probereq_cb(struct hostapd_data *hapd, @@ -510,6 +587,8 @@ int hostapd_register_probereq_cb(struct hostapd_data *hapd, void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr); /* drv_callbacks.c (TODO: move to somewhere else?) */ +void hostapd_notify_assoc_fils_finish(struct hostapd_data *hapd, + struct sta_info *sta); int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, const u8 *ie, size_t ielen, int reassoc); void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr); @@ -533,6 +612,9 @@ hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity, struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces, const char *ifname); +void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr, + enum smps_mode smps_mode, + enum chan_width chan_width, u8 rx_nss); #ifdef CONFIG_FST void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd, diff --git a/src/ap/hs20.c b/src/ap/hs20.c index d7909fad4a144..e265569aef389 100644 --- a/src/ap/hs20.c +++ b/src/ap/hs20.c @@ -11,9 +11,11 @@ #include "common.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" #include "hostapd.h" #include "ap_config.h" #include "ap_drv_ops.h" +#include "sta_info.h" #include "hs20.h" @@ -175,3 +177,72 @@ int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd, return ret; } + + +int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd, + const u8 *addr, const char *url) +{ + struct wpabuf *buf; + int ret; + size_t url_len; + + if (!url) { + wpa_printf(MSG_INFO, "HS 2.0: No T&C Server URL available"); + return -1; + } + + url_len = os_strlen(url); + if (5 + url_len > 255) { + wpa_printf(MSG_INFO, + "HS 2.0: Too long T&C Server URL for WNM-Notification: '%s'", + url); + return -1; + } + + buf = wpabuf_alloc(4 + 7 + url_len); + if (!buf) + return -1; + + wpabuf_put_u8(buf, WLAN_ACTION_WNM); + wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ); + wpabuf_put_u8(buf, 1); /* Dialog token */ + wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */ + + /* Terms and Conditions Acceptance subelement */ + wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); + wpabuf_put_u8(buf, 4 + 1 + url_len); + wpabuf_put_be24(buf, OUI_WFA); + wpabuf_put_u8(buf, HS20_WNM_T_C_ACCEPTANCE); + wpabuf_put_u8(buf, url_len); + wpabuf_put_str(buf, url); + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + + wpabuf_free(buf); + + return ret; +} + + +void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta, + int enabled) +{ + if (enabled) { + wpa_printf(MSG_DEBUG, + "HS 2.0: Terms and Conditions filtering required for " + MACSTR, MAC2STR(sta->addr)); + sta->hs20_t_c_filtering = 1; + /* TODO: Enable firewall filtering for the STA */ + wpa_msg(hapd->msg_ctx, MSG_INFO, HS20_T_C_FILTERING_ADD MACSTR, + MAC2STR(sta->addr)); + } else { + wpa_printf(MSG_DEBUG, + "HS 2.0: Terms and Conditions filtering not required for " + MACSTR, MAC2STR(sta->addr)); + sta->hs20_t_c_filtering = 0; + /* TODO: Disable firewall filtering for the STA */ + wpa_msg(hapd->msg_ctx, MSG_INFO, + HS20_T_C_FILTERING_REMOVE MACSTR, MAC2STR(sta->addr)); + } +} diff --git a/src/ap/hs20.h b/src/ap/hs20.h index 152439f4dcb45..e99e26e911587 100644 --- a/src/ap/hs20.h +++ b/src/ap/hs20.h @@ -18,5 +18,9 @@ int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr, int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd, const u8 *addr, const struct wpabuf *payload); +int hs20_send_wnm_notification_t_c(struct hostapd_data *hapd, + const u8 *addr, const char *url); +void hs20_t_c_filtering(struct hostapd_data *hapd, struct sta_info *sta, + int enabled); #endif /* HS20_H */ diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c index 16887acdfef49..5279abca1f1fa 100644 --- a/src/ap/hw_features.c +++ b/src/ap/hw_features.c @@ -78,10 +78,12 @@ int hostapd_get_hw_features(struct hostapd_iface *iface) int i, j; u16 num_modes, flags; struct hostapd_hw_modes *modes; + u8 dfs_domain; if (hostapd_drv_none(hapd)) return -1; - modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags); + modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags, + &dfs_domain); if (modes == NULL) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -91,6 +93,7 @@ int hostapd_get_hw_features(struct hostapd_iface *iface) } iface->hw_flags = flags; + iface->dfs_domain = dfs_domain; hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); iface->hw_features = modes; @@ -329,6 +332,9 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface) res = ieee80211n_allowed_ht40_channel_pair(iface); if (!res) { iface->conf->secondary_channel = 0; + iface->conf->vht_oper_centr_freq_seg0_idx = 0; + iface->conf->vht_oper_centr_freq_seg1_idx = 0; + iface->conf->vht_oper_chwidth = VHT_CHANWIDTH_USE_HT; res = 1; wpa_printf(MSG_INFO, "Fallback to 20 MHz"); } @@ -621,41 +627,6 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) #ifdef CONFIG_IEEE80211AC - -static int ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, const char *name) -{ - u32 req_cap = conf & cap; - - /* - * Make sure we support all requested capabilities. - * NOTE: We assume that 'cap' represents a capability mask, - * not a discrete value. - */ - if ((hw & req_cap) != req_cap) { - wpa_printf(MSG_ERROR, "Driver does not support configured VHT capability [%s]", - name); - return 0; - } - return 1; -} - - -static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask, - unsigned int shift, - const char *name) -{ - u32 hw_max = hw & mask; - u32 conf_val = conf & mask; - - if (conf_val > hw_max) { - wpa_printf(MSG_ERROR, "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)", - name, conf_val >> shift, hw_max >> shift); - return 0; - } - return 1; -} - - static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface) { struct hostapd_hw_modes *mode = iface->current_mode; @@ -683,45 +654,7 @@ static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface) } } -#define VHT_CAP_CHECK(cap) \ - do { \ - if (!ieee80211ac_cap_check(hw, conf, cap, #cap)) \ - return 0; \ - } while (0) - -#define VHT_CAP_CHECK_MAX(cap) \ - do { \ - if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \ - #cap)) \ - return 0; \ - } while (0) - - VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK); - VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ); - VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ); - VHT_CAP_CHECK(VHT_CAP_RXLDPC); - VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80); - VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160); - VHT_CAP_CHECK(VHT_CAP_TXSTBC); - VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK); - VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE); - VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE); - VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX); - VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX); - VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE); - VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE); - VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS); - VHT_CAP_CHECK(VHT_CAP_HTC_VHT); - VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX); - VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB); - VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB); - VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN); - VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN); - -#undef VHT_CAP_CHECK -#undef VHT_CAP_CHECK_MAX - - return 1; + return ieee80211ac_cap_check(hw, conf); } #endif /* CONFIG_IEEE80211AC */ @@ -746,7 +679,8 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface) if (!ieee80211n_supported_ht_capab(iface)) return -1; #ifdef CONFIG_IEEE80211AC - if (!ieee80211ac_supported_vht_capab(iface)) + if (iface->conf->ieee80211ac && + !ieee80211ac_supported_vht_capab(iface)) return -1; #endif /* CONFIG_IEEE80211AC */ ret = ieee80211n_check_40mhz(iface); @@ -785,20 +719,41 @@ static int hostapd_is_usable_chan(struct hostapd_iface *iface, chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : ""); } + wpa_printf(MSG_INFO, "Channel %d (%s) not allowed for AP mode", + channel, primary ? "primary" : "secondary"); return 0; } static int hostapd_is_usable_chans(struct hostapd_iface *iface) { + int secondary_chan; + if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1)) return 0; if (!iface->conf->secondary_channel) return 1; - return hostapd_is_usable_chan(iface, iface->conf->channel + - iface->conf->secondary_channel * 4, 0); + if (!iface->conf->ht40_plus_minus_allowed) + return hostapd_is_usable_chan( + iface, iface->conf->channel + + iface->conf->secondary_channel * 4, 0); + + /* Both HT40+ and HT40- are set, pick a valid secondary channel */ + secondary_chan = iface->conf->channel + 4; + if (hostapd_is_usable_chan(iface, secondary_chan, 0)) { + iface->conf->secondary_channel = 1; + return 1; + } + + secondary_chan = iface->conf->channel - 4; + if (hostapd_is_usable_chan(iface, secondary_chan, 0)) { + iface->conf->secondary_channel = -1; + return 1; + } + + return 0; } @@ -978,5 +933,19 @@ int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan) int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) { - return hw_get_chan(hapd->iface->current_mode, freq); + int i, channel; + struct hostapd_hw_modes *mode; + + channel = hw_get_chan(hapd->iface->current_mode, freq); + if (channel) + return channel; + /* Check other available modes since the channel list for the current + * mode did not include the specified frequency. */ + for (i = 0; i < hapd->iface->num_hw_features; i++) { + mode = &hapd->iface->hw_features[i]; + channel = hw_get_chan(mode, freq); + if (channel) + return channel; + } + return 0; } diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 333035fe7703e..f9bb99d985495 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -1,6 +1,6 @@ /* * hostapd / IEEE 802.11 Management - * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,6 +14,8 @@ #include "utils/eloop.h" #include "crypto/crypto.h" #include "crypto/sha256.h" +#include "crypto/sha384.h" +#include "crypto/sha512.h" #include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" @@ -45,8 +47,21 @@ #include "mbo_ap.h" #include "rrm.h" #include "taxonomy.h" +#include "fils_hlp.h" +#include "dpp_hostapd.h" +#include "gas_query_ap.h" +#ifdef CONFIG_FILS +static struct wpabuf * +prepare_auth_resp_fils(struct hostapd_data *hapd, + struct sta_info *sta, u16 *resp, + struct rsn_pmksa_cache_entry *pmksa, + struct wpabuf *erp_resp, + const u8 *msk, size_t msk_len, + int *is_pub); +#endif /* CONFIG_FILS */ + u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; @@ -262,7 +277,7 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, static int send_auth_reply(struct hostapd_data *hapd, const u8 *dst, const u8 *bssid, u16 auth_alg, u16 auth_transaction, u16 resp, - const u8 *ies, size_t ies_len) + const u8 *ies, size_t ies_len, const char *dbg) { struct ieee80211_mgmt *reply; u8 *buf; @@ -289,9 +304,9 @@ static int send_auth_reply(struct hostapd_data *hapd, os_memcpy(reply->u.auth.variable, ies, ies_len); wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR - " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)", + " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu) (dbg=%s)", MAC2STR(dst), auth_alg, auth_transaction, - resp, (unsigned long) ies_len); + resp, (unsigned long) ies_len, dbg); if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0) wpa_printf(MSG_INFO, "send_auth_reply: send failed"); else @@ -303,7 +318,7 @@ static int send_auth_reply(struct hostapd_data *hapd, } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, u16 auth_transaction, u16 status, const u8 *ies, size_t ies_len) @@ -313,7 +328,8 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, int reply_res; reply_res = send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, - auth_transaction, status, ies, ies_len); + auth_transaction, status, ies, ies_len, + "auth-ft-finish"); sta = ap_get_sta(hapd, dst); if (sta == NULL) @@ -334,38 +350,65 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, sta->flags |= WLAN_STA_AUTH; mlme_authenticate_indication(hapd, sta); } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SAE -#define dot11RSNASAESync 5 /* attempts */ +static void sae_set_state(struct sta_info *sta, enum sae_state state, + const char *reason) +{ + wpa_printf(MSG_DEBUG, "SAE: State %s -> %s for peer " MACSTR " (%s)", + sae_state_txt(sta->sae->state), sae_state_txt(state), + MAC2STR(sta->addr), reason); + sta->sae->state = state; +} static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd, struct sta_info *sta, int update) { struct wpabuf *buf; + const char *password = NULL; + struct sae_password_entry *pw; + const char *rx_id = NULL; + + if (sta->sae->tmp) + rx_id = sta->sae->tmp->pw_id; - if (hapd->conf->ssid.wpa_passphrase == NULL) { + for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) { + if (!is_broadcast_ether_addr(pw->peer_addr) && + os_memcmp(pw->peer_addr, sta->addr, ETH_ALEN) != 0) + continue; + if ((rx_id && !pw->identifier) || (!rx_id && pw->identifier)) + continue; + if (rx_id && pw->identifier && + os_strcmp(rx_id, pw->identifier) != 0) + continue; + password = pw->password; + break; + } + if (!password) + password = hapd->conf->ssid.wpa_passphrase; + if (!password) { wpa_printf(MSG_DEBUG, "SAE: No password available"); return NULL; } if (update && sae_prepare_commit(hapd->own_addr, sta->addr, - (u8 *) hapd->conf->ssid.wpa_passphrase, - os_strlen(hapd->conf->ssid.wpa_passphrase), + (u8 *) password, os_strlen(password), rx_id, sta->sae) < 0) { wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE"); return NULL; } - buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN); + buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN + + (rx_id ? 3 + os_strlen(rx_id) : 0)); if (buf == NULL) return NULL; sae_write_commit(sta->sae, buf, sta->sae->tmp ? - sta->sae->tmp->anti_clogging_token : NULL); + sta->sae->tmp->anti_clogging_token : NULL, rx_id); return buf; } @@ -394,12 +437,14 @@ static int auth_sae_send_commit(struct hostapd_data *hapd, int reply_res; data = auth_build_sae_commit(hapd, sta, update); + if (!data && sta->sae->tmp && sta->sae->tmp->pw_id) + return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; if (data == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 1, WLAN_STATUS_SUCCESS, wpabuf_head(data), - wpabuf_len(data)); + wpabuf_len(data), "sae-send-commit"); wpabuf_free(data); @@ -420,7 +465,7 @@ static int auth_sae_send_confirm(struct hostapd_data *hapd, reply_res = send_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS, wpabuf_head(data), - wpabuf_len(data)); + wpabuf_len(data), "sae-send-confirm"); wpabuf_free(data); @@ -499,10 +544,10 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd, } -static int sae_check_big_sync(struct sta_info *sta) +static int sae_check_big_sync(struct hostapd_data *hapd, struct sta_info *sta) { - if (sta->sae->sync > dot11RSNASAESync) { - sta->sae->state = SAE_NOTHING; + if (sta->sae->sync > hapd->conf->sae_sync) { + sae_set_state(sta, SAE_NOTHING, "Sync > dot11RSNASAESync"); sta->sae->sync = 0; return -1; } @@ -516,12 +561,13 @@ static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data) struct sta_info *sta = eloop_data; int ret; - if (sae_check_big_sync(sta)) + if (sae_check_big_sync(hapd, sta)) return; sta->sae->sync++; wpa_printf(MSG_DEBUG, "SAE: Auth SAE retransmit timer for " MACSTR - " (sync=%d state=%d)", - MAC2STR(sta->addr), sta->sae->sync, sta->sae->state); + " (sync=%d state=%s)", + MAC2STR(sta->addr), sta->sae->sync, + sae_state_txt(sta->sae->state)); switch (sta->sae->state) { case SAE_COMMITTED: @@ -570,7 +616,7 @@ void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta) sta->auth_alg = WLAN_AUTH_SAE; mlme_authenticate_indication(hapd, sta); wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); - sta->sae->state = SAE_ACCEPTED; + sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm"); wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr, sta->sae->pmk, sta->sae->pmkid); } @@ -584,13 +630,16 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, if (auth_transaction != 1 && auth_transaction != 2) return WLAN_STATUS_UNSPECIFIED_FAILURE; + wpa_printf(MSG_DEBUG, "SAE: Peer " MACSTR " state=%s auth_trans=%u", + MAC2STR(sta->addr), sae_state_txt(sta->sae->state), + auth_transaction); switch (sta->sae->state) { case SAE_NOTHING: if (auth_transaction == 1) { ret = auth_sae_send_commit(hapd, sta, bssid, 1); if (ret) return ret; - sta->sae->state = SAE_COMMITTED; + sae_set_state(sta, SAE_COMMITTED, "Sent Commit"); if (sae_process_commit(sta->sae) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -612,7 +661,8 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, ret = auth_sae_send_confirm(hapd, sta, bssid); if (ret) return ret; - sta->sae->state = SAE_CONFIRMED; + sae_set_state(sta, SAE_CONFIRMED, + "Sent Confirm (mesh)"); } else { /* * For infrastructure BSS, send only the Commit @@ -641,7 +691,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, ret = auth_sae_send_confirm(hapd, sta, bssid); if (ret) return ret; - sta->sae->state = SAE_CONFIRMED; + sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm"); sta->sae->sync = 0; sae_set_retransmit_timer(hapd, sta); } else if (hapd->conf->mesh & MESH_ENABLED) { @@ -649,7 +699,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, * In mesh case, follow SAE finite state machine and * send Commit now, if sync count allows. */ - if (sae_check_big_sync(sta)) + if (sae_check_big_sync(hapd, sta)) return WLAN_STATUS_SUCCESS; sta->sae->sync++; @@ -668,7 +718,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, if (ret) return ret; - sta->sae->state = SAE_CONFIRMED; + sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm"); /* * Since this was triggered on Confirm RX, run another @@ -681,7 +731,7 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, case SAE_CONFIRMED: sae_clear_retransmit_timer(hapd, sta); if (auth_transaction == 1) { - if (sae_check_big_sync(sta)) + if (sae_check_big_sync(hapd, sta)) return WLAN_STATUS_SUCCESS; sta->sae->sync++; @@ -698,18 +748,31 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta, sae_set_retransmit_timer(hapd, sta); } else { + sta->sae->send_confirm = 0xffff; sae_accept_sta(hapd, sta); } break; case SAE_ACCEPTED: - if (auth_transaction == 1) { + if (auth_transaction == 1 && + (hapd->conf->mesh & MESH_ENABLED)) { wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR ") doing reauthentication", MAC2STR(sta->addr)); ap_free_sta(hapd, sta); wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr); + } else if (auth_transaction == 1) { + wpa_printf(MSG_DEBUG, "SAE: Start reauthentication"); + ret = auth_sae_send_commit(hapd, sta, bssid, 1); + if (ret) + return ret; + sae_set_state(sta, SAE_COMMITTED, "Sent Commit"); + + if (sae_process_commit(sta->sae) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + sta->sae->sync = 0; + sae_set_retransmit_timer(hapd, sta); } else { - if (sae_check_big_sync(sta)) + if (sae_check_big_sync(hapd, sta)) return WLAN_STATUS_SUCCESS; sta->sae->sync++; @@ -773,6 +836,29 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, int resp = WLAN_STATUS_SUCCESS; struct wpabuf *data = NULL; +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->conf->sae_reflection_attack && auth_transaction == 1) { + const u8 *pos, *end; + + wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack"); + pos = mgmt->u.auth.variable; + end = ((const u8 *) mgmt) + len; + send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, + auth_transaction, resp, pos, end - pos, + "auth-sae-reflection-attack"); + goto remove_sta; + } + + if (hapd->conf->sae_commit_override && auth_transaction == 1) { + wpa_printf(MSG_DEBUG, "SAE: TESTING - commit override"); + send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, + auth_transaction, resp, + wpabuf_head(hapd->conf->sae_commit_override), + wpabuf_len(hapd->conf->sae_commit_override), + "sae-commit-override"); + goto remove_sta; + } +#endif /* CONFIG_TESTING_OPTIONS */ if (!sta->sae) { if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS) { @@ -784,7 +870,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, resp = -1; goto remove_sta; } - sta->sae->state = SAE_NOTHING; + sae_set_state(sta, SAE_NOTHING, "Init"); sta->sae->sync = 0; } @@ -847,7 +933,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, "SAE: Failed to send commit message"); goto remove_sta; } - sta->sae->state = SAE_COMMITTED; + sae_set_state(sta, SAE_COMMITTED, + "Sent Commit (anti-clogging token case in mesh)"); sta->sae->sync = 0; sae_set_retransmit_timer(hapd, sta); return; @@ -866,6 +953,20 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, if (status_code != WLAN_STATUS_SUCCESS) goto remove_sta; + if (!(hapd->conf->mesh & MESH_ENABLED) && + sta->sae->state == SAE_COMMITTED) { + /* This is needed in the infrastructure BSS case to + * address a sequence where a STA entry may remain in + * hostapd across two attempts to do SAE authentication + * by the same STA. The second attempt may end up trying + * to use a different group and that would not be + * allowed if we remain in Committed state with the + * previously set parameters. */ + sae_set_state(sta, SAE_NOTHING, + "Clear existing state to allow restart"); + sae_clear_data(sta->sae); + } + resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable, ((const u8 *) mgmt) + len - mgmt->u.auth.variable, &token, @@ -876,6 +977,17 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, MAC2STR(sta->addr)); goto remove_sta; } + + if (resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) { + wpa_msg(hapd->msg_ctx, MSG_INFO, + WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER + MACSTR, MAC2STR(sta->addr)); + sae_clear_retransmit_timer(hapd, sta); + sae_set_state(sta, SAE_NOTHING, + "Unknown Password Identifier"); + goto remove_sta; + } + if (token && check_sae_token(hapd, sta->addr, token, token_len) < 0) { wpa_printf(MSG_DEBUG, "SAE: Drop commit message with " @@ -896,7 +1008,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, sta->addr); resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ; if (hapd->conf->mesh & MESH_ENABLED) - sta->sae->state = SAE_NOTHING; + sae_set_state(sta, SAE_NOTHING, + "Request anti-clogging token case in mesh"); goto reply; } @@ -910,12 +1023,36 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta, goto remove_sta; if (sta->sae->state >= SAE_CONFIRMED || !(hapd->conf->mesh & MESH_ENABLED)) { - if (sae_check_confirm(sta->sae, mgmt->u.auth.variable, - ((u8 *) mgmt) + len - - mgmt->u.auth.variable) < 0) { + const u8 *var; + size_t var_len; + u16 peer_send_confirm; + + var = mgmt->u.auth.variable; + var_len = ((u8 *) mgmt) + len - mgmt->u.auth.variable; + if (var_len < 2) { resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto reply; } + + peer_send_confirm = WPA_GET_LE16(var); + + if (sta->sae->state == SAE_ACCEPTED && + (peer_send_confirm <= sta->sae->rc || + peer_send_confirm == 0xffff)) { + wpa_printf(MSG_DEBUG, + "SAE: Silently ignore unexpected Confirm from peer " + MACSTR + " (peer-send-confirm=%u Rc=%u)", + MAC2STR(sta->addr), + peer_send_confirm, sta->sae->rc); + return; + } + + if (sae_check_confirm(sta->sae, var, var_len) < 0) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto reply; + } + sta->sae->rc = peer_send_confirm; } resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction); } else { @@ -933,7 +1070,7 @@ reply: send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE, auth_transaction, resp, data ? wpabuf_head(data) : (u8 *) "", - data ? wpabuf_len(data) : 0); + data ? wpabuf_len(data) : 0, "auth-sae"); } remove_sta: @@ -970,7 +1107,7 @@ int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta) if (ret) return -1; - sta->sae->state = SAE_COMMITTED; + sae_set_state(sta, SAE_COMMITTED, "Init and sent commit"); sta->sae->sync = 0; sae_set_retransmit_timer(hapd, sta); @@ -980,6 +1117,631 @@ int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta) #endif /* CONFIG_SAE */ +static u16 wpa_res_to_status_code(int res) +{ + if (res == WPA_INVALID_GROUP) + return WLAN_STATUS_GROUP_CIPHER_NOT_VALID; + if (res == WPA_INVALID_PAIRWISE) + return WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + if (res == WPA_INVALID_AKMP) + return WLAN_STATUS_AKMP_NOT_VALID; + if (res == WPA_ALLOC_FAIL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; +#ifdef CONFIG_IEEE80211W + if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) + return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; + if (res == WPA_INVALID_MGMT_GROUP_CIPHER) + return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; +#endif /* CONFIG_IEEE80211W */ + if (res == WPA_INVALID_MDIE) + return WLAN_STATUS_INVALID_MDIE; + if (res == WPA_INVALID_PMKID) + return WLAN_STATUS_INVALID_PMKID; + if (res != WPA_IE_OK) + return WLAN_STATUS_INVALID_IE; + return WLAN_STATUS_SUCCESS; +} + + +#ifdef CONFIG_FILS + +static void handle_auth_fils_finish(struct hostapd_data *hapd, + struct sta_info *sta, u16 resp, + struct wpabuf *data, int pub); + +void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *pos, size_t len, u16 auth_alg, + u16 auth_transaction, u16 status_code, + void (*cb)(struct hostapd_data *hapd, + struct sta_info *sta, u16 resp, + struct wpabuf *data, int pub)) +{ + u16 resp = WLAN_STATUS_SUCCESS; + const u8 *end; + struct ieee802_11_elems elems; + int res; + struct wpa_ie_data rsn; + struct rsn_pmksa_cache_entry *pmksa = NULL; + + if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS) + return; + + end = pos + len; + + wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields", + pos, end - pos); + + /* TODO: FILS PK */ +#ifdef CONFIG_FILS_SK_PFS + if (auth_alg == WLAN_AUTH_FILS_SK_PFS) { + u16 group; + struct wpabuf *pub; + size_t elem_len; + + /* Using FILS PFS */ + + /* Finite Cyclic Group */ + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, + "FILS: No room for Finite Cyclic Group"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + group = WPA_GET_LE16(pos); + pos += 2; + if (group != hapd->conf->fils_dh_group) { + wpa_printf(MSG_DEBUG, + "FILS: Unsupported Finite Cyclic Group: %u (expected %u)", + group, hapd->conf->fils_dh_group); + resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + goto fail; + } + + crypto_ecdh_deinit(sta->fils_ecdh); + sta->fils_ecdh = crypto_ecdh_init(group); + if (!sta->fils_ecdh) { + wpa_printf(MSG_INFO, + "FILS: Could not initialize ECDH with group %d", + group); + resp = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + goto fail; + } + + pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1); + if (!pub) { + wpa_printf(MSG_DEBUG, + "FILS: Failed to derive ECDH public key"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + elem_len = wpabuf_len(pub); + wpabuf_free(pub); + + /* Element */ + if ((size_t) (end - pos) < elem_len) { + wpa_printf(MSG_DEBUG, "FILS: No room for Element"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + wpabuf_free(sta->fils_g_sta); + sta->fils_g_sta = wpabuf_alloc_copy(pos, elem_len); + wpabuf_clear_free(sta->fils_dh_ss); + sta->fils_dh_ss = crypto_ecdh_set_peerkey(sta->fils_ecdh, 1, + pos, elem_len); + if (!sta->fils_dh_ss) { + wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", sta->fils_dh_ss); + pos += elem_len; + } else { + crypto_ecdh_deinit(sta->fils_ecdh); + sta->fils_ecdh = NULL; + wpabuf_clear_free(sta->fils_dh_ss); + sta->fils_dh_ss = NULL; + } +#endif /* CONFIG_FILS_SK_PFS */ + + wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos); + if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, "FILS: Could not parse elements"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + /* RSNE */ + wpa_hexdump(MSG_DEBUG, "FILS: RSN element", + elems.rsn_ie, elems.rsn_ie_len); + if (!elems.rsn_ie || + wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, + &rsn) < 0) { + wpa_printf(MSG_DEBUG, "FILS: No valid RSN element"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (!sta->wpa_sm) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, + NULL); + if (!sta->wpa_sm) { + wpa_printf(MSG_DEBUG, + "FILS: Failed to initialize RSN state machine"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + elems.rsn_ie - 2, elems.rsn_ie_len + 2, + elems.mdie, elems.mdie_len, NULL, 0); + resp = wpa_res_to_status_code(res); + if (resp != WLAN_STATUS_SUCCESS) + goto fail; + + if (!elems.fils_nonce) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.fils_nonce, + FILS_NONCE_LEN); + os_memcpy(sta->fils_snonce, elems.fils_nonce, FILS_NONCE_LEN); + + /* PMKID List */ + if (rsn.pmkid && rsn.num_pmkid > 0) { + u8 num; + const u8 *pmkid; + + wpa_hexdump(MSG_DEBUG, "FILS: PMKID List", + rsn.pmkid, rsn.num_pmkid * PMKID_LEN); + + pmkid = rsn.pmkid; + num = rsn.num_pmkid; + while (num) { + wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN); + pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, + pmkid); + if (pmksa) + break; + pmksa = wpa_auth_pmksa_get_fils_cache_id(hapd->wpa_auth, + sta->addr, + pmkid); + if (pmksa) + break; + pmkid += PMKID_LEN; + num--; + } + } + if (pmksa && wpa_auth_sta_key_mgmt(sta->wpa_sm) != pmksa->akmp) { + wpa_printf(MSG_DEBUG, + "FILS: Matching PMKSA cache entry has different AKMP (0x%x != 0x%x) - ignore", + wpa_auth_sta_key_mgmt(sta->wpa_sm), pmksa->akmp); + pmksa = NULL; + } + if (pmksa) + wpa_printf(MSG_DEBUG, "FILS: Found matching PMKSA cache entry"); + + /* FILS Session */ + if (!elems.fils_session) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Session element"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session, + FILS_SESSION_LEN); + os_memcpy(sta->fils_session, elems.fils_session, FILS_SESSION_LEN); + + /* FILS Wrapped Data */ + if (elems.fils_wrapped_data) { + wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data", + elems.fils_wrapped_data, + elems.fils_wrapped_data_len); + if (!pmksa) { +#ifndef CONFIG_NO_RADIUS + if (!sta->eapol_sm) { + sta->eapol_sm = + ieee802_1x_alloc_eapol_sm(hapd, sta); + } + wpa_printf(MSG_DEBUG, + "FILS: Forward EAP-Initiate/Re-auth to authentication server"); + ieee802_1x_encapsulate_radius( + hapd, sta, elems.fils_wrapped_data, + elems.fils_wrapped_data_len); + sta->fils_pending_cb = cb; + wpa_printf(MSG_DEBUG, + "FILS: Will send Authentication frame once the response from authentication server is available"); + sta->flags |= WLAN_STA_PENDING_FILS_ERP; + /* Calculate pending PMKID here so that we do not need + * to maintain a copy of the EAP-Initiate/Reauth + * message. */ + if (fils_pmkid_erp(wpa_auth_sta_key_mgmt(sta->wpa_sm), + elems.fils_wrapped_data, + elems.fils_wrapped_data_len, + sta->fils_erp_pmkid) == 0) + sta->fils_erp_pmkid_set = 1; + return; +#else /* CONFIG_NO_RADIUS */ + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; +#endif /* CONFIG_NO_RADIUS */ + } + } + +fail: + if (cb) { + struct wpabuf *data; + int pub = 0; + + data = prepare_auth_resp_fils(hapd, sta, &resp, pmksa, NULL, + NULL, 0, &pub); + if (!data) { + wpa_printf(MSG_DEBUG, + "%s: prepare_auth_resp_fils() returned failure", + __func__); + } + + cb(hapd, sta, resp, data, pub); + } +} + + +static struct wpabuf * +prepare_auth_resp_fils(struct hostapd_data *hapd, + struct sta_info *sta, u16 *resp, + struct rsn_pmksa_cache_entry *pmksa, + struct wpabuf *erp_resp, + const u8 *msk, size_t msk_len, + int *is_pub) +{ + u8 fils_nonce[FILS_NONCE_LEN]; + size_t ielen; + struct wpabuf *data = NULL; + const u8 *ie; + u8 *ie_buf = NULL; + const u8 *pmk = NULL; + size_t pmk_len = 0; + u8 pmk_buf[PMK_LEN_MAX]; + struct wpabuf *pub = NULL; + + if (*resp != WLAN_STATUS_SUCCESS) + goto fail; + + ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen); + if (!ie) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (pmksa) { + /* Add PMKID of the selected PMKSA into RSNE */ + ie_buf = os_malloc(ielen + 2 + 2 + PMKID_LEN); + if (!ie_buf) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + os_memcpy(ie_buf, ie, ielen); + if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid) < 0) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + ie = ie_buf; + } + + if (random_get_bytes(fils_nonce, FILS_NONCE_LEN) < 0) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce", + fils_nonce, FILS_NONCE_LEN); + +#ifdef CONFIG_FILS_SK_PFS + if (sta->fils_dh_ss && sta->fils_ecdh) { + pub = crypto_ecdh_get_pubkey(sta->fils_ecdh, 1); + if (!pub) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + } +#endif /* CONFIG_FILS_SK_PFS */ + + data = wpabuf_alloc(1000 + ielen + (pub ? wpabuf_len(pub) : 0)); + if (!data) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + /* TODO: FILS PK */ +#ifdef CONFIG_FILS_SK_PFS + if (pub) { + /* Finite Cyclic Group */ + wpabuf_put_le16(data, hapd->conf->fils_dh_group); + + /* Element */ + wpabuf_put_buf(data, pub); + } +#endif /* CONFIG_FILS_SK_PFS */ + + /* RSNE */ + wpabuf_put_data(data, ie, ielen); + + /* MDE when using FILS+FT (already included in ie,ielen with RSNE) */ + +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm))) { + /* FTE[R1KH-ID,R0KH-ID] when using FILS+FT */ + int res; + int use_sha384 = wpa_key_mgmt_sha384( + wpa_auth_sta_key_mgmt(sta->wpa_sm)); + + res = wpa_auth_write_fte(hapd->wpa_auth, use_sha384, + wpabuf_put(data, 0), + wpabuf_tailroom(data)); + if (res < 0) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpabuf_put(data, res); + } +#endif /* CONFIG_IEEE80211R_AP */ + + /* FILS Nonce */ + wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(data, 1 + FILS_NONCE_LEN); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(data, WLAN_EID_EXT_FILS_NONCE); + wpabuf_put_data(data, fils_nonce, FILS_NONCE_LEN); + + /* FILS Session */ + wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(data, 1 + FILS_SESSION_LEN); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(data, WLAN_EID_EXT_FILS_SESSION); + wpabuf_put_data(data, sta->fils_session, FILS_SESSION_LEN); + + /* FILS Wrapped Data */ + if (!pmksa && erp_resp) { + wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(data, 1 + wpabuf_len(erp_resp)); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(data, WLAN_EID_EXT_FILS_WRAPPED_DATA); + wpabuf_put_buf(data, erp_resp); + + if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm), + msk, msk_len, sta->fils_snonce, fils_nonce, + sta->fils_dh_ss ? + wpabuf_head(sta->fils_dh_ss) : NULL, + sta->fils_dh_ss ? + wpabuf_len(sta->fils_dh_ss) : 0, + pmk_buf, &pmk_len)) { + wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK"); + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + wpabuf_free(data); + data = NULL; + goto fail; + } + pmk = pmk_buf; + + /* Don't use DHss in PTK derivation if PMKSA caching is not + * used. */ + wpabuf_clear_free(sta->fils_dh_ss); + sta->fils_dh_ss = NULL; + + if (sta->fils_erp_pmkid_set) { + /* TODO: get PMKLifetime from WPA parameters */ + unsigned int dot11RSNAConfigPMKLifetime = 43200; + int session_timeout; + + session_timeout = dot11RSNAConfigPMKLifetime; + if (sta->session_timeout_set) { + struct os_reltime now, diff; + + os_get_reltime(&now); + os_reltime_sub(&sta->session_timeout, &now, + &diff); + session_timeout = diff.sec; + } + + sta->fils_erp_pmkid_set = 0; + if (wpa_auth_pmksa_add2( + hapd->wpa_auth, sta->addr, + pmk, pmk_len, + sta->fils_erp_pmkid, + session_timeout, + wpa_auth_sta_key_mgmt(sta->wpa_sm)) < 0) { + wpa_printf(MSG_ERROR, + "FILS: Failed to add PMKSA cache entry based on ERP"); + } + } + } else if (pmksa) { + pmk = pmksa->pmk; + pmk_len = pmksa->pmk_len; + } + + if (!pmk) { + wpa_printf(MSG_DEBUG, "FILS: No PMK available"); + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + wpabuf_free(data); + data = NULL; + goto fail; + } + + if (fils_auth_pmk_to_ptk(sta->wpa_sm, pmk, pmk_len, + sta->fils_snonce, fils_nonce, + sta->fils_dh_ss ? + wpabuf_head(sta->fils_dh_ss) : NULL, + sta->fils_dh_ss ? + wpabuf_len(sta->fils_dh_ss) : 0, + sta->fils_g_sta, pub) < 0) { + *resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + wpabuf_free(data); + data = NULL; + goto fail; + } + +fail: + if (is_pub) + *is_pub = pub != NULL; + os_free(ie_buf); + wpabuf_free(pub); + wpabuf_clear_free(sta->fils_dh_ss); + sta->fils_dh_ss = NULL; +#ifdef CONFIG_FILS_SK_PFS + crypto_ecdh_deinit(sta->fils_ecdh); + sta->fils_ecdh = NULL; +#endif /* CONFIG_FILS_SK_PFS */ + return data; +} + + +static void handle_auth_fils_finish(struct hostapd_data *hapd, + struct sta_info *sta, u16 resp, + struct wpabuf *data, int pub) +{ + u16 auth_alg; + + auth_alg = (pub || + resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED) ? + WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK; + send_auth_reply(hapd, sta->addr, hapd->own_addr, auth_alg, 2, resp, + data ? wpabuf_head(data) : (u8 *) "", + data ? wpabuf_len(data) : 0, "auth-fils-finish"); + wpabuf_free(data); + + if (resp == WLAN_STATUS_SUCCESS) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "authentication OK (FILS)"); + sta->flags |= WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->auth_alg = pub ? WLAN_AUTH_FILS_SK_PFS : WLAN_AUTH_FILS_SK; + mlme_authenticate_indication(hapd, sta); + } +} + + +void ieee802_11_finish_fils_auth(struct hostapd_data *hapd, + struct sta_info *sta, int success, + struct wpabuf *erp_resp, + const u8 *msk, size_t msk_len) +{ + struct wpabuf *data; + int pub = 0; + u16 resp; + + sta->flags &= ~WLAN_STA_PENDING_FILS_ERP; + + if (!sta->fils_pending_cb) + return; + resp = success ? WLAN_STATUS_SUCCESS : WLAN_STATUS_UNSPECIFIED_FAILURE; + data = prepare_auth_resp_fils(hapd, sta, &resp, NULL, erp_resp, + msk, msk_len, &pub); + if (!data) { + wpa_printf(MSG_DEBUG, + "%s: prepare_auth_resp_fils() returned failure", + __func__); + } + sta->fils_pending_cb(hapd, sta, resp, data, pub); +} + +#endif /* CONFIG_FILS */ + + +int +ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr, + const u8 *msg, size_t len, u32 *session_timeout, + u32 *acct_interim_interval, + struct vlan_description *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui, int is_probe_req) +{ + int res; + + os_memset(vlan_id, 0, sizeof(*vlan_id)); + res = hostapd_allowed_address(hapd, addr, msg, len, + session_timeout, acct_interim_interval, + vlan_id, psk, identity, radius_cui, + is_probe_req); + + if (res == HOSTAPD_ACL_REJECT) { + if (!is_probe_req) + wpa_printf(MSG_DEBUG, + "Station " MACSTR + " not allowed to authenticate", + MAC2STR(addr)); + return HOSTAPD_ACL_REJECT; + } + + if (res == HOSTAPD_ACL_PENDING) { + wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR + " waiting for an external authentication", + MAC2STR(addr)); + /* Authentication code will re-send the authentication frame + * after it has received (and cached) information from the + * external source. */ + return HOSTAPD_ACL_PENDING; + } + + return res; +} + + +static int +ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta, + int res, u32 session_timeout, + u32 acct_interim_interval, + struct vlan_description *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui) +{ + if (vlan_id->notempty && + !hostapd_vlan_valid(hapd->conf->vlan, vlan_id)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "Invalid VLAN %d%s received from RADIUS server", + vlan_id->untagged, + vlan_id->tagged[0] ? "+" : ""); + return -1; + } + if (ap_sta_set_vlan(hapd, sta, vlan_id) < 0) + return -1; + if (sta->vlan_id) + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); + + hostapd_free_psk_list(sta->psk); + if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) { + sta->psk = *psk; + *psk = NULL; + } else { + sta->psk = NULL; + } + + os_free(sta->identity); + sta->identity = *identity; + *identity = NULL; + + os_free(sta->radius_cui); + sta->radius_cui = *radius_cui; + *radius_cui = NULL; + + if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval) + sta->acct_interim_interval = acct_interim_interval; + if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) { + sta->session_timeout_set = 1; + os_get_reltime(&sta->session_timeout); + sta->session_timeout.sec += session_timeout; + ap_sta_session_timeout(hapd, sta, session_timeout); + } else { + sta->session_timeout_set = 0; + ap_sta_no_session_timeout(hapd, sta); + } + + return 0; +} + + static void handle_auth(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { @@ -998,8 +1760,6 @@ static void handle_auth(struct hostapd_data *hapd, char *radius_cui = NULL; u16 seq_ctrl; - os_memset(&vlan_id, 0, sizeof(vlan_id)); - if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)", (unsigned long) len); @@ -1047,20 +1807,29 @@ static void handle_auth(struct hostapd_data *hapd, #endif /* CONFIG_NO_RC4 */ if (hapd->tkip_countermeasures) { - resp = WLAN_REASON_MICHAEL_MIC_FAILURE; + wpa_printf(MSG_DEBUG, + "Ongoing TKIP countermeasures (Michael MIC failure) - reject authentication"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) && auth_alg == WLAN_AUTH_OPEN) || -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && auth_alg == WLAN_AUTH_FT) || -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SAE (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) && auth_alg == WLAN_AUTH_SAE) || #endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) && + auth_alg == WLAN_AUTH_FILS_SK) || + (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) && + hapd->conf->fils_dh_group && + auth_alg == WLAN_AUTH_FILS_SK_PFS) || +#endif /* CONFIG_FILS */ ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && auth_alg == WLAN_AUTH_SHARED_KEY))) { wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)", @@ -1139,29 +1908,23 @@ static void handle_auth(struct hostapd_data *hapd, } } - res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len, - &session_timeout, - &acct_interim_interval, &vlan_id, - &psk, &identity, &radius_cui); - + res = ieee802_11_allowed_address( + hapd, mgmt->sa, (const u8 *) mgmt, len, &session_timeout, + &acct_interim_interval, &vlan_id, &psk, &identity, &radius_cui, + 0); if (res == HOSTAPD_ACL_REJECT) { - wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate", - MAC2STR(mgmt->sa)); + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Ignore Authentication frame from " MACSTR + " due to ACL reject", MAC2STR(mgmt->sa)); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } - if (res == HOSTAPD_ACL_PENDING) { - wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR - " waiting for an external authentication", - MAC2STR(mgmt->sa)); - /* Authentication code will re-send the authentication frame - * after it has received (and cached) information from the - * external source. */ + if (res == HOSTAPD_ACL_PENDING) return; - } sta = ap_get_sta(hapd, mgmt->sa); if (sta) { + sta->flags &= ~WLAN_STA_PENDING_FILS_ERP; if ((fc & WLAN_FC_RETRY) && sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && sta->last_seq_ctrl == seq_ctrl && @@ -1203,6 +1966,7 @@ static void handle_auth(struct hostapd_data *hapd, sta = ap_sta_add(hapd, mgmt->sa); if (!sta) { + wpa_printf(MSG_DEBUG, "ap_sta_add() failed"); resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; goto fail; } @@ -1210,47 +1974,18 @@ static void handle_auth(struct hostapd_data *hapd, sta->last_seq_ctrl = seq_ctrl; sta->last_subtype = WLAN_FC_STYPE_AUTH; - if (vlan_id.notempty && - !hostapd_vlan_valid(hapd->conf->vlan, &vlan_id)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_INFO, - "Invalid VLAN %d%s received from RADIUS server", - vlan_id.untagged, - vlan_id.tagged[0] ? "+" : ""); + res = ieee802_11_set_radius_info( + hapd, sta, res, session_timeout, acct_interim_interval, + &vlan_id, &psk, &identity, &radius_cui); + if (res) { + wpa_printf(MSG_DEBUG, "ieee802_11_set_radius_info() failed"); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } - if (ap_sta_set_vlan(hapd, sta, &vlan_id) < 0) { - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; - goto fail; - } - if (sta->vlan_id) - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); - - hostapd_free_psk_list(sta->psk); - if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) { - sta->psk = psk; - psk = NULL; - } else { - sta->psk = NULL; - } - - sta->identity = identity; - identity = NULL; - sta->radius_cui = radius_cui; - radius_cui = NULL; sta->flags &= ~WLAN_STA_PREAUTH; ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); - if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval) - sta->acct_interim_interval = acct_interim_interval; - if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) - ap_sta_session_timeout(hapd, sta, session_timeout); - else - ap_sta_no_session_timeout(hapd, sta); - /* * If the driver supports full AP client state, add a station to the * driver before sending authentication reply to make sure the driver @@ -1263,8 +1998,15 @@ static void handle_auth(struct hostapd_data *hapd, * * In mesh mode, the station was already added to the driver when the * NEW_PEER_CANDIDATE event is received. + * + * If PMF was negotiated for the existing association, skip this to + * avoid dropping the STA entry and the associated keys. This is needed + * to allow the original connection work until the attempt can complete + * (re)association, so that unprotected Authentication frame cannot be + * used to bypass PMF protection. */ if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) && + (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) && !(hapd->conf->mesh & MESH_ENABLED) && !(sta->added_unassoc)) { /* @@ -1274,6 +2016,7 @@ static void handle_auth(struct hostapd_data *hapd, * updated. To handle this, station's added_unassoc flag is * cleared once the station has completed association. */ + ap_sta_set_authorized(hapd, sta, 0); hostapd_drv_sta_remove(hapd, sta->addr); sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH | WLAN_STA_AUTHORIZED); @@ -1305,6 +2048,9 @@ static void handle_auth(struct hostapd_data *hapd, case WLAN_AUTH_SHARED_KEY: resp = auth_shared_key(hapd, sta, auth_transaction, challenge, fc & WLAN_FC_ISWEP); + if (resp != 0) + wpa_printf(MSG_DEBUG, + "auth_shared_key() failed: status=%d", resp); sta->auth_alg = WLAN_AUTH_SHARED_KEY; mlme_authenticate_indication(hapd, sta); if (sta->challenge && auth_transaction == 1) { @@ -1316,7 +2062,7 @@ static void handle_auth(struct hostapd_data *hapd, } break; #endif /* CONFIG_NO_RC4 */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP case WLAN_AUTH_FT: sta->auth_alg = WLAN_AUTH_FT; if (sta->wpa_sm == NULL) @@ -1335,7 +2081,7 @@ static void handle_auth(struct hostapd_data *hapd, handle_auth_ft_finish, hapd); /* handle_auth_ft_finish() callback will complete auth. */ return; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SAE case WLAN_AUTH_SAE: #ifdef CONFIG_MESH @@ -1357,6 +2103,15 @@ static void handle_auth(struct hostapd_data *hapd, status_code); return; #endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + case WLAN_AUTH_FILS_SK: + case WLAN_AUTH_FILS_SK_PFS: + handle_auth_fils(hapd, sta, mgmt->u.auth.variable, + len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth), + auth_alg, auth_transaction, status_code, + handle_auth_fils_finish); + return; +#endif /* CONFIG_FILS */ } fail: @@ -1366,7 +2121,7 @@ static void handle_auth(struct hostapd_data *hapd, reply_res = send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, auth_transaction + 1, resp, resp_ies, - resp_ies_len); + resp_ies_len, "handle-auth"); if (sta && sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS || reply_res != WLAN_STATUS_SUCCESS)) { @@ -1459,6 +2214,11 @@ static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta, static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta, struct ieee802_11_elems *elems) { + /* Supported rates not used in IEEE 802.11ad/DMG */ + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) + return WLAN_STATUS_SUCCESS; + if (!elems->supp_rates) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -1496,12 +2256,187 @@ static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_INTERWORKING */ - if (ext_capab_ie_len > 0) + if (ext_capab_ie_len > 0) { sta->ecsa_supported = !!(ext_capab_ie[0] & BIT(2)); + os_free(sta->ext_capability); + sta->ext_capability = os_malloc(1 + ext_capab_ie_len); + if (sta->ext_capability) { + sta->ext_capability[0] = ext_capab_ie_len; + os_memcpy(sta->ext_capability + 1, ext_capab_ie, + ext_capab_ie_len); + } + } + + return WLAN_STATUS_SUCCESS; +} + + +#ifdef CONFIG_OWE + +static int owe_group_supported(struct hostapd_data *hapd, u16 group) +{ + int i; + int *groups = hapd->conf->owe_groups; + + if (group != 19 && group != 20 && group != 21) + return 0; + + if (!groups) + return 1; + + for (i = 0; groups[i] > 0; i++) { + if (groups[i] == group) + return 1; + } + + return 0; +} + + +static u16 owe_process_assoc_req(struct hostapd_data *hapd, + struct sta_info *sta, const u8 *owe_dh, + u8 owe_dh_len) +{ + struct wpabuf *secret, *pub, *hkey; + int res; + u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN]; + const char *info = "OWE Key Generation"; + const u8 *addr[2]; + size_t len[2]; + u16 group; + size_t hash_len, prime_len; + + if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) { + wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching"); + return WLAN_STATUS_SUCCESS; + } + + group = WPA_GET_LE16(owe_dh); + if (!owe_group_supported(hapd, group)) { + wpa_printf(MSG_DEBUG, "OWE: Unsupported DH group %u", group); + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + } + if (group == 19) + prime_len = 32; + else if (group == 20) + prime_len = 48; + else if (group == 21) + prime_len = 66; + else + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + + crypto_ecdh_deinit(sta->owe_ecdh); + sta->owe_ecdh = crypto_ecdh_init(group); + if (!sta->owe_ecdh) + return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; + sta->owe_group = group; + + secret = crypto_ecdh_set_peerkey(sta->owe_ecdh, 0, owe_dh + 2, + owe_dh_len - 2); + secret = wpabuf_zeropad(secret, prime_len); + if (!secret) { + wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret); + + /* prk = HKDF-extract(C | A | group, z) */ + + pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); + if (!pub) { + wpabuf_clear_free(secret); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + /* PMKID = Truncate-128(Hash(C | A)) */ + addr[0] = owe_dh + 2; + len[0] = owe_dh_len - 2; + addr[1] = wpabuf_head(pub); + len[1] = wpabuf_len(pub); + if (group == 19) { + res = sha256_vector(2, addr, len, pmkid); + hash_len = SHA256_MAC_LEN; + } else if (group == 20) { + res = sha384_vector(2, addr, len, pmkid); + hash_len = SHA384_MAC_LEN; + } else if (group == 21) { + res = sha512_vector(2, addr, len, pmkid); + hash_len = SHA512_MAC_LEN; + } else { + wpabuf_free(pub); + wpabuf_clear_free(secret); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + pub = wpabuf_zeropad(pub, prime_len); + if (res < 0 || !pub) { + wpabuf_free(pub); + wpabuf_clear_free(secret); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + hkey = wpabuf_alloc(owe_dh_len - 2 + wpabuf_len(pub) + 2); + if (!hkey) { + wpabuf_free(pub); + wpabuf_clear_free(secret); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + wpabuf_put_data(hkey, owe_dh + 2, owe_dh_len - 2); /* C */ + wpabuf_put_buf(hkey, pub); /* A */ + wpabuf_free(pub); + wpabuf_put_le16(hkey, group); /* group */ + if (group == 19) + res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey), + wpabuf_head(secret), wpabuf_len(secret), prk); + else if (group == 20) + res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey), + wpabuf_head(secret), wpabuf_len(secret), prk); + else if (group == 21) + res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey), + wpabuf_head(secret), wpabuf_len(secret), prk); + wpabuf_clear_free(hkey); + wpabuf_clear_free(secret); + if (res < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len); + + /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */ + + os_free(sta->owe_pmk); + sta->owe_pmk = os_malloc(hash_len); + if (!sta->owe_pmk) { + os_memset(prk, 0, SHA512_MAC_LEN); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (group == 19) + res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info, + os_strlen(info), sta->owe_pmk, hash_len); + else if (group == 20) + res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info, + os_strlen(info), sta->owe_pmk, hash_len); + else if (group == 21) + res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info, + os_strlen(info), sta->owe_pmk, hash_len); + os_memset(prk, 0, SHA512_MAC_LEN); + if (res < 0) { + os_free(sta->owe_pmk); + sta->owe_pmk = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + sta->owe_pmk_len = hash_len; + + wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sta->owe_pmk, sta->owe_pmk_len); + wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN); + wpa_auth_pmksa_add2(hapd->wpa_auth, sta->addr, sta->owe_pmk, + sta->owe_pmk_len, pmkid, 0, WPA_KEY_MGMT_OWE); return WLAN_STATUS_SUCCESS; } +#endif /* CONFIG_OWE */ + static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, const u8 *ies, size_t ies_len, int reassoc) @@ -1644,32 +2579,20 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, wpa_ie, wpa_ie_len, - elems.mdie, elems.mdie_len); - if (res == WPA_INVALID_GROUP) - resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; - else if (res == WPA_INVALID_PAIRWISE) - resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; - else if (res == WPA_INVALID_AKMP) - resp = WLAN_STATUS_AKMP_NOT_VALID; - else if (res == WPA_ALLOC_FAIL) - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; -#ifdef CONFIG_IEEE80211W - else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) - resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; - else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) - resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; -#endif /* CONFIG_IEEE80211W */ - else if (res == WPA_INVALID_MDIE) - resp = WLAN_STATUS_INVALID_MDIE; - else if (res != WPA_IE_OK) - resp = WLAN_STATUS_INVALID_IE; + elems.mdie, elems.mdie_len, + elems.owe_dh, elems.owe_dh_len); + resp = wpa_res_to_status_code(res); if (resp != WLAN_STATUS_SUCCESS) return resp; #ifdef CONFIG_IEEE80211W - if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) == + (WLAN_STA_ASSOC | WLAN_STA_MFP) && + !sta->sa_query_timed_out && sta->sa_query_count > 0) ap_check_sa_query_timeout(hapd, sta); - if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_MFP)) == + (WLAN_STA_ASSOC | WLAN_STA_MFP) && + !sta->sa_query_timed_out && (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) { /* * STA has already been associated with MFP and SA @@ -1690,7 +2613,7 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, sta->flags &= ~WLAN_STA_MFP; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (sta->auth_alg == WLAN_AUTH_FT) { if (!reassoc) { wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried " @@ -1705,9 +2628,13 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, if (resp != WLAN_STATUS_SUCCESS) return resp; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_SAE + if (wpa_auth_uses_sae(sta->wpa_sm) && sta->sae && + sta->sae->state == SAE_ACCEPTED) + wpa_auth_add_sae_pmkid(sta->wpa_sm, sta->sae->pmkid); + if (wpa_auth_uses_sae(sta->wpa_sm) && sta->auth_alg == WLAN_AUTH_OPEN) { struct rsn_pmksa_cache_entry *sa; @@ -1731,6 +2658,17 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, } #endif /* CONFIG_SAE */ +#ifdef CONFIG_OWE + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE && + elems.owe_dh) { + resp = owe_process_assoc_req(hapd, sta, elems.owe_dh, + elems.owe_dh_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + } +#endif /* CONFIG_OWE */ + #ifdef CONFIG_IEEE80211N if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) && wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) { @@ -1779,6 +2717,14 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, elems.hs20_len - 4); } else sta->hs20_ie = NULL; + + wpabuf_free(sta->roaming_consortium); + if (elems.roaming_cons_sel) + sta->roaming_consortium = wpabuf_alloc_copy( + elems.roaming_cons_sel + 4, + elems.roaming_cons_sel_len - 4); + else + sta->roaming_consortium = NULL; #endif /* CONFIG_HS20 */ #ifdef CONFIG_FST @@ -1810,6 +2756,14 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled, sizeof(sta->rrm_enabled_capa)); + if (elems.power_capab) { + sta->min_tx_power = elems.power_capab[0]; + sta->max_tx_power = elems.power_capab[1]; + sta->power_capab = 1; + } else { + sta->power_capab = 0; + } + return WLAN_STATUS_SUCCESS; } @@ -1856,7 +2810,8 @@ static int add_associated_sta(struct hostapd_data *hapd, */ if (!sta->added_unassoc && (!(sta->flags & WLAN_STA_AUTHORIZED) || - !wpa_auth_sta_ft_tk_already_set(sta->wpa_sm))) { + (!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) && + !wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) { hostapd_drv_sta_remove(hapd, sta->addr); wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED); set = 0; @@ -1904,21 +2859,36 @@ static int add_associated_sta(struct hostapd_data *hapd, static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, - u16 status_code, int reassoc, const u8 *ies, - size_t ies_len) + const u8 *addr, u16 status_code, int reassoc, + const u8 *ies, size_t ies_len) { int send_len; - u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; + u8 *buf; + size_t buflen; struct ieee80211_mgmt *reply; u8 *p; - - os_memset(buf, 0, sizeof(buf)); + u16 res = WLAN_STATUS_SUCCESS; + + buflen = sizeof(struct ieee80211_mgmt) + 1024; +#ifdef CONFIG_FILS + if (sta && sta->fils_hlp_resp) + buflen += wpabuf_len(sta->fils_hlp_resp); +#endif /* CONFIG_FILS */ +#ifdef CONFIG_OWE + if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) + buflen += 150; +#endif /* CONFIG_OWE */ + buf = os_zalloc(buflen); + if (!buf) { + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto done; + } reply = (struct ieee80211_mgmt *) buf; reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, (reassoc ? WLAN_FC_STYPE_REASSOC_RESP : WLAN_FC_STYPE_ASSOC_RESP)); - os_memcpy(reply->da, sta->addr, ETH_ALEN); + os_memcpy(reply->da, addr, ETH_ALEN); os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN); os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN); @@ -1927,24 +2897,40 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, reply->u.assoc_resp.capab_info = host_to_le16(hostapd_own_capab_info(hapd)); reply->u.assoc_resp.status_code = host_to_le16(status_code); - reply->u.assoc_resp.aid = host_to_le16(sta->aid | BIT(14) | BIT(15)); + + reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) | + BIT(14) | BIT(15)); /* Supported rates */ p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable); /* Extended supported rates */ p = hostapd_eid_ext_supp_rates(hapd, p); -#ifdef CONFIG_IEEE80211R - if (status_code == WLAN_STATUS_SUCCESS) { +#ifdef CONFIG_IEEE80211R_AP + if (sta && status_code == WLAN_STATUS_SUCCESS) { /* IEEE 802.11r: Mobility Domain Information, Fast BSS * Transition Information, RSN, [RIC Response] */ p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p, - buf + sizeof(buf) - p, + buf + buflen - p, sta->auth_alg, ies, ies_len); + if (!p) { + wpa_printf(MSG_DEBUG, + "FT: Failed to write AssocResp IEs"); + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto done; + } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ + +#ifdef CONFIG_OWE + if (sta && status_code == WLAN_STATUS_SUCCESS && + (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE)) + p = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, p, + buf + buflen - p, + ies, ies_len); +#endif /* CONFIG_OWE */ #ifdef CONFIG_IEEE80211W - if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) + if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) p = hostapd_eid_assoc_comeback_time(hapd, sta, p); #endif /* CONFIG_IEEE80211W */ @@ -1957,7 +2943,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { u32 nsts = 0, sta_nsts; - if (hapd->conf->use_sta_nsts && sta->vht_capabilities) { + if (sta && hapd->conf->use_sta_nsts && sta->vht_capabilities) { struct ieee80211_vht_capabilities *capa; nsts = (hapd->iface->conf->vht_capab >> @@ -1978,7 +2964,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, p = hostapd_eid_ext_capab(hapd, p); p = hostapd_eid_bss_max_idle_period(hapd, p); - if (sta->qos_map_enabled) + if (sta && sta->qos_map_enabled) p = hostapd_eid_qos_map_set(hapd, p); #ifdef CONFIG_FST @@ -1990,16 +2976,17 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_FST */ #ifdef CONFIG_IEEE80211AC - if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT)) + if (sta && hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT)) p = hostapd_eid_vendor_vht(hapd, p); #endif /* CONFIG_IEEE80211AC */ - if (sta->flags & WLAN_STA_WMM) + if (sta && (sta->flags & WLAN_STA_WMM)) p = hostapd_eid_wmm(hapd, p); #ifdef CONFIG_WPS - if ((sta->flags & WLAN_STA_WPS) || - ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa)) { + if (sta && + ((sta->flags & WLAN_STA_WPS) || + ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa))) { struct wpabuf *wps = wps_build_assoc_resp_ie(); if (wps) { os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps)); @@ -2010,7 +2997,7 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P - if (sta->p2p_ie && hapd->p2p_group) { + if (sta && sta->p2p_ie && hapd->p2p_group) { struct wpabuf *p2p_resp_ie; enum p2p_status_code status; switch (status_code) { @@ -2039,10 +3026,10 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, p = hostapd_eid_p2p_manage(hapd, p); #endif /* CONFIG_P2P_MANAGER */ - p = hostapd_eid_mbo(hapd, p, buf + sizeof(buf) - p); + p = hostapd_eid_mbo(hapd, p, buf + buflen - p); if (hapd->conf->assocresp_elements && - (size_t) (buf + sizeof(buf) - p) >= + (size_t) (buf + buflen - p) >= wpabuf_len(hapd->conf->assocresp_elements)) { os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements), wpabuf_len(hapd->conf->assocresp_elements)); @@ -2051,14 +3038,174 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, send_len += p - reply->u.assoc_resp.variable; +#ifdef CONFIG_FILS + if (sta && + (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) && + status_code == WLAN_STATUS_SUCCESS) { + struct ieee802_11_elems elems; + + if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) == + ParseFailed || !elems.fils_session) { + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto done; + } + + /* FILS Session */ + *p++ = WLAN_EID_EXTENSION; /* Element ID */ + *p++ = 1 + FILS_SESSION_LEN; /* Length */ + *p++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */ + os_memcpy(p, elems.fils_session, FILS_SESSION_LEN); + send_len += 2 + 1 + FILS_SESSION_LEN; + + send_len = fils_encrypt_assoc(sta->wpa_sm, buf, send_len, + buflen, sta->fils_hlp_resp); + if (send_len < 0) { + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto done; + } + } +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && + sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS && + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) { + struct wpabuf *pub; + + pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); + if (!pub) { + res = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto done; + } + /* OWE Diffie-Hellman Parameter element */ + *p++ = WLAN_EID_EXTENSION; /* Element ID */ + *p++ = 1 + 2 + wpabuf_len(pub); /* Length */ + *p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */ + WPA_PUT_LE16(p, sta->owe_group); + p += 2; + os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub)); + p += wpabuf_len(pub); + send_len += 3 + 2 + wpabuf_len(pub); + wpabuf_free(pub); + } +#endif /* CONFIG_OWE */ + if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) { wpa_printf(MSG_INFO, "Failed to send assoc resp: %s", strerror(errno)); - return WLAN_STATUS_UNSPECIFIED_FAILURE; + res = WLAN_STATUS_UNSPECIFIED_FAILURE; } - return WLAN_STATUS_SUCCESS; +done: + os_free(buf); + return res; +} + + +#ifdef CONFIG_OWE +u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *owe_dh, u8 owe_dh_len, + u8 *owe_buf, size_t owe_buf_len, u16 *reason) +{ +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->conf->own_ie_override) { + wpa_printf(MSG_DEBUG, "OWE: Using IE override"); + *reason = WLAN_STATUS_SUCCESS; + return wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, + owe_buf_len, NULL, 0); + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) { + wpa_printf(MSG_DEBUG, "OWE: Using PMKSA caching"); + owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, + owe_buf_len, NULL, 0); + *reason = WLAN_STATUS_SUCCESS; + return owe_buf; + } + + *reason = owe_process_assoc_req(hapd, sta, owe_dh, owe_dh_len); + if (*reason != WLAN_STATUS_SUCCESS) + return NULL; + + owe_buf = wpa_auth_write_assoc_resp_owe(sta->wpa_sm, owe_buf, + owe_buf_len, NULL, 0); + + if (sta->owe_ecdh && owe_buf) { + struct wpabuf *pub; + + pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0); + if (!pub) { + *reason = WLAN_STATUS_UNSPECIFIED_FAILURE; + return owe_buf; + } + + /* OWE Diffie-Hellman Parameter element */ + *owe_buf++ = WLAN_EID_EXTENSION; /* Element ID */ + *owe_buf++ = 1 + 2 + wpabuf_len(pub); /* Length */ + *owe_buf++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension + */ + WPA_PUT_LE16(owe_buf, sta->owe_group); + owe_buf += 2; + os_memcpy(owe_buf, wpabuf_head(pub), wpabuf_len(pub)); + owe_buf += wpabuf_len(pub); + wpabuf_free(pub); + } + + return owe_buf; } +#endif /* CONFIG_OWE */ + + +#ifdef CONFIG_FILS + +void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta) +{ + u16 reply_res; + + wpa_printf(MSG_DEBUG, "FILS: Finish association with " MACSTR, + MAC2STR(sta->addr)); + eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); + if (!sta->fils_pending_assoc_req) + return; + reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS, + sta->fils_pending_assoc_is_reassoc, + sta->fils_pending_assoc_req, + sta->fils_pending_assoc_req_len); + os_free(sta->fils_pending_assoc_req); + sta->fils_pending_assoc_req = NULL; + sta->fils_pending_assoc_req_len = 0; + wpabuf_free(sta->fils_hlp_resp); + sta->fils_hlp_resp = NULL; + wpabuf_free(sta->hlp_dhcp_discover); + sta->hlp_dhcp_discover = NULL; + + /* + * Remove the station in case transmission of a success response fails. + * At this point the station was already added associated to the driver. + */ + if (reply_res != WLAN_STATUS_SUCCESS) + hostapd_drv_sta_remove(hapd, sta->addr); +} + + +void fils_hlp_timeout(void *eloop_ctx, void *eloop_data) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = eloop_data; + + wpa_printf(MSG_DEBUG, + "FILS: HLP response timeout - continue with association response for " + MACSTR, MAC2STR(sta->addr)); + if (sta->fils_drv_assoc_finish) + hostapd_notify_assoc_fils_finish(hapd, sta); + else + fils_hlp_finish_assoc(hapd, sta); +} + +#endif /* CONFIG_FILS */ static void handle_assoc(struct hostapd_data *hapd, @@ -2070,6 +3217,13 @@ static void handle_assoc(struct hostapd_data *hapd, const u8 *pos; int left, i; struct sta_info *sta; + u8 *tmp = NULL; + struct hostapd_sta_wpa_psk_short *psk = NULL; + char *identity = NULL; + char *radius_cui = NULL; +#ifdef CONFIG_FILS + int delay_assoc = 0; +#endif /* CONFIG_FILS */ if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : sizeof(mgmt->u.assoc_req))) { @@ -2127,7 +3281,7 @@ static void handle_assoc(struct hostapd_data *hapd, } sta = ap_get_sta(hapd, mgmt->sa); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (sta && sta->auth_alg == WLAN_AUTH_FT && (sta->flags & WLAN_STA_AUTH) == 0) { wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate " @@ -2140,24 +3294,76 @@ static void handle_assoc(struct hostapd_data *hapd, */ sta->flags |= WLAN_STA_AUTH; } else -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { - hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, - HOSTAPD_LEVEL_INFO, "Station tried to " - "associate before authentication " - "(aid=%d flags=0x%x)", - sta ? sta->aid : -1, - sta ? sta->flags : 0); - send_deauth(hapd, mgmt->sa, - WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA); - return; + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == + HOSTAPD_MODE_IEEE80211AD) { + int acl_res; + u32 session_timeout, acct_interim_interval; + struct vlan_description vlan_id; + + acl_res = ieee802_11_allowed_address( + hapd, mgmt->sa, (const u8 *) mgmt, len, + &session_timeout, &acct_interim_interval, + &vlan_id, &psk, &identity, &radius_cui, 0); + if (acl_res == HOSTAPD_ACL_REJECT) { + wpa_msg(hapd->msg_ctx, MSG_DEBUG, + "Ignore Association Request frame from " + MACSTR " due to ACL reject", + MAC2STR(mgmt->sa)); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + if (acl_res == HOSTAPD_ACL_PENDING) + return; + + /* DMG/IEEE 802.11ad does not use authentication. + * Allocate sta entry upon association. */ + sta = ap_sta_add(hapd, mgmt->sa); + if (!sta) { + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Failed to add STA"); + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + goto fail; + } + + acl_res = ieee802_11_set_radius_info( + hapd, sta, acl_res, session_timeout, + acct_interim_interval, &vlan_id, &psk, + &identity, &radius_cui); + if (acl_res) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Skip authentication for DMG/IEEE 802.11ad"); + sta->flags |= WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->auth_alg = WLAN_AUTH_OPEN; + } else { + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Station tried to associate before authentication (aid=%d flags=0x%x)", + sta ? sta->aid : -1, + sta ? sta->flags : 0); + send_deauth(hapd, mgmt->sa, + WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA); + return; + } } if ((fc & WLAN_FC_RETRY) && sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ && sta->last_seq_ctrl == seq_ctrl && - sta->last_subtype == reassoc ? WLAN_FC_STYPE_REASSOC_REQ : - WLAN_FC_STYPE_ASSOC_REQ) { + sta->last_subtype == (reassoc ? WLAN_FC_STYPE_REASSOC_REQ : + WLAN_FC_STYPE_ASSOC_REQ)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "Drop repeated association frame seq_ctrl=0x%x", @@ -2169,7 +3375,7 @@ static void handle_assoc(struct hostapd_data *hapd, WLAN_FC_STYPE_ASSOC_REQ; if (hapd->tkip_countermeasures) { - resp = WLAN_REASON_MICHAEL_MIC_FAILURE; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } @@ -2195,6 +3401,32 @@ static void handle_assoc(struct hostapd_data *hapd, */ sta->capability = capab_info; +#ifdef CONFIG_FILS + if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) { + int res; + + /* The end of the payload is encrypted. Need to decrypt it + * before parsing. */ + + tmp = os_memdup(pos, left); + if (!tmp) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + res = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt, + len, tmp, left); + if (res < 0) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + pos = tmp; + left = res; + } +#endif /* CONFIG_FILS */ + /* followed by SSID and Supported rates; and HT capabilities if 802.11n * is used */ resp = check_assoc_ies(hapd, sta, pos, left, reassoc); @@ -2210,7 +3442,8 @@ static void handle_assoc(struct hostapd_data *hapd, sta->listen_interval = listen_interval; - if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) sta->flags |= WLAN_STA_NONERP; for (i = 0; i < sta->supported_rates_len; i++) { if ((sta->supported_rates[i] & 0x7f) > 22) { @@ -2229,7 +3462,8 @@ static void handle_assoc(struct hostapd_data *hapd, !sta->no_short_slot_time_set) { sta->no_short_slot_time_set = 1; hapd->iface->num_sta_no_short_slot_time++; - if (hapd->iface->current_mode->mode == + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_slot_time == 1) ieee802_11_set_beacons(hapd->iface); @@ -2244,7 +3478,8 @@ static void handle_assoc(struct hostapd_data *hapd, !sta->no_short_preamble_set) { sta->no_short_preamble_set = 1; hapd->iface->num_sta_no_short_preamble++; - if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_preamble == 1) ieee802_11_set_beacons(hapd->iface); } @@ -2281,7 +3516,22 @@ static void handle_assoc(struct hostapd_data *hapd, taxonomy_sta_info_assoc_req(hapd, sta, pos, left); #endif /* CONFIG_TAXONOMY */ + sta->pending_wds_enable = 0; + +#ifdef CONFIG_FILS + if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) { + if (fils_process_hlp(hapd, sta, pos, left) > 0) + delay_assoc = 1; + } +#endif /* CONFIG_FILS */ + fail: + os_free(identity); + os_free(radius_cui); + hostapd_free_psk_list(psk); + /* * In case of a successful response, add the station to the driver. * Otherwise, the kernel may ignore Data frames before we process the @@ -2300,18 +3550,44 @@ static void handle_assoc(struct hostapd_data *hapd, * issues with processing other non-Data Class 3 frames during this * window. */ - if (resp == WLAN_STATUS_SUCCESS && add_associated_sta(hapd, sta)) + if (resp == WLAN_STATUS_SUCCESS && sta && add_associated_sta(hapd, sta)) resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; - reply_res = send_assoc_resp(hapd, sta, resp, reassoc, pos, left); +#ifdef CONFIG_FILS + if (sta) { + eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); + os_free(sta->fils_pending_assoc_req); + sta->fils_pending_assoc_req = NULL; + sta->fils_pending_assoc_req_len = 0; + wpabuf_free(sta->fils_hlp_resp); + sta->fils_hlp_resp = NULL; + } + if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS) { + sta->fils_pending_assoc_req = tmp; + sta->fils_pending_assoc_req_len = left; + sta->fils_pending_assoc_is_reassoc = reassoc; + sta->fils_drv_assoc_finish = 0; + wpa_printf(MSG_DEBUG, + "FILS: Waiting for HLP processing before sending (Re)Association Response frame to " + MACSTR, MAC2STR(sta->addr)); + eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); + eloop_register_timeout(0, hapd->conf->fils_hlp_wait_time * 1024, + fils_hlp_timeout, hapd, sta); + return; + } +#endif /* CONFIG_FILS */ + + reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc, pos, + left); + os_free(tmp); /* * Remove the station in case tranmission of a success response fails * (the STA was added associated to the driver) or if the station was * previously added unassociated. */ - if ((reply_res != WLAN_STATUS_SUCCESS && - resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc) { + if (sta && ((reply_res != WLAN_STATUS_SUCCESS && + resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc)) { hostapd_drv_sta_remove(hapd, sta->addr); sta->added_unassoc = 0; } @@ -2368,6 +3644,17 @@ static void handle_disassoc(struct hostapd_data *hapd, mlme_disassociate_indication( hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code)); + + /* DMG/IEEE 802.11ad does not use deauthication. Deallocate sta upon + * disassociation. */ + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { + sta->flags &= ~WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "deauthenticated"); + ap_free_sta(hapd, sta); + } } @@ -2462,12 +3749,13 @@ static int robust_action_frame(u8 category) static int handle_action(struct hostapd_data *hapd, - const struct ieee80211_mgmt *mgmt, size_t len) + const struct ieee80211_mgmt *mgmt, size_t len, + unsigned int freq) { struct sta_info *sta; - sta = ap_get_sta(hapd, mgmt->sa); + u8 *action __maybe_unused; - if (len < IEEE80211_HDRLEN + 1) { + if (len < IEEE80211_HDRLEN + 2 + 1) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "handle_action - too short payload (len=%lu)", @@ -2475,11 +3763,19 @@ static int handle_action(struct hostapd_data *hapd, return 0; } + action = (u8 *) &mgmt->u.action.u; + wpa_printf(MSG_DEBUG, "RX_ACTION category %u action %u sa " MACSTR + " da " MACSTR " len %d freq %u", + mgmt->u.action.category, *action, + MAC2STR(mgmt->sa), MAC2STR(mgmt->da), (int) len, freq); + + sta = ap_get_sta(hapd, mgmt->sa); + if (mgmt->u.action.category != WLAN_ACTION_PUBLIC && (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) { wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action " "frame (category=%u) from unassociated STA " MACSTR, - MAC2STR(mgmt->sa), mgmt->u.action.category); + mgmt->u.action.category, MAC2STR(mgmt->sa)); return 0; } @@ -2516,14 +3812,14 @@ static int handle_action(struct hostapd_data *hapd, } switch (mgmt->u.action.category) { -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP case WLAN_ACTION_FT: if (!sta || wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, len - IEEE80211_HDRLEN)) break; return 1; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ case WLAN_ACTION_WMM: hostapd_wmm_action(hapd, mgmt, len); return 1; @@ -2531,11 +3827,11 @@ static int handle_action(struct hostapd_data *hapd, case WLAN_ACTION_SA_QUERY: return hostapd_sa_query_action(hapd, mgmt, len); #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP case WLAN_ACTION_WNM: ieee802_11_rx_wnm_action_ap(hapd, mgmt, len); return 1; -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ #ifdef CONFIG_FST case WLAN_ACTION_FST: if (hapd->iface->fst) @@ -2551,12 +3847,41 @@ static int handle_action(struct hostapd_data *hapd, if (len >= IEEE80211_HDRLEN + 2 && mgmt->u.action.u.public_action.action == WLAN_PA_20_40_BSS_COEX) { - wpa_printf(MSG_DEBUG, - "HT20/40 coex mgmt frame received from STA " - MACSTR, MAC2STR(mgmt->sa)); hostapd_2040_coex_action(hapd, mgmt, len); + return 1; } #endif /* CONFIG_IEEE80211N */ +#ifdef CONFIG_DPP + if (len >= IEEE80211_HDRLEN + 6 && + mgmt->u.action.u.vs_public_action.action == + WLAN_PA_VENDOR_SPECIFIC && + WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) == + OUI_WFA && + mgmt->u.action.u.vs_public_action.variable[0] == + DPP_OUI_TYPE) { + const u8 *pos, *end; + + pos = mgmt->u.action.u.vs_public_action.oui; + end = ((const u8 *) mgmt) + len; + hostapd_dpp_rx_action(hapd, mgmt->sa, pos, end - pos, + freq); + return 1; + } + if (len >= IEEE80211_HDRLEN + 2 && + (mgmt->u.action.u.public_action.action == + WLAN_PA_GAS_INITIAL_RESP || + mgmt->u.action.u.public_action.action == + WLAN_PA_GAS_COMEBACK_RESP)) { + const u8 *pos, *end; + + pos = &mgmt->u.action.u.public_action.action; + end = ((const u8 *) mgmt) + len; + gas_query_ap_rx(hapd->gas, mgmt->sa, + mgmt->u.action.category, + pos, end - pos, hapd->iface->freq); + return 1; + } +#endif /* CONFIG_DPP */ if (hapd->public_action_cb) { hapd->public_action_cb(hapd->public_action_cb_ctx, (u8 *) mgmt, len, @@ -2600,10 +3925,9 @@ static int handle_action(struct hostapd_data *hapd, */ wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action " "frame back to sender"); - resp = os_malloc(len); + resp = os_memdup(mgmt, len); if (resp == NULL) return 0; - os_memcpy(resp, mgmt, len); os_memcpy(resp->da, resp->sa, ETH_ALEN); os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); @@ -2639,10 +3963,17 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, struct ieee80211_mgmt *mgmt; u16 fc, stype; int ret = 0; + unsigned int freq; + int ssi_signal = fi ? fi->ssi_signal : 0; if (len < 24) return 0; + if (fi && fi->freq) + freq = fi->freq; + else + freq = hapd->iface->freq; + mgmt = (struct ieee80211_mgmt *) buf; fc = le_to_host16(mgmt->frame_control); stype = WLAN_FC_GET_STYPE(fc); @@ -2669,11 +4000,13 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, if (stype == WLAN_FC_STYPE_PROBE_REQ) { - handle_probe_req(hapd, mgmt, len, fi->ssi_signal); + handle_probe_req(hapd, mgmt, len, ssi_signal); return 1; } - if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) { + if ((!is_broadcast_ether_addr(mgmt->da) || + stype != WLAN_FC_STYPE_ACTION) && + os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) { hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "MGMT: DA=" MACSTR " not our address", @@ -2682,7 +4015,7 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, } if (hapd->iconf->track_sta_max_num) - sta_track_add(hapd->iface, mgmt->sa); + sta_track_add(hapd->iface, mgmt->sa, ssi_signal); switch (stype) { case WLAN_FC_STYPE_AUTH: @@ -2712,7 +4045,7 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, break; case WLAN_FC_STYPE_ACTION: wpa_printf(MSG_DEBUG, "mgmt::action"); - ret = handle_action(hapd, mgmt, len); + ret = handle_action(hapd, mgmt, len, freq); break; default: hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, @@ -2734,7 +4067,8 @@ static void handle_auth_cb(struct hostapd_data *hapd, sta = ap_get_sta(hapd, mgmt->da); if (!sta) { - wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found", + wpa_printf(MSG_DEBUG, "handle_auth_cb: STA " MACSTR + " not found", MAC2STR(mgmt->da)); return; } @@ -2856,11 +4190,15 @@ static void handle_assoc_cb(struct hostapd_data *hapd, new_assoc = 0; sta->flags |= WLAN_STA_ASSOC; sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE; - if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) || + if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && + !hapd->conf->osen) || + sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK || sta->auth_alg == WLAN_AUTH_FT) { /* - * Open, static WEP, or FT protocol; no separate authorization - * step. + * Open, static WEP, FT protocol, or FILS; no separate + * authorization step. */ ap_sta_set_authorized(hapd, sta, 1); } @@ -2874,16 +4212,6 @@ static void handle_assoc_cb(struct hostapd_data *hapd, sta->sa_query_timed_out = 0; #endif /* CONFIG_IEEE80211W */ - if (sta->flags & WLAN_STA_WDS) { - int ret; - char ifname_wds[IFNAMSIZ + 1]; - - ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr, - sta->aid, 1); - if (!ret) - hostapd_set_wds_encryption(hapd, sta, ifname_wds); - } - if (sta->eapol_sm == NULL) { /* * This STA does not use RADIUS server for EAP authentication, @@ -2900,6 +4228,27 @@ static void handle_assoc_cb(struct hostapd_data *hapd, hostapd_set_sta_flags(hapd, sta); + if (!(sta->flags & WLAN_STA_WDS) && sta->pending_wds_enable) { + wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for STA " + MACSTR " based on pending request", + MAC2STR(sta->addr)); + sta->pending_wds_enable = 0; + sta->flags |= WLAN_STA_WDS; + } + + if (sta->flags & WLAN_STA_WDS) { + int ret; + char ifname_wds[IFNAMSIZ + 1]; + + wpa_printf(MSG_DEBUG, "Reenable 4-address WDS mode for STA " + MACSTR " (aid %u)", + MAC2STR(sta->addr), sta->aid); + ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr, + sta->aid, 1); + if (!ret) + hostapd_set_wds_encryption(hapd, sta, ifname_wds); + } + if (sta->auth_alg == WLAN_AUTH_FT) wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); else @@ -2907,6 +4256,18 @@ static void handle_assoc_cb(struct hostapd_data *hapd, hapd->new_assoc_sta_cb(hapd, sta, !new_assoc); ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); +#ifdef CONFIG_FILS + if ((sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) && + fils_set_tk(sta->wpa_sm) < 0) { + wpa_printf(MSG_DEBUG, "FILS: TK configuration failed"); + ap_sta_disconnect(hapd, sta, sta->addr, + WLAN_REASON_UNSPECIFIED); + return; + } +#endif /* CONFIG_FILS */ + if (sta->pending_eapol_rx) { struct os_reltime now, age; @@ -2976,6 +4337,65 @@ static void handle_disassoc_cb(struct hostapd_data *hapd, } +static void handle_action_cb(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int ok) +{ + struct sta_info *sta; + const struct rrm_measurement_report_element *report; + + if (is_multicast_ether_addr(mgmt->da)) + return; +#ifdef CONFIG_DPP + if (len >= IEEE80211_HDRLEN + 6 && + mgmt->u.action.category == WLAN_ACTION_PUBLIC && + mgmt->u.action.u.vs_public_action.action == + WLAN_PA_VENDOR_SPECIFIC && + WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) == + OUI_WFA && + mgmt->u.action.u.vs_public_action.variable[0] == + DPP_OUI_TYPE) { + const u8 *pos, *end; + + pos = &mgmt->u.action.u.vs_public_action.variable[1]; + end = ((const u8 *) mgmt) + len; + hostapd_dpp_tx_status(hapd, mgmt->da, pos, end - pos, ok); + return; + } + if (len >= IEEE80211_HDRLEN + 2 && + mgmt->u.action.category == WLAN_ACTION_PUBLIC && + (mgmt->u.action.u.public_action.action == + WLAN_PA_GAS_INITIAL_REQ || + mgmt->u.action.u.public_action.action == + WLAN_PA_GAS_COMEBACK_REQ)) { + const u8 *pos, *end; + + pos = mgmt->u.action.u.public_action.variable; + end = ((const u8 *) mgmt) + len; + gas_query_ap_tx_status(hapd->gas, mgmt->da, pos, end - pos, ok); + return; + } +#endif /* CONFIG_DPP */ + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { + wpa_printf(MSG_DEBUG, "handle_action_cb: STA " MACSTR + " not found", MAC2STR(mgmt->da)); + return; + } + + if (len < 24 + 5 + sizeof(*report)) + return; + report = (const struct rrm_measurement_report_element *) + &mgmt->u.action.u.rrm.variable[2]; + if (mgmt->u.action.category == WLAN_ACTION_RADIO_MEASUREMENT && + mgmt->u.action.u.rrm.action == WLAN_RRM_RADIO_MEASUREMENT_REQUEST && + report->eid == WLAN_EID_MEASURE_REQUEST && + report->len >= 3 && + report->type == MEASURE_TYPE_BEACON) + hostapd_rrm_beacon_req_tx_status(hapd, mgmt, len, ok); +} + + /** * ieee802_11_mgmt_cb - Process management frame TX status callback * @hapd: hostapd BSS data structure (the BSS from which the management frame @@ -2993,8 +4413,16 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, #ifdef CONFIG_TESTING_OPTIONS if (hapd->ext_mgmt_frame_handling) { - wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-TX-STATUS stype=%u ok=%d", - stype, ok); + size_t hex_len = 2 * len + 1; + char *hex = os_malloc(hex_len); + + if (hex) { + wpa_snprintf_hex(hex, hex_len, buf, len); + wpa_msg(hapd->msg_ctx, MSG_INFO, + "MGMT-TX-STATUS stype=%u ok=%d buf=%s", + stype, ok, hex); + os_free(hex); + } return; } #endif /* CONFIG_TESTING_OPTIONS */ @@ -3025,6 +4453,7 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, break; case WLAN_FC_STYPE_ACTION: wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok); + handle_action_cb(hapd, mgmt, len, ok); break; default: wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype); @@ -3139,10 +4568,22 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, struct sta_info *sta; sta = ap_get_sta(hapd, src); - if (sta && (sta->flags & WLAN_STA_ASSOC)) { + if (sta && + ((sta->flags & WLAN_STA_ASSOC) || + ((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) { if (!hapd->conf->wds_sta) return; + if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK)) == + WLAN_STA_ASSOC_REQ_OK) { + wpa_printf(MSG_DEBUG, + "Postpone 4-address WDS mode enabling for STA " + MACSTR " since TX status for AssocResp is not yet known", + MAC2STR(sta->addr)); + sta->pending_wds_enable = 1; + return; + } + if (wds && !(sta->flags & WLAN_STA_WDS)) { int ret; char ifname_wds[IFNAMSIZ + 1]; diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 0327dec2a2bc3..2f3b4da8e752a 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -16,6 +16,8 @@ struct hostapd_frame_info; struct ieee80211_ht_capabilities; struct ieee80211_vht_capabilities; struct ieee80211_mgmt; +struct vlan_description; +struct hostapd_sta_wpa_psk_short; int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, struct hostapd_frame_info *fi); @@ -55,6 +57,8 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid); int hostapd_ht_operation_update(struct hostapd_iface *iface); void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, @@ -135,4 +139,31 @@ void ap_copy_sta_supp_op_classes(struct sta_info *sta, const u8 *supp_op_classes, size_t supp_op_classes_len); +u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid); +void ieee802_11_finish_fils_auth(struct hostapd_data *hapd, + struct sta_info *sta, int success, + struct wpabuf *erp_resp, + const u8 *msk, size_t msk_len); +u8 * owe_assoc_req_process(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *owe_dh, u8 owe_dh_len, + u8 *owe_buf, size_t owe_buf_len, u16 *reason); +void fils_hlp_timeout(void *eloop_ctx, void *eloop_data); +void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta); +void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *pos, size_t len, u16 auth_alg, + u16 auth_transaction, u16 status_code, + void (*cb)(struct hostapd_data *hapd, + struct sta_info *sta, + u16 resp, struct wpabuf *data, int pub)); + +size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd); +u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, size_t len); +int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr, + const u8 *msg, size_t len, u32 *session_timeout, + u32 *acct_interim_interval, + struct vlan_description *vlan_id, + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui, + int is_probe_req); + #endif /* IEEE802_11_H */ diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c index b8905373618db..5cb7fb1454f76 100644 --- a/src/ap/ieee802_11_auth.c +++ b/src/ap/ieee802_11_auth.c @@ -244,6 +244,7 @@ int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr, * @psk: Linked list buffer for returning WPA PSK * @identity: Buffer for returning identity (from RADIUS) * @radius_cui: Buffer for returning CUI (from RADIUS) + * @is_probe_req: Whether this query for a Probe Request frame * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING * * The caller is responsible for freeing the returned *identity and *radius_cui @@ -254,7 +255,8 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, u32 *acct_interim_interval, struct vlan_description *vlan_id, struct hostapd_sta_wpa_psk_short **psk, - char **identity, char **radius_cui) + char **identity, char **radius_cui, + int is_probe_req) { int res; @@ -281,6 +283,12 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, #else /* CONFIG_NO_RADIUS */ struct hostapd_acl_query_data *query; + if (is_probe_req) { + /* Skip RADIUS queries for Probe Request frames to avoid + * excessive load on the authentication server. */ + return HOSTAPD_ACL_ACCEPT; + }; + /* Check whether ACL cache has an entry for this station */ res = hostapd_acl_cache_get(hapd, addr, session_timeout, acct_interim_interval, vlan_id, psk, @@ -327,14 +335,13 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, return HOSTAPD_ACL_REJECT; } - query->auth_msg = os_malloc(len); + query->auth_msg = os_memdup(msg, len); if (query->auth_msg == NULL) { wpa_printf(MSG_ERROR, "Failed to allocate memory for " "auth frame."); hostapd_acl_query_free(query); return HOSTAPD_ACL_REJECT; } - os_memcpy(query->auth_msg, msg, len); query->auth_msg_len = len; query->next = hapd->acl_queries; hapd->acl_queries = query; @@ -665,9 +672,11 @@ void hostapd_acl_deinit(struct hostapd_data *hapd) #ifndef CONFIG_NO_RADIUS hostapd_acl_cache_free(hapd->acl_cache); + hapd->acl_cache = NULL; #endif /* CONFIG_NO_RADIUS */ query = hapd->acl_queries; + hapd->acl_queries = NULL; while (query) { prev = query; query = query->next; diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h index 71f53b9612faf..5aece5183c691 100644 --- a/src/ap/ieee802_11_auth.h +++ b/src/ap/ieee802_11_auth.h @@ -23,7 +23,8 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, u32 *acct_interim_interval, struct vlan_description *vlan_id, struct hostapd_sta_wpa_psk_short **psk, - char **identity, char **radius_cui); + char **identity, char **radius_cui, + int is_probe_req); int hostapd_acl_init(struct hostapd_data *hapd); void hostapd_acl_deinit(struct hostapd_data *hapd); void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk); diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c new file mode 100644 index 0000000000000..1a8d469729856 --- /dev/null +++ b/src/ap/ieee802_11_he.c @@ -0,0 +1,88 @@ +/* + * hostapd / IEEE 802.11ax HE + * Copyright (c) 2016-2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "hostapd.h" +#include "ap_config.h" +#include "beacon.h" +#include "ieee802_11.h" +#include "dfs.h" + +u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid) +{ + struct ieee80211_he_capabilities *cap; + u8 *pos = eid; + + if (!hapd->iface->current_mode) + return eid; + + *pos++ = WLAN_EID_EXTENSION; + *pos++ = 1 + sizeof(struct ieee80211_he_capabilities); + *pos++ = WLAN_EID_EXT_HE_CAPABILITIES; + + cap = (struct ieee80211_he_capabilities *) pos; + os_memset(cap, 0, sizeof(*cap)); + + if (hapd->iface->conf->he_phy_capab.he_su_beamformer) + cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |= + HE_PHYCAP_SU_BEAMFORMER_CAPAB; + + if (hapd->iface->conf->he_phy_capab.he_su_beamformee) + cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |= + HE_PHYCAP_SU_BEAMFORMEE_CAPAB; + + if (hapd->iface->conf->he_phy_capab.he_mu_beamformer) + cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |= + HE_PHYCAP_MU_BEAMFORMER_CAPAB; + + pos += sizeof(*cap); + + return pos; +} + + +u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid) +{ + struct ieee80211_he_operation *oper; + u8 *pos = eid; + + if (!hapd->iface->current_mode) + return eid; + + *pos++ = WLAN_EID_EXTENSION; + *pos++ = 1 + sizeof(struct ieee80211_he_operation); + *pos++ = WLAN_EID_EXT_HE_OPERATION; + + oper = (struct ieee80211_he_operation *) pos; + os_memset(oper, 0, sizeof(*oper)); + + if (hapd->iface->conf->he_op.he_bss_color) + oper->he_oper_params |= hapd->iface->conf->he_op.he_bss_color; + + if (hapd->iface->conf->he_op.he_default_pe_duration) + oper->he_oper_params |= + (hapd->iface->conf->he_op.he_default_pe_duration << + HE_OPERATION_DFLT_PE_DURATION_OFFSET); + + if (hapd->iface->conf->he_op.he_twt_required) + oper->he_oper_params |= HE_OPERATION_TWT_REQUIRED; + + if (hapd->iface->conf->he_op.he_rts_threshold) + oper->he_oper_params |= + (hapd->iface->conf->he_op.he_rts_threshold << + HE_OPERATION_RTS_THRESHOLD_OFFSET); + + /* TODO: conditional MaxBSSID Indicator subfield */ + + pos += sizeof(*oper); + + return pos; +} diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c index 5eb1060a29657..214855dccb8cf 100644 --- a/src/ap/ieee802_11_ht.c +++ b/src/ap/ieee802_11_ht.c @@ -236,17 +236,29 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, int i; const u8 *start = (const u8 *) mgmt; const u8 *data = start + IEEE80211_HDRLEN + 2; + struct sta_info *sta; + + wpa_printf(MSG_DEBUG, + "HT: Received 20/40 BSS Coexistence Management frame from " + MACSTR, MAC2STR(mgmt->sa)); hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d", mgmt->u.action.u.public_action.action); - if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) + if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { + wpa_printf(MSG_DEBUG, + "Ignore 20/40 BSS Coexistence Management frame since 40 MHz capability is not enabled"); return; + } - if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) + if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) { + wpa_printf(MSG_DEBUG, + "Ignore too short 20/40 BSS Coexistence Management frame"); return; + } + /* 20/40 BSS Coexistence element */ bc_ie = (struct ieee80211_2040_bss_coex_ie *) data; if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE || bc_ie->length < 1) { @@ -254,13 +266,35 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, bc_ie->element_id, bc_ie->length); return; } - if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) + if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) { + wpa_printf(MSG_DEBUG, + "Truncated 20/40 BSS Coexistence element"); return; + } data += 2 + bc_ie->length; - wpa_printf(MSG_DEBUG, "20/40 BSS Coexistence Information field: 0x%x", - bc_ie->coex_param); + wpa_printf(MSG_DEBUG, + "20/40 BSS Coexistence Information field: 0x%x (%s%s%s%s%s%s)", + bc_ie->coex_param, + (bc_ie->coex_param & BIT(0)) ? "[InfoReq]" : "", + (bc_ie->coex_param & BIT(1)) ? "[40MHzIntolerant]" : "", + (bc_ie->coex_param & BIT(2)) ? "[20MHzBSSWidthReq]" : "", + (bc_ie->coex_param & BIT(3)) ? "[OBSSScanExemptionReq]" : "", + (bc_ie->coex_param & BIT(4)) ? + "[OBSSScanExemptionGrant]" : "", + (bc_ie->coex_param & (BIT(5) | BIT(6) | BIT(7))) ? + "[Reserved]" : ""); + if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) { + /* Intra-BSS communication prohibiting 20/40 MHz BSS operation + */ + sta = ap_get_sta(hapd, mgmt->sa); + if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, + "Ignore intra-BSS 20/40 BSS Coexistence Management frame from not-associated STA"); + return; + } + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -269,6 +303,8 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, } if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) { + /* Inter-BSS communication prohibiting 20/40 MHz BSS operation + */ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -276,12 +312,16 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, is_ht40_allowed = 0; } - if (start + len - data >= 3 && - data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) { + /* 20/40 BSS Intolerant Channel Report element (zero or more times) */ + while (start + len - data >= 3 && + data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) { u8 ielen = data[1]; - if (ielen > start + len - data - 2) + if (ielen > start + len - data - 2) { + wpa_printf(MSG_DEBUG, + "Truncated 20/40 BSS Intolerant Channel Report element"); return; + } ic_report = (struct ieee80211_2040_intol_chan_report *) data; wpa_printf(MSG_DEBUG, "20/40 BSS Intolerant Channel Report: Operating Class %u", @@ -292,8 +332,10 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, for (i = 0; i < ielen - 1; i++) { u8 chan = ic_report->variable[i]; + if (chan == iface->conf->channel) + continue; /* matching own primary channel */ if (is_40_allowed(iface, chan)) - continue; + continue; /* not within affected channels */ hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, @@ -301,6 +343,8 @@ void hostapd_2040_coex_action(struct hostapd_data *hapd, chan); is_ht40_allowed = 0; } + + data += 2 + ielen; } wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d", is_ht40_allowed, iface->num_sta_ht40_intolerant); @@ -340,8 +384,8 @@ u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, * that did not specify a valid WMM IE in the (Re)Association Request * frame. */ - if (!ht_capab || - !(sta->flags & WLAN_STA_WMM) || hapd->conf->disable_11n) { + if (!ht_capab || !(sta->flags & WLAN_STA_WMM) || + !hapd->iconf->ieee80211n || hapd->conf->disable_11n) { sta->flags &= ~WLAN_STA_HT; os_free(sta->ht_capabilities); sta->ht_capabilities = NULL; diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c index 259413bd12ffb..49e9bf8cc7c9c 100644 --- a/src/ap/ieee802_11_shared.c +++ b/src/ap/ieee802_11_shared.c @@ -178,6 +178,10 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) case 1: /* Bits 8-15 */ if (hapd->conf->proxy_arp) *pos |= 0x10; /* Bit 12 - Proxy ARP */ + if (hapd->conf->coloc_intf_reporting) { + /* Bit 13 - Collocated Interference Reporting */ + *pos |= 0x20; + } break; case 2: /* Bits 16-23 */ if (hapd->conf->wnm_sleep_mode) @@ -186,9 +190,9 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) *pos |= 0x08; /* Bit 19 - BSS Transition */ break; case 3: /* Bits 24-31 */ -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP *pos |= 0x02; /* Bit 25 - SSID List */ -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ if (hapd->conf->time_advertisement == 2) *pos |= 0x08; /* Bit 27 - UTC TSF Offset */ if (hapd->conf->interworking) @@ -218,12 +222,21 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx) if (hapd->conf->ssid.utf8_ssid) *pos |= 0x01; /* Bit 48 - UTF-8 SSID */ break; + case 7: /* Bits 56-63 */ + break; case 8: /* Bits 64-71 */ if (hapd->conf->ftm_responder) *pos |= 0x40; /* Bit 70 - FTM responder */ if (hapd->conf->ftm_initiator) *pos |= 0x80; /* Bit 71 - FTM initiator */ break; + case 9: /* Bits 72-79 */ +#ifdef CONFIG_FILS + if ((hapd->conf->wpa & WPA_PROTO_RSN) && + wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) + *pos |= 0x01; +#endif /* CONFIG_FILS */ + break; } } @@ -246,10 +259,10 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) if (len < 9 && (hapd->conf->ftm_initiator || hapd->conf->ftm_responder)) len = 9; -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP if (len < 4) len = 4; -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ #ifdef CONFIG_HS20 if (hapd->conf->hs20 && len < 6) len = 6; @@ -258,6 +271,11 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid) if (hapd->conf->mbo_enabled && len < 6) len = 6; #endif /* CONFIG_MBO */ +#ifdef CONFIG_FILS + if ((!(hapd->conf->wpa & WPA_PROTO_RSN) || + !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) && len < 10) + len = 10; +#endif /* CONFIG_FILS */ if (len < hapd->iface->extended_capa_len) len = hapd->iface->extended_capa_len; if (len == 0) @@ -432,7 +450,7 @@ u8 * hostapd_eid_time_zone(struct hostapd_data *hapd, u8 *eid) { size_t len; - if (hapd->conf->time_advertisement != 2) + if (hapd->conf->time_advertisement != 2 || !hapd->conf->time_zone) return eid; len = os_strlen(hapd->conf->time_zone); @@ -503,7 +521,7 @@ u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP if (hapd->conf->ap_max_inactivity > 0) { unsigned int val; *pos++ = WLAN_EID_BSS_MAX_IDLE_PERIOD; @@ -521,7 +539,7 @@ u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) pos += 2; *pos++ = 0x00; /* TODO: Protected Keep-Alive Required */ } -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ return pos; } @@ -531,23 +549,38 @@ u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid) u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len) { - u8 mbo[6], *mbo_pos = mbo; + u8 mbo[9], *mbo_pos = mbo; u8 *pos = eid; - if (!hapd->conf->mbo_enabled) + if (!hapd->conf->mbo_enabled && + !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) return eid; - *mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND; - *mbo_pos++ = 1; - /* Not Cellular aware */ - *mbo_pos++ = 0; + if (hapd->conf->mbo_enabled) { + *mbo_pos++ = MBO_ATTR_ID_AP_CAPA_IND; + *mbo_pos++ = 1; + /* Not Cellular aware */ + *mbo_pos++ = 0; + } - if (hapd->mbo_assoc_disallow) { + if (hapd->conf->mbo_enabled && hapd->mbo_assoc_disallow) { *mbo_pos++ = MBO_ATTR_ID_ASSOC_DISALLOW; *mbo_pos++ = 1; *mbo_pos++ = hapd->mbo_assoc_disallow; } + if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) { + u8 ctrl; + + ctrl = OCE_RELEASE; + if (OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) + ctrl |= OCE_IS_STA_CFON; + + *mbo_pos++ = OCE_ATTR_ID_CAPA_IND; + *mbo_pos++ = 1; + *mbo_pos++ = ctrl; + } + pos += mbo_add_ie(pos, len, mbo, mbo_pos - mbo); return pos; @@ -556,19 +589,91 @@ u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len) u8 hostapd_mbo_ie_len(struct hostapd_data *hapd) { - if (!hapd->conf->mbo_enabled) + u8 len; + + if (!hapd->conf->mbo_enabled && + !OCE_STA_CFON_ENABLED(hapd) && !OCE_AP_ENABLED(hapd)) return 0; /* * MBO IE header (6) + Capability Indication attribute (3) + * Association Disallowed attribute (3) = 12 */ - return 6 + 3 + (hapd->mbo_assoc_disallow ? 3 : 0); + len = 6; + if (hapd->conf->mbo_enabled) + len += 3 + (hapd->mbo_assoc_disallow ? 3 : 0); + + /* OCE capability indication attribute (3) */ + if (OCE_STA_CFON_ENABLED(hapd) || OCE_AP_ENABLED(hapd)) + len += 3; + + return len; } #endif /* CONFIG_MBO */ +#ifdef CONFIG_OWE +static int hostapd_eid_owe_trans_enabled(struct hostapd_data *hapd) +{ + return hapd->conf->owe_transition_ssid_len > 0 && + !is_zero_ether_addr(hapd->conf->owe_transition_bssid); +} +#endif /* CONFIG_OWE */ + + +size_t hostapd_eid_owe_trans_len(struct hostapd_data *hapd) +{ +#ifdef CONFIG_OWE + if (!hostapd_eid_owe_trans_enabled(hapd)) + return 0; + return 6 + ETH_ALEN + 1 + hapd->conf->owe_transition_ssid_len; +#else /* CONFIG_OWE */ + return 0; +#endif /* CONFIG_OWE */ +} + + +u8 * hostapd_eid_owe_trans(struct hostapd_data *hapd, u8 *eid, + size_t len) +{ +#ifdef CONFIG_OWE + u8 *pos = eid; + size_t elen; + + if (hapd->conf->owe_transition_ifname[0] && + !hostapd_eid_owe_trans_enabled(hapd)) + hostapd_owe_trans_get_info(hapd); + + if (!hostapd_eid_owe_trans_enabled(hapd)) + return pos; + + elen = hostapd_eid_owe_trans_len(hapd); + if (len < elen) { + wpa_printf(MSG_DEBUG, + "OWE: Not enough room in the buffer for OWE IE"); + return pos; + } + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = elen - 2; + WPA_PUT_BE24(pos, OUI_WFA); + pos += 3; + *pos++ = OWE_OUI_TYPE; + os_memcpy(pos, hapd->conf->owe_transition_bssid, ETH_ALEN); + pos += ETH_ALEN; + *pos++ = hapd->conf->owe_transition_ssid_len; + os_memcpy(pos, hapd->conf->owe_transition_ssid, + hapd->conf->owe_transition_ssid_len); + pos += hapd->conf->owe_transition_ssid_len; + + return pos; +#else /* CONFIG_OWE */ + return eid; +#endif /* CONFIG_OWE */ +} + + void ap_copy_sta_supp_op_classes(struct sta_info *sta, const u8 *supp_op_classes, size_t supp_op_classes_len) @@ -584,3 +689,66 @@ void ap_copy_sta_supp_op_classes(struct sta_info *sta, os_memcpy(sta->supp_op_classes + 1, supp_op_classes, supp_op_classes_len); } + + +u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid) +{ + u8 *pos = eid; +#ifdef CONFIG_FILS + u8 *len; + u16 fils_info = 0; + size_t realms; + struct fils_realm *realm; + + if (!(hapd->conf->wpa & WPA_PROTO_RSN) || + !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) + return pos; + + realms = dl_list_len(&hapd->conf->fils_realms); + if (realms > 7) + realms = 7; /* 3 bit count field limits this to max 7 */ + + *pos++ = WLAN_EID_FILS_INDICATION; + len = pos++; + /* TODO: B0..B2: Number of Public Key Identifiers */ + if (hapd->conf->erp_domain) { + /* B3..B5: Number of Realm Identifiers */ + fils_info |= realms << 3; + } + /* TODO: B6: FILS IP Address Configuration */ + if (hapd->conf->fils_cache_id_set) + fils_info |= BIT(7); + if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) + fils_info |= BIT(8); /* HESSID Included */ + /* FILS Shared Key Authentication without PFS Supported */ + fils_info |= BIT(9); + if (hapd->conf->fils_dh_group) { + /* FILS Shared Key Authentication with PFS Supported */ + fils_info |= BIT(10); + } + /* TODO: B11: FILS Public Key Authentication Supported */ + /* B12..B15: Reserved */ + WPA_PUT_LE16(pos, fils_info); + pos += 2; + if (hapd->conf->fils_cache_id_set) { + os_memcpy(pos, hapd->conf->fils_cache_id, FILS_CACHE_ID_LEN); + pos += FILS_CACHE_ID_LEN; + } + if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) { + os_memcpy(pos, hapd->conf->hessid, ETH_ALEN); + pos += ETH_ALEN; + } + + dl_list_for_each(realm, &hapd->conf->fils_realms, struct fils_realm, + list) { + if (realms == 0) + break; + realms--; + os_memcpy(pos, realm->hash, 2); + pos += 2; + } + *len = pos - len - 1; +#endif /* CONFIG_FILS */ + + return pos; +} diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c index f30f63bc57094..8d0662078bcf1 100644 --- a/src/ap/ieee802_11_vht.c +++ b/src/ap/ieee802_11_vht.c @@ -334,7 +334,7 @@ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, { /* Disable VHT caps for STAs associated to no-VHT BSSes. */ if (!vht_capab || - hapd->conf->disable_11ac || + !hapd->iconf->ieee80211ac || hapd->conf->disable_11ac || !check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) { sta->flags &= ~WLAN_STA_VHT; os_free(sta->vht_capabilities); diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index 80ff996948f9e..185279f9e3ef1 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -31,6 +31,8 @@ #include "ap_drv_ops.h" #include "wps_hostapd.h" #include "hs20.h" +/* FIX: Not really a good thing to require ieee802_11.h here.. (FILS) */ +#include "ieee802_11.h" #include "ieee802_1x.h" @@ -316,6 +318,7 @@ static void ieee802_1x_learn_identity(struct hostapd_data *hapd, hdr->code != EAP_CODE_INITIATE)) return; + eap_erp_update_identity(sm->eap, eap, len); identity = eap_get_identity(sm->eap, &identity_len); if (identity == NULL) return; @@ -472,7 +475,7 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd, } } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && sta->wpa_sm && (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) || @@ -485,7 +488,7 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd, wpa_printf(MSG_ERROR, "Could not add Mobility-Domain-Id"); return -1; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm && add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0) @@ -588,9 +591,9 @@ int add_common_radius_attr(struct hostapd_data *hapd, } -static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, - struct sta_info *sta, - const u8 *eap, size_t len) +void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *eap, size_t len) { struct radius_msg *msg; struct eapol_state_machine *sm = sta->eapol_sm; @@ -680,6 +683,8 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, #ifdef CONFIG_HS20 if (hapd->conf->hs20) { u8 ver = 1; /* Release 2 */ + if (HS20_VERSION > 0x10) + ver = 2; /* Release 3 */ if (!radius_msg_add_wfa( msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION, &ver, 1)) { @@ -709,6 +714,41 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, goto fail; } } + + if (sta->roaming_consortium && + !radius_msg_add_wfa( + msg, RADIUS_VENDOR_ATTR_WFA_HS20_ROAMING_CONSORTIUM, + wpabuf_head(sta->roaming_consortium), + wpabuf_len(sta->roaming_consortium))) { + wpa_printf(MSG_ERROR, + "Could not add HS 2.0 Roaming Consortium"); + goto fail; + } + + if (hapd->conf->t_c_filename) { + be32 timestamp; + + if (!radius_msg_add_wfa( + msg, + RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILENAME, + (const u8 *) hapd->conf->t_c_filename, + os_strlen(hapd->conf->t_c_filename))) { + wpa_printf(MSG_ERROR, + "Could not add HS 2.0 T&C Filename"); + goto fail; + } + + timestamp = host_to_be32(hapd->conf->t_c_timestamp); + if (!radius_msg_add_wfa( + msg, + RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP, + (const u8 *) ×tamp, + sizeof(timestamp))) { + wpa_printf(MSG_ERROR, + "Could not add HS 2.0 Timestamp"); + goto fail; + } + } } #endif /* CONFIG_HS20 */ @@ -845,7 +885,7 @@ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta, } -static struct eapol_state_machine * +struct eapol_state_machine * ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta) { int flags = 0; @@ -970,7 +1010,9 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, } key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm); - if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) { + if (key_mgmt != -1 && + (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE || + key_mgmt == WPA_KEY_MGMT_DPP)) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - " "STA is using PSK"); return; @@ -1113,7 +1155,9 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) } key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm); - if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) { + if (key_mgmt != -1 && + (wpa_key_mgmt_wpa_psk(key_mgmt) || key_mgmt == WPA_KEY_MGMT_OWE || + key_mgmt == WPA_KEY_MGMT_DPP)) { wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK"); /* * Clear any possible EAPOL authenticator state to support @@ -1154,7 +1198,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) sta->eapol_sm->eap_if->portEnabled = TRUE; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (sta->auth_alg == WLAN_AUTH_FT) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, @@ -1170,10 +1214,32 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) sta->eapol_sm->portValid = TRUE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); - /* TODO: get vlan_id from R0KH using RRB message */ + ap_sta_bind_vlan(hapd, sta); + return; + } +#endif /* CONFIG_IEEE80211R_AP */ + +#ifdef CONFIG_FILS + if (sta->auth_alg == WLAN_AUTH_FILS_SK || + sta->auth_alg == WLAN_AUTH_FILS_SK_PFS || + sta->auth_alg == WLAN_AUTH_FILS_PK) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, + "PMK from FILS - skip IEEE 802.1X/EAP"); + /* Setup EAPOL state machines to already authenticated state + * because of existing FILS information. */ + sta->eapol_sm->keyRun = TRUE; + sta->eapol_sm->eap_if->eapKeyAvailable = TRUE; + sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING; + sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; + sta->eapol_sm->authSuccess = TRUE; + sta->eapol_sm->authFail = FALSE; + sta->eapol_sm->portValid = TRUE; + if (sta->eapol_sm->eap) + eap_sm_notify_cached(sta->eapol_sm->eap); return; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_FILS */ pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); if (pmksa) { @@ -1395,11 +1461,10 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, } } while (class_len < 1); - nclass[nclass_count].data = os_malloc(class_len); + nclass[nclass_count].data = os_memdup(attr_class, class_len); if (nclass[nclass_count].data == NULL) break; - os_memcpy(nclass[nclass_count].data, attr_class, class_len); nclass[nclass_count].len = class_len; nclass_count++; } @@ -1559,6 +1624,33 @@ static void ieee802_1x_hs20_session_info(struct hostapd_data *hapd, ap_sta_session_warning_timeout(hapd, sta, warning_time); } + +static void ieee802_1x_hs20_t_c_filtering(struct hostapd_data *hapd, + struct sta_info *sta, u8 *pos, + size_t len) +{ + if (len < 4) + return; /* Malformed information */ + wpa_printf(MSG_DEBUG, + "HS 2.0: Terms and Conditions filtering %02x %02x %02x %02x", + pos[0], pos[1], pos[2], pos[3]); + hs20_t_c_filtering(hapd, sta, pos[0] & BIT(0)); +} + + +static void ieee802_1x_hs20_t_c_url(struct hostapd_data *hapd, + struct sta_info *sta, u8 *pos, size_t len) +{ + os_free(sta->t_c_url); + sta->t_c_url = os_malloc(len + 1); + if (!sta->t_c_url) + return; + os_memcpy(sta->t_c_url, pos, len); + sta->t_c_url[len] = '\0'; + wpa_printf(MSG_DEBUG, + "HS 2.0: Terms and Conditions URL %s", sta->t_c_url); +} + #endif /* CONFIG_HS20 */ @@ -1606,6 +1698,12 @@ static void ieee802_1x_check_hs20(struct hostapd_data *hapd, ieee802_1x_hs20_session_info(hapd, sta, pos, sublen, session_timeout); break; + case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING: + ieee802_1x_hs20_t_c_filtering(hapd, sta, pos, sublen); + break; + case RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL: + ieee802_1x_hs20_t_c_url(hapd, sta, pos, sublen); + break; } } #endif /* CONFIG_HS20 */ @@ -1663,6 +1761,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, struct sta_info *sta; u32 session_timeout = 0, termination_action, acct_interim_interval; int session_timeout_set; + u32 reason_code; struct eapol_state_machine *sm; int override_eapReq = 0; struct radius_hdr *hdr = radius_msg_get_hdr(msg); @@ -1788,14 +1887,17 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, break; sta->session_timeout_set = !!session_timeout_set; - sta->session_timeout = session_timeout; + os_get_reltime(&sta->session_timeout); + sta->session_timeout.sec += session_timeout; /* RFC 3580, Ch. 3.17 */ if (session_timeout_set && termination_action == - RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) { + RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) sm->reAuthPeriod = session_timeout; - } else if (session_timeout_set) + else if (session_timeout_set) ap_sta_session_timeout(hapd, sta, session_timeout); + else + ap_sta_no_session_timeout(hapd, sta); sm->eap_if->aaaSuccess = TRUE; override_eapReq = 1; @@ -1811,6 +1913,13 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, case RADIUS_CODE_ACCESS_REJECT: sm->eap_if->aaaFail = TRUE; override_eapReq = 1; + if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE, + &reason_code) == 0) { + wpa_printf(MSG_DEBUG, + "RADIUS server indicated WLAN-Reason-Code %u in Access-Reject for " + MACSTR, reason_code, MAC2STR(sta->addr)); + sta->disconnect_reason_code = reason_code; + } break; case RADIUS_CODE_ACCESS_CHALLENGE: sm->eap_if->aaaEapReq = TRUE; @@ -1837,6 +1946,19 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, if (override_eapReq) sm->eap_if->aaaEapReq = FALSE; +#ifdef CONFIG_FILS +#ifdef NEED_AP_MLME + if (sta->flags & WLAN_STA_PENDING_FILS_ERP) { + /* TODO: Add a PMKSA entry on success? */ + ieee802_11_finish_fils_auth( + hapd, sta, hdr->code == RADIUS_CODE_ACCESS_ACCEPT, + sm->eap_if->aaaEapReqData, + sm->eap_if->aaaEapKeyData, + sm->eap_if->aaaEapKeyDataLen); + } +#endif /* NEED_AP_MLME */ +#endif /* CONFIG_FILS */ + eapol_auth_step(sm); return RADIUS_RX_QUEUED; @@ -1924,7 +2046,7 @@ static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx) wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d", eapol->default_wep_key_idx); - + if (ieee802_1x_rekey_broadcast(hapd)) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "failed to generate a " @@ -2034,13 +2156,19 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, } if (eap_user->password) { - user->password = os_malloc(eap_user->password_len); + user->password = os_memdup(eap_user->password, + eap_user->password_len); if (user->password == NULL) goto out; - os_memcpy(user->password, eap_user->password, - eap_user->password_len); user->password_len = eap_user->password_len; user->password_hash = eap_user->password_hash; + if (eap_user->salt && eap_user->salt_len) { + user->salt = os_memdup(eap_user->salt, + eap_user->salt_len); + if (!user->salt) + goto out; + user->salt_len = eap_user->salt_len; + } } user->force_version = eap_user->force_version; user->macacl = eap_user->macacl; @@ -2190,6 +2318,7 @@ int ieee802_1x_init(struct hostapd_data *hapd) conf.erp_domain = hapd->conf->erp_domain; conf.erp = hapd->conf->eap_server_erp; conf.tls_session_lifetime = hapd->conf->tls_session_lifetime; + conf.tls_flags = hapd->conf->tls_flags; conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key; conf.eap_fast_a_id = hapd->conf->eap_fast_a_id; conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len; @@ -2326,6 +2455,16 @@ int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta, MAC2STR(sta->addr), xhdr->version, xhdr->type, be_to_host16(xhdr->length), ack); +#ifdef CONFIG_WPS + if (xhdr->type == IEEE802_1X_TYPE_EAP_PACKET && ack && + (sta->flags & WLAN_STA_WPS) && + ap_sta_pending_delayed_1x_auth_fail_disconnect(hapd, sta)) { + wpa_printf(MSG_DEBUG, + "WPS: Indicate EAP completion on ACK for EAP-Failure"); + hostapd_wps_eap_completed(hapd); + } +#endif /* CONFIG_WPS */ + if (xhdr->type != IEEE802_1X_TYPE_EAPOL_KEY) return 0; @@ -2642,6 +2781,15 @@ static void ieee802_1x_wnm_notif_send(void *eloop_ctx, void *timeout_ctx) hs20_send_wnm_notification_deauth_req(hapd, sta->addr, sta->hs20_deauth_req); } + + if (sta->hs20_t_c_filtering) { + wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " + MACSTR " to indicate Terms and Conditions filtering", + MAC2STR(sta->addr)); + hs20_send_wnm_notification_t_c(hapd, sta->addr, sta->t_c_url); + os_free(sta->t_c_url); + sta->t_c_url = NULL; + } } #endif /* CONFIG_HS20 */ @@ -2655,6 +2803,7 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, /* TODO: get PMKLifetime from WPA parameters */ static const int dot11RSNAConfigPMKLifetime = 43200; unsigned int session_timeout; + struct os_reltime now, remaining; #ifdef CONFIG_HS20 if (remediation && !sta->remediation) { @@ -2665,7 +2814,8 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, sta->remediation_method = 1; /* SOAP-XML SPP */ } - if (success && (sta->remediation || sta->hs20_deauth_req)) { + if (success && (sta->remediation || sta->hs20_deauth_req || + sta->hs20_t_c_filtering)) { wpa_printf(MSG_DEBUG, "HS 2.0: Schedule WNM-Notification to " MACSTR " in 100 ms", MAC2STR(sta->addr)); eloop_cancel_timeout(ieee802_1x_wnm_notif_send, hapd, sta); @@ -2675,10 +2825,13 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, #endif /* CONFIG_HS20 */ key = ieee802_1x_get_key(sta->eapol_sm, &len); - if (sta->session_timeout_set) - session_timeout = sta->session_timeout; - else + if (sta->session_timeout_set) { + os_get_reltime(&now); + os_reltime_sub(&sta->session_timeout, &now, &remaining); + session_timeout = (remaining.sec > 0) ? remaining.sec : 1; + } else { session_timeout = dot11RSNAConfigPMKLifetime; + } if (success && key && len >= PMK_LEN && !sta->remediation && !sta->hs20_deauth_requested && wpa_auth_pmksa_add(sta->wpa_sm, key, len, session_timeout, @@ -2699,15 +2852,6 @@ static void ieee802_1x_finished(struct hostapd_data *hapd, * EAP-FAST with anonymous provisioning, may require another * EAPOL authentication to be started to complete connection. */ - wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "IEEE 802.1X: Force " - "disconnection after EAP-Failure"); - /* Add a small sleep to increase likelihood of previously - * requested EAP-Failure TX getting out before this should the - * driver reorder operations. - */ - os_sleep(0, 10000); - ap_sta_disconnect(hapd, sta, sta->addr, - WLAN_REASON_IEEE_802_1X_AUTH_FAILED); - hostapd_wps_eap_completed(hapd); + ap_sta_delayed_1x_auth_fail_disconnect(hapd, sta); } } diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h index ec80199007b68..9594661be8eec 100644 --- a/src/ap/ieee802_1x.h +++ b/src/ap/ieee802_1x.h @@ -57,5 +57,10 @@ int add_common_radius_attr(struct hostapd_data *hapd, struct hostapd_radius_attr *req_attr, struct sta_info *sta, struct radius_msg *msg); +void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *eap, size_t len); +struct eapol_state_machine * +ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta); #endif /* IEEE802_1X_H */ diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c index 3c086bfc7131b..4d6a92e086bd5 100644 --- a/src/ap/ndisc_snoop.c +++ b/src/ap/ndisc_snoop.c @@ -182,4 +182,5 @@ int ndisc_snoop_init(struct hostapd_data *hapd) void ndisc_snoop_deinit(struct hostapd_data *hapd) { l2_packet_deinit(hapd->sock_ndisc); + hapd->sock_ndisc = NULL; } diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c index a2efff6182868..b8fd5924b8893 100644 --- a/src/ap/neighbor_db.c +++ b/src/ap/neighbor_db.c @@ -43,6 +43,7 @@ static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr) nr->civic = NULL; os_memset(nr->bssid, 0, sizeof(nr->bssid)); os_memset(&nr->ssid, 0, sizeof(nr->ssid)); + nr->stationary = 0; } @@ -64,7 +65,7 @@ hostapd_neighbor_add(struct hostapd_data *hapd) int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid, const struct wpa_ssid_value *ssid, const struct wpabuf *nr, const struct wpabuf *lci, - const struct wpabuf *civic) + const struct wpabuf *civic, int stationary) { struct hostapd_neighbor_entry *entry; @@ -83,18 +84,20 @@ int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid, if (!entry->nr) goto fail; - if (lci) { + if (lci && wpabuf_len(lci)) { entry->lci = wpabuf_dup(lci); if (!entry->lci || os_get_time(&entry->lci_date)) goto fail; } - if (civic) { + if (civic && wpabuf_len(civic)) { entry->civic = wpabuf_dup(civic); if (!entry->civic) goto fail; } + entry->stationary = stationary; + return 0; fail: diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h index c22e043c120ef..ba46d88435e56 100644 --- a/src/ap/neighbor_db.h +++ b/src/ap/neighbor_db.h @@ -16,7 +16,7 @@ hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid, int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid, const struct wpa_ssid_value *ssid, const struct wpabuf *nr, const struct wpabuf *lci, - const struct wpabuf *civic); + const struct wpabuf *civic, int stationary); int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid, const struct wpa_ssid_value *ssid); void hostpad_free_neighbor_db(struct hostapd_data *hapd); diff --git a/src/ap/peerkey_auth.c b/src/ap/peerkey_auth.c deleted file mode 100644 index efc1d7e4c78f0..0000000000000 --- a/src/ap/peerkey_auth.c +++ /dev/null @@ -1,396 +0,0 @@ -/* - * hostapd - PeerKey for Direct Link Setup (DLS) - * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#include "utils/includes.h" - -#include "utils/common.h" -#include "utils/eloop.h" -#include "crypto/sha1.h" -#include "crypto/sha256.h" -#include "crypto/random.h" -#include "wpa_auth.h" -#include "wpa_auth_i.h" -#include "wpa_auth_ie.h" - -#ifdef CONFIG_PEERKEY - -static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx) -{ -#if 0 - struct wpa_authenticator *wpa_auth = eloop_ctx; - struct wpa_stsl_negotiation *neg = timeout_ctx; -#endif - - /* TODO: ? */ -} - - -struct wpa_stsl_search { - const u8 *addr; - struct wpa_state_machine *sm; -}; - - -static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx) -{ - struct wpa_stsl_search *search = ctx; - if (os_memcmp(search->addr, sm->addr, ETH_ALEN) == 0) { - search->sm = sm; - return 1; - } - return 0; -} - - -static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, const u8 *peer, - u16 mui, u16 error_type) -{ - u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN + - 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)]; - u8 *pos; - struct rsn_error_kde error; - - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, - "Sending SMK Error"); - - pos = kde; - - if (peer) { - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, - NULL, 0); - } - - error.mui = host_to_be16(mui); - error.error_type = host_to_be16(error_type); - pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR, - (u8 *) &error, sizeof(error), NULL, 0); - - __wpa_send_eapol(wpa_auth, sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR, - NULL, NULL, kde, pos - kde, 0, 0, 0); -} - - -void wpa_smk_m1(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key, - const u8 *key_data, size_t key_data_len) -{ - struct wpa_eapol_ie_parse kde; - struct wpa_stsl_search search; - u8 *buf, *pos; - size_t buf_len; - - if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1"); - return; - } - - if (kde.rsn_ie == NULL || kde.mac_addr == NULL || - kde.mac_addr_len < ETH_ALEN) { - wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " - "SMK M1"); - return; - } - - /* Initiator = sm->addr; Peer = kde.mac_addr */ - - search.addr = kde.mac_addr; - search.sm = NULL; - if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == - 0 || search.sm == NULL) { - wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR - " aborted - STA not associated anymore", - MAC2STR(kde.mac_addr)); - wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, - STK_ERR_STA_NR); - /* FIX: wpa_stsl_remove(wpa_auth, neg); */ - return; - } - - buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; - buf = os_malloc(buf_len); - if (buf == NULL) - return; - /* Initiator RSN IE */ - os_memcpy(buf, kde.rsn_ie, kde.rsn_ie_len); - pos = buf + kde.rsn_ie_len; - /* Initiator MAC Address */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN, - NULL, 0); - - /* SMK M2: - * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, - * MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE) - */ - - wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG, - "Sending SMK M2"); - - __wpa_send_eapol(wpa_auth, search.sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE, - NULL, key->key_nonce, buf, pos - buf, 0, 0, 0); - - os_free(buf); -} - - -static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - struct wpa_eapol_key *key, - struct wpa_eapol_ie_parse *kde, - const u8 *smk) -{ - u8 *buf, *pos; - size_t buf_len; - u32 lifetime; - - /* SMK M4: - * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce, - * MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE, - * Lifetime KDE) - */ - - buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN + - 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + - 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + - 2 + RSN_SELECTOR_LEN + sizeof(lifetime); - pos = buf = os_malloc(buf_len); - if (buf == NULL) - return; - - /* Initiator MAC Address */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN, - NULL, 0); - - /* Initiator Nonce */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN, - NULL, 0); - - /* SMK with PNonce */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, - key->key_nonce, WPA_NONCE_LEN); - - /* Lifetime */ - lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, - (u8 *) &lifetime, sizeof(lifetime), NULL, 0); - - wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "Sending SMK M4"); - - __wpa_send_eapol(wpa_auth, sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE, - NULL, key->key_nonce, buf, pos - buf, 0, 1, 0); - - os_free(buf); -} - - -static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - struct wpa_eapol_key *key, - struct wpa_eapol_ie_parse *kde, - const u8 *smk, const u8 *peer) -{ - u8 *buf, *pos; - size_t buf_len; - u32 lifetime; - - /* SMK M5: - * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, - * MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE, - * Lifetime KDE)) - */ - - buf_len = kde->rsn_ie_len + - 2 + RSN_SELECTOR_LEN + ETH_ALEN + - 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + - 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + - 2 + RSN_SELECTOR_LEN + sizeof(lifetime); - pos = buf = os_malloc(buf_len); - if (buf == NULL) - return; - - /* Peer RSN IE */ - os_memcpy(pos, kde->rsn_ie, kde->rsn_ie_len); - pos += kde->rsn_ie_len; - - /* Peer MAC Address */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0); - - /* PNonce */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce, - WPA_NONCE_LEN, NULL, 0); - - /* SMK and INonce */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, - kde->nonce, WPA_NONCE_LEN); - - /* Lifetime */ - lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, - (u8 *) &lifetime, sizeof(lifetime), NULL, 0); - - wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "Sending SMK M5"); - - __wpa_send_eapol(wpa_auth, sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_SMK_MESSAGE, - NULL, kde->nonce, buf, pos - buf, 0, 1, 0); - - os_free(buf); -} - - -void wpa_smk_m3(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key, - const u8 *key_data, size_t key_data_len) -{ - struct wpa_eapol_ie_parse kde; - struct wpa_stsl_search search; - u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos; - - if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3"); - return; - } - - if (kde.rsn_ie == NULL || - kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || - kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) { - wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or " - "Nonce KDE in SMK M3"); - return; - } - - /* Peer = sm->addr; Initiator = kde.mac_addr; - * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */ - - search.addr = kde.mac_addr; - search.sm = NULL; - if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == - 0 || search.sm == NULL) { - wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR - " aborted - STA not associated anymore", - MAC2STR(kde.mac_addr)); - wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, - STK_ERR_STA_NR); - /* FIX: wpa_stsl_remove(wpa_auth, neg); */ - return; - } - - if (random_get_bytes(smk, PMK_LEN)) { - wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK"); - return; - } - - /* SMK = PRF-256(Random number, "SMK Derivation", - * AA || Time || INonce || PNonce) - */ - os_memcpy(buf, wpa_auth->addr, ETH_ALEN); - pos = buf + ETH_ALEN; - wpa_get_ntp_timestamp(pos); - pos += 8; - os_memcpy(pos, kde.nonce, WPA_NONCE_LEN); - pos += WPA_NONCE_LEN; - os_memcpy(pos, key->key_nonce, WPA_NONCE_LEN); -#ifdef CONFIG_IEEE80211W - sha256_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), - smk, PMK_LEN); -#else /* CONFIG_IEEE80211W */ - sha1_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), - smk, PMK_LEN); -#endif /* CONFIG_IEEE80211W */ - - wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, PMK_LEN); - - wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk); - wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr); - - /* Authenticator does not need SMK anymore and it is required to forget - * it. */ - os_memset(smk, 0, sizeof(*smk)); -} - - -void wpa_smk_error(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - const u8 *key_data, size_t key_data_len) -{ - struct wpa_eapol_ie_parse kde; - struct wpa_stsl_search search; - struct rsn_error_kde error; - u16 mui, error_type; - - if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); - return; - } - - if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || - kde.error == NULL || kde.error_len < sizeof(error)) { - wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in " - "SMK Error"); - return; - } - - search.addr = kde.mac_addr; - search.sm = NULL; - if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == - 0 || search.sm == NULL) { - wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not " - "associated for SMK Error message from " MACSTR, - MAC2STR(kde.mac_addr), MAC2STR(sm->addr)); - return; - } - - os_memcpy(&error, kde.error, sizeof(error)); - mui = be_to_host16(error.mui); - error_type = be_to_host16(error.error_type); - wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, - "STA reported SMK Error: Peer " MACSTR - " MUI %d Error Type %d", - MAC2STR(kde.mac_addr), mui, error_type); - - wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type); -} - - -int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, - struct wpa_stsl_negotiation *neg) -{ - struct wpa_stsl_negotiation *pos, *prev; - - if (wpa_auth == NULL) - return -1; - pos = wpa_auth->stsl_negotiations; - prev = NULL; - while (pos) { - if (pos == neg) { - if (prev) - prev->next = pos->next; - else - wpa_auth->stsl_negotiations = pos->next; - - eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos); - os_free(pos); - return 0; - } - prev = pos; - pos = pos->next; - } - - return -1; -} - -#endif /* CONFIG_PEERKEY */ diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c index d610e7e5b0057..15e2c4943f2bd 100644 --- a/src/ap/pmksa_cache_auth.c +++ b/src/ap/pmksa_cache_auth.c @@ -282,7 +282,42 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa, int session_timeout, struct eapol_state_machine *eapol, int akmp) { - struct rsn_pmksa_cache_entry *entry, *pos; + struct rsn_pmksa_cache_entry *entry; + + entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, kck, kck_len, + aa, spa, session_timeout, eapol, + akmp); + + if (pmksa_cache_auth_add_entry(pmksa, entry) < 0) + return NULL; + + return entry; +} + + +/** + * pmksa_cache_auth_create_entry - Create a PMKSA cache entry + * @pmk: The new pairwise master key + * @pmk_len: PMK length in bytes, usually PMK_LEN (32) + * @pmkid: Calculated PMKID + * @kck: Key confirmation key or %NULL if not yet derived + * @kck_len: KCK length in bytes + * @aa: Authenticator address + * @spa: Supplicant address + * @session_timeout: Session timeout + * @eapol: Pointer to EAPOL state machine data + * @akmp: WPA_KEY_MGMT_* used in key derivation + * Returns: Pointer to the added PMKSA cache entry or %NULL on error + * + * This function creates a PMKSA entry. + */ +struct rsn_pmksa_cache_entry * +pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid, + const u8 *kck, size_t kck_len, const u8 *aa, + const u8 *spa, int session_timeout, + struct eapol_state_machine *eapol, int akmp) +{ + struct rsn_pmksa_cache_entry *entry; struct os_reltime now; if (pmk_len > PMK_LEN_MAX) @@ -303,8 +338,7 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, else if (wpa_key_mgmt_suite_b(akmp)) rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); else - rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, - wpa_key_mgmt_sha256(akmp)); + rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp); os_get_reltime(&now); entry->expiration = now.sec; if (session_timeout > 0) @@ -315,9 +349,30 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, os_memcpy(entry->spa, spa, ETH_ALEN); pmksa_cache_from_eapol_data(entry, eapol); + return entry; +} + + +/** + * pmksa_cache_auth_add_entry - Add a PMKSA cache entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + * @entry: Pointer to PMKSA cache entry + * + * This function adds PMKSA cache entry to the PMKSA cache. If an old entry is + * already in the cache for the same Supplicant, this entry will be replaced + * with the new entry. PMKID will be calculated based on the PMK. + */ +int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) +{ + struct rsn_pmksa_cache_entry *pos; + + if (entry == NULL) + return -1; + /* Replace an old entry for the same STA (if found) with the new entry */ - pos = pmksa_cache_auth_get(pmksa, spa, NULL); + pos = pmksa_cache_auth_get(pmksa, entry->spa, NULL); if (pos) pmksa_cache_free_entry(pmksa, pos); @@ -331,7 +386,7 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, pmksa_cache_link_entry(pmksa, entry); - return entry; + return 0; } @@ -462,7 +517,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0) continue; rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid, - wpa_key_mgmt_sha256(entry->akmp)); + entry->akmp); if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0) return entry; } @@ -605,3 +660,70 @@ int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) } return pos - buf; } + + +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL +#ifdef CONFIG_MESH + +/** + * pmksa_cache_auth_list_mesh - Dump text list of entries in PMKSA cache + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + * @addr: MAC address of the peer (NULL means any) + * @buf: Buffer for the list + * @len: Length of the buffer + * Returns: Number of bytes written to buffer + * + * This function is used to generate a text format representation of the + * current PMKSA cache contents for the ctrl_iface PMKSA_GET command to store + * in external storage. + */ +int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr, + char *buf, size_t len) +{ + int ret; + char *pos, *end; + struct rsn_pmksa_cache_entry *entry; + struct os_reltime now; + + pos = buf; + end = buf + len; + os_get_reltime(&now); + + + /* + * Entry format: + * <BSSID> <PMKID> <PMK> <expiration in seconds> + */ + for (entry = pmksa->pmksa; entry; entry = entry->next) { + if (addr && os_memcmp(entry->spa, addr, ETH_ALEN) != 0) + continue; + + ret = os_snprintf(pos, end - pos, MACSTR " ", + MAC2STR(entry->spa)); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + + pos += wpa_snprintf_hex(pos, end - pos, entry->pmkid, + PMKID_LEN); + + ret = os_snprintf(pos, end - pos, " "); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + + pos += wpa_snprintf_hex(pos, end - pos, entry->pmk, + entry->pmk_len); + + ret = os_snprintf(pos, end - pos, " %d\n", + (int) (entry->expiration - now.sec)); + if (os_snprintf_error(end - pos, ret)) + return 0; + pos += ret; + } + + return pos - buf; +} + +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h index d8d9c5a25c0e4..2ef217435b11f 100644 --- a/src/ap/pmksa_cache_auth.h +++ b/src/ap/pmksa_cache_auth.h @@ -35,6 +35,7 @@ struct rsn_pmksa_cache_entry { }; struct rsn_pmksa_cache; +struct radius_das_attrs; struct rsn_pmksa_cache * pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, @@ -53,6 +54,13 @@ pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa, int session_timeout, struct eapol_state_machine *eapol, int akmp); struct rsn_pmksa_cache_entry * +pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid, + const u8 *kck, size_t kck_len, const u8 *aa, + const u8 *spa, int session_timeout, + struct eapol_state_machine *eapol, int akmp); +int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry); +struct rsn_pmksa_cache_entry * pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, const struct rsn_pmksa_cache_entry *old_entry, const u8 *aa, const u8 *pmkid); @@ -65,5 +73,7 @@ int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa, struct radius_das_attrs *attr); int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa); +int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr, + char *buf, size_t len); #endif /* PMKSA_CACHE_H */ diff --git a/src/ap/rrm.c b/src/ap/rrm.c index 3569f955bcd27..56ed29c1c0687 100644 --- a/src/ap/rrm.c +++ b/src/ap/rrm.c @@ -2,6 +2,7 @@ * hostapd / Radio Measurement (RRM) * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. + * Copyright (c) 2016-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +11,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "common/wpa_ctrl.h" #include "hostapd.h" #include "ap_drv_ops.h" #include "sta_info.h" @@ -69,24 +71,47 @@ static void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token, } +static void hostapd_handle_beacon_report(struct hostapd_data *hapd, + const u8 *addr, u8 token, u8 rep_mode, + const u8 *pos, size_t len) +{ + char report[2 * 255 + 1]; + + wpa_printf(MSG_DEBUG, "Beacon report token %u len %zu from " MACSTR, + token, len, MAC2STR(addr)); + /* Skip to the beginning of the Beacon report */ + if (len < 3) + return; + pos += 3; + len -= 3; + report[0] = '\0'; + if (wpa_snprintf_hex(report, sizeof(report), pos, len) < 0) + return; + wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s", + MAC2STR(addr), token, rep_mode, report); +} + + static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd, const u8 *buf, size_t len) { const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; const u8 *pos, *ie, *end; - u8 token; + u8 token, rep_mode; end = buf + len; token = mgmt->u.action.u.rrm.dialog_token; pos = mgmt->u.action.u.rrm.variable; while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) { - if (ie[1] < 5) { + if (ie[1] < 3) { wpa_printf(MSG_DEBUG, "Bad Measurement Report element"); break; } - wpa_printf(MSG_DEBUG, "Measurement report type %u", ie[4]); + rep_mode = ie[3]; + wpa_printf(MSG_DEBUG, "Measurement report mode 0x%x type %u", + rep_mode, ie[4]); switch (ie[4]) { case MEASURE_TYPE_LCI: @@ -95,6 +120,10 @@ static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd, case MEASURE_TYPE_FTM_RANGE: hostapd_handle_range_report(hapd, token, ie + 2, ie[1]); break; + case MEASURE_TYPE_BEACON: + hostapd_handle_beacon_report(hapd, mgmt->sa, token, + rep_mode, ie + 2, ie[1]); + break; default: wpa_printf(MSG_DEBUG, "Measurement report type %u is not supported", @@ -118,7 +147,7 @@ static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len) /* Subelements are arranged as IEs */ subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE); if (subelem && subelem[1] == 2) - return *(u16 *) (subelem + 2); + return WPA_GET_LE16(subelem + 2); return 0; } @@ -129,12 +158,12 @@ static int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age) struct os_time curr, diff; unsigned long diff_l; + if (nr->stationary || max_age == 0xffff) + return 1; + if (!max_age) return 0; - if (max_age == 0xffff) - return 1; - if (os_get_time(&curr)) return 0; @@ -341,13 +370,7 @@ int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr) struct sta_info *sta = ap_get_sta(hapd, addr); int ret; - if (!sta) { - wpa_printf(MSG_INFO, - "Request LCI: Destination address is not in station list"); - return -1; - } - - if (!(sta->flags & WLAN_STA_AUTHORIZED)) { + if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) { wpa_printf(MSG_INFO, "Request LCI: Destination address is not connected"); return -1; @@ -450,9 +473,8 @@ int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr, wpa_printf(MSG_DEBUG, "Request range: Range request is already in process; overriding"); hapd->range_req_active = 0; - eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0, - hostapd_range_rep_timeout_handler, hapd, - NULL); + eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, + NULL); } /* Action + measurement type + token + reps + EID + len = 7 */ @@ -542,3 +564,111 @@ void hostapd_clean_rrm(struct hostapd_data *hapd) eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL); hapd->range_req_active = 0; } + + +int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr, + u8 req_mode, const struct wpabuf *req) +{ + struct wpabuf *buf; + struct sta_info *sta = ap_get_sta(hapd, addr); + int ret; + enum beacon_report_mode mode; + const u8 *pos; + + /* Request data: + * Operating Class (1), Channel Number (1), Randomization Interval (2), + * Measurement Duration (2), Measurement Mode (1), BSSID (6), + * Optional Subelements (variable) + */ + if (wpabuf_len(req) < 13) { + wpa_printf(MSG_INFO, "Beacon request: Too short request data"); + return -1; + } + pos = wpabuf_head(req); + mode = pos[6]; + + if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) { + wpa_printf(MSG_INFO, + "Beacon request: " MACSTR " is not connected", + MAC2STR(addr)); + return -1; + } + + switch (mode) { + case BEACON_REPORT_MODE_PASSIVE: + if (!(sta->rrm_enabled_capa[0] & + WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE)) { + wpa_printf(MSG_INFO, + "Beacon request: " MACSTR + " does not support passive beacon report", + MAC2STR(addr)); + return -1; + } + break; + case BEACON_REPORT_MODE_ACTIVE: + if (!(sta->rrm_enabled_capa[0] & + WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE)) { + wpa_printf(MSG_INFO, + "Beacon request: " MACSTR + " does not support active beacon report", + MAC2STR(addr)); + return -1; + } + break; + case BEACON_REPORT_MODE_TABLE: + if (!(sta->rrm_enabled_capa[0] & + WLAN_RRM_CAPS_BEACON_REPORT_TABLE)) { + wpa_printf(MSG_INFO, + "Beacon request: " MACSTR + " does not support table beacon report", + MAC2STR(addr)); + return -1; + } + break; + default: + wpa_printf(MSG_INFO, + "Beacon request: Unknown measurement mode %d", mode); + return -1; + } + + buf = wpabuf_alloc(5 + 2 + 3 + wpabuf_len(req)); + if (!buf) + return -1; + + hapd->beacon_req_token++; + if (!hapd->beacon_req_token) + hapd->beacon_req_token++; + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST); + wpabuf_put_u8(buf, hapd->beacon_req_token); + wpabuf_put_le16(buf, 0); /* Number of repetitions */ + + /* Measurement Request element */ + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + wpabuf_put_u8(buf, 3 + wpabuf_len(req)); + wpabuf_put_u8(buf, 1); /* Measurement Token */ + wpabuf_put_u8(buf, req_mode); /* Measurement Request Mode */ + wpabuf_put_u8(buf, MEASURE_TYPE_BEACON); /* Measurement Type */ + wpabuf_put_buf(buf, req); + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); + if (ret < 0) + return ret; + + return hapd->beacon_req_token; +} + + +void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int ok) +{ + if (len < 24 + 3) + return; + wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_REQ_TX_STATUS MACSTR + " %u ack=%d", MAC2STR(mgmt->da), + mgmt->u.action.u.rrm.dialog_token, ok); +} diff --git a/src/ap/rrm.h b/src/ap/rrm.h index f07fd41ac0195..02cd522ee9d63 100644 --- a/src/ap/rrm.h +++ b/src/ap/rrm.h @@ -24,5 +24,10 @@ int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr, u16 random_interval, u8 min_ap, const u8 *responders, unsigned int n_responders); void hostapd_clean_rrm(struct hostapd_data *hapd); +int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr, + u8 req_mode, const struct wpabuf *req); +void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int ok); #endif /* RRM_H */ diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index f12d4088b1314..179cf43b60b17 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -1,6 +1,6 @@ /* * hostapd / Station table - * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -17,6 +17,7 @@ #include "radius/radius_client.h" #include "p2p/p2p.h" #include "fst/fst.h" +#include "crypto/crypto.h" #include "hostapd.h" #include "accounting.h" #include "ieee802_1x.h" @@ -36,6 +37,7 @@ #include "ndisc_snoop.h" #include "sta_info.h" #include "vlan.h" +#include "wps_hostapd.h" static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, struct sta_info *sta); @@ -47,6 +49,7 @@ static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx); static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx); #endif /* CONFIG_IEEE80211W */ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta); +static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx); int ap_for_each_sta(struct hostapd_data *hapd, int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, @@ -194,7 +197,8 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) if (sta->no_short_slot_time_set) { sta->no_short_slot_time_set = 0; hapd->iface->num_sta_no_short_slot_time--; - if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_slot_time == 0) set_beacon++; } @@ -202,7 +206,8 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) if (sta->no_short_preamble_set) { sta->no_short_preamble_set = 0; hapd->iface->num_sta_no_short_preamble--; - if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->iface->num_sta_no_short_preamble == 0) set_beacon++; } @@ -316,6 +321,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) wpabuf_free(sta->wps_ie); wpabuf_free(sta->p2p_ie); wpabuf_free(sta->hs20_ie); + wpabuf_free(sta->roaming_consortium); #ifdef CONFIG_FST wpabuf_free(sta->mb_ies); #endif /* CONFIG_FST */ @@ -326,6 +332,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) os_free(sta->identity); os_free(sta->radius_cui); os_free(sta->remediation_url); + os_free(sta->t_c_url); wpabuf_free(sta->hs20_deauth_req); os_free(sta->hs20_session_info_url); @@ -337,6 +344,31 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) mbo_ap_sta_free(sta); os_free(sta->supp_op_classes); +#ifdef CONFIG_FILS + os_free(sta->fils_pending_assoc_req); + wpabuf_free(sta->fils_hlp_resp); + wpabuf_free(sta->hlp_dhcp_discover); + eloop_cancel_timeout(fils_hlp_timeout, hapd, sta); +#ifdef CONFIG_FILS_SK_PFS + crypto_ecdh_deinit(sta->fils_ecdh); + wpabuf_clear_free(sta->fils_dh_ss); + wpabuf_free(sta->fils_g_sta); +#endif /* CONFIG_FILS_SK_PFS */ +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE + bin_clear_free(sta->owe_pmk, sta->owe_pmk_len); + crypto_ecdh_deinit(sta->owe_ecdh); +#endif /* CONFIG_OWE */ + + os_free(sta->ext_capability); + +#ifdef CONFIG_WNM_AP + eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta); +#endif /* CONFIG_WNM_AP */ + + os_free(sta->ifname_wds); + os_free(sta); } @@ -597,7 +629,7 @@ void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta) static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx) { -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; @@ -608,7 +640,7 @@ static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx) wnm_send_ess_disassoc_imminent(hapd, sta, sta->hs20_session_info_url, sta->hs20_disassoc_timer); -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ } @@ -745,9 +777,17 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; - sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { + /* Skip deauthentication in DMG/IEEE 802.11ad */ + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | + WLAN_STA_ASSOC_REQ_OK); + sta->timeout_next = STA_REMOVE; + } else { + sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK); + sta->timeout_next = STA_DEAUTH; + } ap_sta_set_authorized(hapd, sta, 0); - sta->timeout_next = STA_DEAUTH; wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout " "for " MACSTR " (%d seconds - " "AP_MAX_INACTIVITY_AFTER_DISASSOC)", @@ -783,6 +823,14 @@ static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx) void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, u16 reason) { + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { + /* Deauthentication is not used in DMG/IEEE 802.11ad; + * disassociate the STA instead. */ + ap_sta_disassociate(hapd, sta, reason); + return; + } + wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ; @@ -1229,6 +1277,20 @@ void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, ap_handle_timer, hapd, sta); sta->timeout_next = STA_REMOVE; + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) { + /* Deauthentication is not used in DMG/IEEE 802.11ad; + * disassociate the STA instead. */ + sta->disassoc_reason = reason; + sta->flags |= WLAN_STA_PENDING_DISASSOC_CB; + eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta); + eloop_register_timeout(hapd->iface->drv_flags & + WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ? + 2 : 0, 0, ap_sta_disassoc_cb_timeout, + hapd, sta); + return; + } + sta->deauth_reason = reason; sta->flags |= WLAN_STA_PENDING_DEAUTH_CB; eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta); @@ -1275,6 +1337,15 @@ void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd, "%s: Removed ap_sta_disassoc_cb_timeout timeout for " MACSTR, hapd->conf->iface, MAC2STR(sta->addr)); + if (eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta) > 0) + { + wpa_printf(MSG_DEBUG, + "%s: Removed ap_sta_delayed_1x_auth_fail_cb timeout for " + MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + if (sta->flags & WLAN_STA_WPS) + hostapd_wps_eap_completed(hapd); + } } @@ -1283,7 +1354,7 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen) int res; buf[0] = '\0'; - res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", (flags & WLAN_STA_AUTH ? "[AUTH]" : ""), (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""), (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""), @@ -1300,6 +1371,7 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen) (flags & WLAN_STA_NONERP ? "[NonERP]" : ""), (flags & WLAN_STA_WPS2 ? "[WPS2]" : ""), (flags & WLAN_STA_GAS ? "[GAS]" : ""), + (flags & WLAN_STA_HT ? "[HT]" : ""), (flags & WLAN_STA_VHT ? "[VHT]" : ""), (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""), (flags & WLAN_STA_WNM_SLEEP_MODE ? @@ -1309,3 +1381,48 @@ int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen) return res; } + + +static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + u16 reason; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "IEEE 802.1X: Scheduled disconnection of " MACSTR + " after EAP-Failure", MAC2STR(sta->addr)); + + reason = sta->disconnect_reason_code; + if (!reason) + reason = WLAN_REASON_IEEE_802_1X_AUTH_FAILED; + ap_sta_disconnect(hapd, sta, sta->addr, reason); + if (sta->flags & WLAN_STA_WPS) + hostapd_wps_eap_completed(hapd); +} + + +void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, + struct sta_info *sta) +{ + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "IEEE 802.1X: Force disconnection of " MACSTR + " after EAP-Failure in 10 ms", MAC2STR(sta->addr)); + + /* + * Add a small sleep to increase likelihood of previously requested + * EAP-Failure TX getting out before this should the driver reorder + * operations. + */ + eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta); + eloop_register_timeout(0, 10000, ap_sta_delayed_1x_auth_fail_cb, + hapd, sta); +} + + +int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, + struct sta_info *sta) +{ + return eloop_is_timeout_registered(ap_sta_delayed_1x_auth_fail_cb, + hapd, sta); +} diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 099de62d1a9ae..9cac6f157f68f 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -1,6 +1,6 @@ /* * hostapd / Station table - * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -9,14 +9,11 @@ #ifndef STA_INFO_H #define STA_INFO_H -#ifdef CONFIG_MESH -/* needed for mesh_plink_state enum */ #include "common/defs.h" -#include "common/wpa_common.h" -#endif /* CONFIG_MESH */ - #include "list.h" #include "vlan.h" +#include "common/wpa_common.h" +#include "common/ieee802_11_defs.h" /* STA flags */ #define WLAN_STA_AUTH BIT(0) @@ -38,6 +35,7 @@ #define WLAN_STA_WNM_SLEEP_MODE BIT(19) #define WLAN_STA_VHT_OPMODE_ENABLED BIT(20) #define WLAN_STA_VENDOR_VHT BIT(21) +#define WLAN_STA_PENDING_FILS_ERP BIT(22) #define WLAN_STA_PENDING_DISASSOC_CB BIT(29) #define WLAN_STA_PENDING_DEAUTH_CB BIT(30) #define WLAN_STA_NONERP BIT(31) @@ -46,6 +44,7 @@ * Supported Rates IEs). */ #define WLAN_SUPP_RATES_MAX 32 +struct hostapd_data; struct mbo_non_pref_chan_info { struct mbo_non_pref_chan_info *next; @@ -68,6 +67,7 @@ struct sta_info { be32 ipaddr; struct dl_list ip6addr; /* list head for struct ip6addr */ u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */ + u16 disconnect_reason_code; /* RADIUS server override */ u32 flags; /* Bitfield of WLAN_STA_* */ u16 capability; u16 listen_interval; /* or beacon_int for APs */ @@ -113,6 +113,10 @@ struct sta_info { unsigned int radius_das_match:1; unsigned int ecsa_supported:1; unsigned int added_unassoc:1; + unsigned int pending_wds_enable:1; + unsigned int power_capab:1; + unsigned int agreed_to_steer:1; + unsigned int hs20_t_c_filtering:1; u16 auth_alg; @@ -170,17 +174,20 @@ struct sta_info { struct os_reltime sa_query_start; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_INTERWORKING +#if defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP) #define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */ struct gas_dialog_info *gas_dialog; u8 gas_dialog_next; -#endif /* CONFIG_INTERWORKING */ +#endif /* CONFIG_INTERWORKING || CONFIG_DPP */ struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */ struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */ struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */ + /* Hotspot 2.0 Roaming Consortium from (Re)Association Request */ + struct wpabuf *roaming_consortium; u8 remediation_method; char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */ + char *t_c_url; /* HS 2.0 Terms and Conditions Server URL */ struct wpabuf *hs20_deauth_req; char *hs20_session_info_url; int hs20_disassoc_timer; @@ -195,7 +202,8 @@ struct sta_info { unsigned int mesh_sae_pmksa_caching:1; #endif /* CONFIG_SAE */ - u32 session_timeout; /* valid only if session_timeout_set == 1 */ + /* valid only if session_timeout_set == 1 */ + struct os_reltime session_timeout; /* Last Authentication/(Re)Association Request/Action frame sequence * control */ @@ -214,10 +222,51 @@ struct sta_info { u8 rrm_enabled_capa[5]; + s8 min_tx_power; + s8 max_tx_power; + #ifdef CONFIG_TAXONOMY struct wpabuf *probe_ie_taxonomy; struct wpabuf *assoc_ie_taxonomy; #endif /* CONFIG_TAXONOMY */ + +#ifdef CONFIG_FILS + u8 fils_snonce[FILS_NONCE_LEN]; + u8 fils_session[FILS_SESSION_LEN]; + u8 fils_erp_pmkid[PMKID_LEN]; + u8 *fils_pending_assoc_req; + size_t fils_pending_assoc_req_len; + unsigned int fils_pending_assoc_is_reassoc:1; + unsigned int fils_dhcp_rapid_commit_proxy:1; + unsigned int fils_erp_pmkid_set:1; + unsigned int fils_drv_assoc_finish:1; + struct wpabuf *fils_hlp_resp; + struct wpabuf *hlp_dhcp_discover; + void (*fils_pending_cb)(struct hostapd_data *hapd, struct sta_info *sta, + u16 resp, struct wpabuf *data, int pub); +#ifdef CONFIG_FILS_SK_PFS + struct crypto_ecdh *fils_ecdh; +#endif /* CONFIG_FILS_SK_PFS */ + struct wpabuf *fils_dh_ss; + struct wpabuf *fils_g_sta; +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE + u8 *owe_pmk; + size_t owe_pmk_len; + struct crypto_ecdh *owe_ecdh; + u16 owe_group; +#endif /* CONFIG_OWE */ + + u8 *ext_capability; + char *ifname_wds; /* WDS ifname, if in use */ + +#ifdef CONFIG_TESTING_OPTIONS + enum wpa_alg last_tk_alg; + int last_tk_key_idx; + u8 last_tk[WPA_TK_MAX_LEN]; + size_t last_tk_len; +#endif /* CONFIG_TESTING_OPTIONS */ }; @@ -237,8 +286,6 @@ struct sta_info { #define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5) -struct hostapd_data; - int ap_for_each_sta(struct hostapd_data *hapd, int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, void *ctx), @@ -289,5 +336,9 @@ void ap_sta_clear_disconnect_timeouts(struct hostapd_data *hapd, struct sta_info *sta); int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen); +void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, + struct sta_info *sta); +int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd, + struct sta_info *sta); #endif /* STA_INFO_H */ diff --git a/src/ap/taxonomy.c b/src/ap/taxonomy.c index cea8b726f47af..ae157a7c91d6d 100644 --- a/src/ap/taxonomy.c +++ b/src/ap/taxonomy.c @@ -21,6 +21,7 @@ #include "common/wpa_ctrl.h" #include "hostapd.h" #include "sta_info.h" +#include "taxonomy.h" /* Copy a string with no funny schtuff allowed; only alphanumerics. */ diff --git a/src/ap/tkip_countermeasures.c b/src/ap/tkip_countermeasures.c index 4725e2b3e8f1e..557570cd1bc40 100644 --- a/src/ap/tkip_countermeasures.c +++ b/src/ap/tkip_countermeasures.c @@ -71,6 +71,11 @@ int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local) struct os_reltime now; int ret = 0; + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Michael MIC failure detected in received frame%s", + local ? " (local)" : ""); + if (addr && local) { struct sta_info *sta = ap_get_sta(hapd, addr); if (sta != NULL) { diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c index 31e4fc6b396a1..01fecee026a11 100644 --- a/src/ap/vlan_init.c +++ b/src/ap/vlan_init.c @@ -138,6 +138,8 @@ int vlan_init(struct hostapd_data *hapd) !hapd->conf->vlan) { /* dynamic vlans enabled but no (or empty) vlan_file given */ struct hostapd_vlan *vlan; + int ret; + vlan = os_zalloc(sizeof(*vlan)); if (vlan == NULL) { wpa_printf(MSG_ERROR, "Out of memory while assigning " @@ -146,8 +148,16 @@ int vlan_init(struct hostapd_data *hapd) } vlan->vlan_id = VLAN_ID_WILDCARD; - os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#", - hapd->conf->iface); + ret = os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#", + hapd->conf->iface); + if (ret >= (int) sizeof(vlan->ifname)) { + wpa_printf(MSG_WARNING, + "VLAN: Interface name was truncated to %s", + vlan->ifname); + } else if (ret < 0) { + os_free(vlan); + return ret; + } vlan->next = hapd->conf->vlan; hapd->conf->vlan = vlan; } diff --git a/src/ap/wmm.c b/src/ap/wmm.c index 314e244bc956a..8054c5d2f2433 100644 --- a/src/ap/wmm.c +++ b/src/ap/wmm.c @@ -21,11 +21,6 @@ #include "wmm.h" -/* TODO: maintain separate sequence and fragment numbers for each AC - * TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA - * if only WMM stations are receiving a certain group */ - - static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci) { u8 ret; @@ -157,8 +152,9 @@ static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr, int wmm_process_tspec(struct wmm_tspec_element *tspec) { - int medium_time, pps, duration; - int up, psb, dir, tid; + u64 medium_time; + unsigned int pps, duration; + unsigned int up, psb, dir, tid; u16 val, surplus; up = (tspec->ts_info[1] >> 3) & 0x07; @@ -206,8 +202,9 @@ int wmm_process_tspec(struct wmm_tspec_element *tspec) return WMM_ADDTS_STATUS_INVALID_PARAMETERS; } - medium_time = surplus * pps * duration / 0x2000; - wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time); + medium_time = (u64) surplus * pps * duration / 0x2000; + wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %lu", + (unsigned long) medium_time); /* * TODO: store list of granted (and still active) TSPECs and check diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c index 41d50cebfbe0a..1e8f58b4d07ee 100644 --- a/src/ap/wnm_ap.c +++ b/src/ap/wnm_ap.c @@ -95,8 +95,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, if (mgmt == NULL) { wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " "WNM-Sleep Response action frame"); - os_free(wnmtfs_ie); - return -1; + res = -1; + goto fail; } os_memcpy(mgmt->da, addr, ETH_ALEN); os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); @@ -109,6 +109,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, pos = (u8 *)mgmt->u.action.u.wnm_sleep_resp.variable; /* add key data if MFP is enabled */ if (!wpa_auth_uses_mfp(sta->wpa_sm) || + hapd->conf->wnm_sleep_mode_no_keys || action_type != WNM_SLEEP_MODE_EXIT) { mgmt->u.action.u.wnm_sleep_resp.keydata_len = 0; } else { @@ -118,11 +119,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, (int) gtk_elem_len); #ifdef CONFIG_IEEE80211W res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos); - if (res < 0) { - os_free(wnmtfs_ie); - os_free(mgmt); - return -1; - } + if (res < 0) + goto fail; igtk_elem_len = res; pos += igtk_elem_len; wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d", @@ -176,7 +174,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, wpa_set_wnmsleep(sta->wpa_sm, 0); hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM, addr, NULL, NULL); - if (!wpa_auth_uses_mfp(sta->wpa_sm)) + if (!wpa_auth_uses_mfp(sta->wpa_sm) || + hapd->conf->wnm_sleep_mode_no_keys) wpa_wnmsleep_rekey_gtk(sta->wpa_sm); } } else @@ -184,6 +183,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd, #undef MAX_GTK_SUBELEM_LEN #undef MAX_IGTK_SUBELEM_LEN +fail: os_free(wnmtfs_ie); os_free(mgmt); return res; @@ -202,12 +202,20 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, u8 *tfsreq_ie_end = NULL; u16 tfsreq_ie_len = 0; + if (!hapd->conf->wnm_sleep_mode) { + wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from " + MACSTR " since WNM-Sleep Mode is disabled", + MAC2STR(addr)); + return; + } + dialog_token = *pos++; while (pos + 1 < frm + len) { u8 ie_len = pos[1]; if (pos + 2 + ie_len > frm + len) break; - if (*pos == WLAN_EID_WNMSLEEP) + if (*pos == WLAN_EID_WNMSLEEP && + ie_len >= (int) sizeof(*wnmsleep_ie) - 2) wnmsleep_ie = (struct wnm_sleep_element *) pos; else if (*pos == WLAN_EID_TFS_REQ) { if (!tfsreq_ie_start) @@ -251,20 +259,14 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd, static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd, const u8 *addr, - u8 dialog_token, - const char *url) + u8 dialog_token) { struct ieee80211_mgmt *mgmt; - size_t url_len, len; + size_t len; u8 *pos; int res; - if (url) - url_len = os_strlen(url); - else - url_len = 0; - - mgmt = os_zalloc(sizeof(*mgmt) + (url_len ? 1 + url_len : 0)); + mgmt = os_zalloc(sizeof(*mgmt)); if (mgmt == NULL) return -1; os_memcpy(mgmt->da, addr, ETH_ALEN); @@ -279,11 +281,6 @@ static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd, mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0); mgmt->u.action.u.bss_tm_req.validity_interval = 1; pos = mgmt->u.action.u.bss_tm_req.variable; - if (url) { - *pos++ += url_len; - os_memcpy(pos, url, url_len); - pos += url_len; - } wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to " MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u " @@ -307,6 +304,20 @@ static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd, { u8 dialog_token, reason; const u8 *pos, *end; + int enabled = hapd->conf->bss_transition; + +#ifdef CONFIG_MBO + if (hapd->conf->mbo_enabled) + enabled = 1; +#endif /* CONFIG_MBO */ + if (!enabled) { + wpa_printf(MSG_DEBUG, + "Ignore BSS Transition Management Query from " + MACSTR + " since BSS Transition Management is disabled", + MAC2STR(addr)); + return; + } if (len < 2) { wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from " @@ -326,7 +337,20 @@ static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd, wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries", pos, end - pos); - ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token, NULL); + ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token); +} + + +void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + + if (sta->agreed_to_steer) { + wpa_printf(MSG_DEBUG, "%s: Reset steering flag for STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + sta->agreed_to_steer = 0; + } } @@ -336,6 +360,21 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd, { u8 dialog_token, status_code, bss_termination_delay; const u8 *pos, *end; + int enabled = hapd->conf->bss_transition; + struct sta_info *sta; + +#ifdef CONFIG_MBO + if (hapd->conf->mbo_enabled) + enabled = 1; +#endif /* CONFIG_MBO */ + if (!enabled) { + wpa_printf(MSG_DEBUG, + "Ignore BSS Transition Management Response from " + MACSTR + " since BSS Transition Management is disabled", + MAC2STR(addr)); + return; + } if (len < 3) { wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from " @@ -354,11 +393,23 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd, "bss_termination_delay=%u", MAC2STR(addr), dialog_token, status_code, bss_termination_delay); + sta = ap_get_sta(hapd, addr); + if (!sta) { + wpa_printf(MSG_DEBUG, "Station " MACSTR + " not found for received BSS TM Response", + MAC2STR(addr)); + return; + } + if (status_code == WNM_BSS_TM_ACCEPT) { if (end - pos < ETH_ALEN) { wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field"); return; } + sta->agreed_to_steer = 1; + eloop_cancel_timeout(ap_sta_reset_steer_flag_timer, hapd, sta); + eloop_register_timeout(2, 0, ap_sta_reset_steer_flag_timer, + hapd, sta); wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR, MAC2STR(pos)); wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR @@ -368,6 +419,7 @@ static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd, MAC2STR(pos)); pos += ETH_ALEN; } else { + sta->agreed_to_steer = 0; wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR " status_code=%u bss_termination_delay=%u", MAC2STR(addr), status_code, bss_termination_delay); @@ -401,6 +453,48 @@ static void ieee802_11_rx_wnm_notification_req(struct hostapd_data *hapd, } +static void ieee802_11_rx_wnm_coloc_intf_report(struct hostapd_data *hapd, + const u8 *addr, const u8 *buf, + size_t len) +{ + u8 dialog_token; + char *hex; + size_t hex_len; + + if (!hapd->conf->coloc_intf_reporting) { + wpa_printf(MSG_DEBUG, + "WNM: Ignore unexpected Collocated Interference Report from " + MACSTR, MAC2STR(addr)); + return; + } + + if (len < 1) { + wpa_printf(MSG_DEBUG, + "WNM: Ignore too short Collocated Interference Report from " + MACSTR, MAC2STR(addr)); + return; + } + dialog_token = *buf++; + len--; + + wpa_printf(MSG_DEBUG, + "WNM: Received Collocated Interference Report frame from " + MACSTR " (dialog_token=%u)", + MAC2STR(addr), dialog_token); + wpa_hexdump(MSG_MSGDUMP, "WNM: Collocated Interference Report Elements", + buf, len); + + hex_len = 2 * len + 1; + hex = os_malloc(hex_len); + if (!hex) + return; + wpa_snprintf_hex(hex, hex_len, buf, len); + wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, COLOC_INTF_REPORT MACSTR " %d %s", + MAC2STR(addr), dialog_token, hex); + os_free(hex); +} + + int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, const struct ieee80211_mgmt *mgmt, size_t len) { @@ -431,6 +525,10 @@ int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd, ieee802_11_rx_wnm_notification_req(hapd, mgmt->sa, payload, plen); return 0; + case WNM_COLLOCATED_INTERFERENCE_REPORT: + ieee802_11_rx_wnm_coloc_intf_report(hapd, mgmt->sa, payload, + plen); + return 0; } wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR, @@ -629,3 +727,40 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, return 0; } + + +int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta, + unsigned int auto_report, unsigned int timeout) +{ + u8 buf[100], *pos; + struct ieee80211_mgmt *mgmt; + u8 dialog_token = 1; + + if (auto_report > 3 || timeout > 63) + return -1; + os_memset(buf, 0, sizeof(buf)); + mgmt = (struct ieee80211_mgmt *) buf; + mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt->da, sta->addr, ETH_ALEN); + os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); + mgmt->u.action.category = WLAN_ACTION_WNM; + mgmt->u.action.u.coloc_intf_req.action = + WNM_COLLOCATED_INTERFERENCE_REQ; + mgmt->u.action.u.coloc_intf_req.dialog_token = dialog_token; + mgmt->u.action.u.coloc_intf_req.req_info = auto_report | (timeout << 2); + pos = &mgmt->u.action.u.coloc_intf_req.req_info; + pos++; + + wpa_printf(MSG_DEBUG, "WNM: Sending Collocated Interference Request to " + MACSTR " (dialog_token=%u auto_report=%u timeout=%u)", + MAC2STR(sta->addr), dialog_token, auto_report, timeout); + if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) { + wpa_printf(MSG_DEBUG, + "WNM: Failed to send Collocated Interference Request frame"); + return -1; + } + + return 0; +} diff --git a/src/ap/wnm_ap.h b/src/ap/wnm_ap.h index a44eadb85e555..1806ba0e0525b 100644 --- a/src/ap/wnm_ap.h +++ b/src/ap/wnm_ap.h @@ -23,5 +23,8 @@ int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta, const u8 *bss_term_dur, const char *url, const u8 *nei_rep, size_t nei_rep_len, const u8 *mbo_attrs, size_t mbo_len); +void ap_sta_reset_steer_flag_timer(void *eloop_ctx, void *timeout_ctx); +int wnm_send_coloc_intf_req(struct hostapd_data *hapd, struct sta_info *sta, + unsigned int auto_report, unsigned int timeout); #endif /* WNM_AP_H */ diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index bf10cc1646f7d..34969e79e46ff 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -1,6 +1,6 @@ /* * IEEE 802.11 RSN / WPA Authenticator - * Copyright (c) 2004-2015, 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. @@ -13,10 +13,13 @@ #include "utils/state_machine.h" #include "utils/bitfield.h" #include "common/ieee802_11_defs.h" +#include "crypto/aes.h" #include "crypto/aes_wrap.h" +#include "crypto/aes_siv.h" #include "crypto/crypto.h" #include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/sha384.h" #include "crypto/random.h" #include "eapol_auth/eapol_auth_sm.h" #include "ap_config.h" @@ -33,8 +36,14 @@ static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); static int wpa_sm_step(struct wpa_state_machine *sm); -static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data, - size_t data_len); +static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK, + u8 *data, size_t data_len); +#ifdef CONFIG_FILS +static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk, + u8 *buf, size_t buf_len, u16 *_key_data_len); +static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm, + const struct wpabuf *hlp); +#endif /* CONFIG_FILS */ static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx); static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, struct wpa_group *group); @@ -52,12 +61,12 @@ static void wpa_group_get(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static void wpa_group_put(struct wpa_authenticator *wpa_auth, struct wpa_group *group); +static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos); -static const u32 dot11RSNAConfigGroupUpdateCount = 4; -static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; static const u32 eapol_key_timeout_first = 100; /* ms */ static const u32 eapol_key_timeout_subseq = 1000; /* ms */ static const u32 eapol_key_timeout_first_group = 500; /* ms */ +static const u32 eapol_key_timeout_no_retrans = 4000; /* ms */ /* TODO: make these configurable */ static const int dot11RSNAConfigPMKLifetime = 43200; @@ -68,8 +77,8 @@ static const int dot11RSNAConfigSATimeout = 60; static inline int wpa_auth_mic_failure_report( struct wpa_authenticator *wpa_auth, const u8 *addr) { - if (wpa_auth->cb.mic_failure_report) - return wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr); + if (wpa_auth->cb->mic_failure_report) + return wpa_auth->cb->mic_failure_report(wpa_auth->cb_ctx, addr); return 0; } @@ -77,8 +86,8 @@ static inline int wpa_auth_mic_failure_report( static inline void wpa_auth_psk_failure_report( struct wpa_authenticator *wpa_auth, const u8 *addr) { - if (wpa_auth->cb.psk_failure_report) - wpa_auth->cb.psk_failure_report(wpa_auth->cb.ctx, addr); + if (wpa_auth->cb->psk_failure_report) + wpa_auth->cb->psk_failure_report(wpa_auth->cb_ctx, addr); } @@ -86,38 +95,38 @@ static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, wpa_eapol_variable var, int value) { - if (wpa_auth->cb.set_eapol) - wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value); + if (wpa_auth->cb->set_eapol) + wpa_auth->cb->set_eapol(wpa_auth->cb_ctx, addr, var, value); } static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, wpa_eapol_variable var) { - if (wpa_auth->cb.get_eapol == NULL) + if (wpa_auth->cb->get_eapol == NULL) return -1; - return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var); + return wpa_auth->cb->get_eapol(wpa_auth->cb_ctx, addr, var); } static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *p2p_dev_addr, - const u8 *prev_psk) + const u8 *prev_psk, size_t *psk_len) { - if (wpa_auth->cb.get_psk == NULL) + if (wpa_auth->cb->get_psk == NULL) return NULL; - return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, p2p_dev_addr, - prev_psk); + return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr, + prev_psk, psk_len); } static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth, const u8 *addr, u8 *msk, size_t *len) { - if (wpa_auth->cb.get_msk == NULL) + if (wpa_auth->cb->get_msk == NULL) return -1; - return wpa_auth->cb.get_msk(wpa_auth->cb.ctx, addr, msk, len); + return wpa_auth->cb->get_msk(wpa_auth->cb_ctx, addr, msk, len); } @@ -126,19 +135,19 @@ static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len) { - if (wpa_auth->cb.set_key == NULL) + if (wpa_auth->cb->set_key == NULL) return -1; - return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, - key, key_len); + return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx, + key, key_len); } static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, const u8 *addr, int idx, u8 *seq) { - if (wpa_auth->cb.get_seqnum == NULL) + if (wpa_auth->cb->get_seqnum == NULL) return -1; - return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); + return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq); } @@ -146,10 +155,10 @@ static inline int wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *data, size_t data_len, int encrypt) { - if (wpa_auth->cb.send_eapol == NULL) + if (wpa_auth->cb->send_eapol == NULL) return -1; - return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len, - encrypt); + return wpa_auth->cb->send_eapol(wpa_auth->cb_ctx, addr, data, data_len, + encrypt); } @@ -157,9 +166,9 @@ wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth, const u8 *addr) { - if (wpa_auth->cb.start_ampe == NULL) + if (wpa_auth->cb->start_ampe == NULL) return -1; - return wpa_auth->cb.start_ampe(wpa_auth->cb.ctx, addr); + return wpa_auth->cb->start_ampe(wpa_auth->cb_ctx, addr); } #endif /* CONFIG_MESH */ @@ -168,9 +177,9 @@ int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, int (*cb)(struct wpa_state_machine *sm, void *ctx), void *cb_ctx) { - if (wpa_auth->cb.for_each_sta == NULL) + if (wpa_auth->cb->for_each_sta == NULL) return 0; - return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx); + return wpa_auth->cb->for_each_sta(wpa_auth->cb_ctx, cb, cb_ctx); } @@ -178,18 +187,18 @@ int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, int (*cb)(struct wpa_authenticator *a, void *ctx), void *cb_ctx) { - if (wpa_auth->cb.for_each_auth == NULL) + if (wpa_auth->cb->for_each_auth == NULL) return 0; - return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx); + return wpa_auth->cb->for_each_auth(wpa_auth->cb_ctx, cb, cb_ctx); } void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, logger_level level, const char *txt) { - if (wpa_auth->cb.logger == NULL) + if (wpa_auth->cb->logger == NULL) return; - wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt); + wpa_auth->cb->logger(wpa_auth->cb_ctx, addr, level, txt); } @@ -200,7 +209,7 @@ void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr, int maxlen; va_list ap; - if (wpa_auth->cb.logger == NULL) + if (wpa_auth->cb->logger == NULL) return; maxlen = os_strlen(fmt) + 100; @@ -219,30 +228,13 @@ void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr, static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, - const u8 *addr) + const u8 *addr, u16 reason) { - if (wpa_auth->cb.disconnect == NULL) + if (wpa_auth->cb->disconnect == NULL) return; - wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR, MAC2STR(addr)); - wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr, - WLAN_REASON_PREV_AUTH_NOT_VALID); -} - - -static int wpa_use_aes_cmac(struct wpa_state_machine *sm) -{ - int ret = 0; -#ifdef CONFIG_IEEE80211R - if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) - ret = 1; -#endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_IEEE80211W - if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt)) - ret = 1; -#endif /* CONFIG_IEEE80211W */ - if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) - ret = 1; - return ret; + wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR " (reason %u)", + MAC2STR(addr), reason); + wpa_auth->cb->disconnect(wpa_auth->cb_ctx, addr, reason); } @@ -409,7 +401,8 @@ static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, */ struct wpa_authenticator * wpa_init(const u8 *addr, struct wpa_auth_config *conf, - struct wpa_auth_callbacks *cb) + const struct wpa_auth_callbacks *cb, + void *cb_ctx) { struct wpa_authenticator *wpa_auth; @@ -418,7 +411,8 @@ struct wpa_authenticator * wpa_init(const u8 *addr, return NULL; os_memcpy(wpa_auth->addr, addr, ETH_ALEN); os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); - os_memcpy(&wpa_auth->cb, cb, sizeof(*cb)); + wpa_auth->cb = cb; + wpa_auth->cb_ctx = cb_ctx; if (wpa_auth_gen_wpa_ie(wpa_auth)) { wpa_printf(MSG_ERROR, "Could not generate WPA IE."); @@ -443,7 +437,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr, return NULL; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init(); if (wpa_auth->ft_pmk_cache == NULL) { wpa_printf(MSG_ERROR, "FT PMK cache initialization failed."); @@ -453,7 +447,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr, os_free(wpa_auth); return NULL; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if (wpa_auth->conf.wpa_gmk_rekey) { eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, @@ -506,17 +500,13 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth) eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL); eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); -#ifdef CONFIG_PEERKEY - while (wpa_auth->stsl_negotiations) - wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations); -#endif /* CONFIG_PEERKEY */ - pmksa_cache_auth_deinit(wpa_auth->pmksa); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache); wpa_auth->ft_pmk_cache = NULL; -#endif /* CONFIG_IEEE80211R */ + wpa_ft_deinit(wpa_auth); +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_P2P bitfield_free(wpa_auth->ip_pool); @@ -599,16 +589,28 @@ int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) return -1; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (sm->ft_completed) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "FT authentication already completed - do not " "start 4-way handshake"); /* Go to PTKINITDONE state to allow GTK rekeying */ sm->wpa_ptk_state = WPA_PTK_PTKINITDONE; + sm->Pair = TRUE; + return 0; + } +#endif /* CONFIG_IEEE80211R_AP */ + +#ifdef CONFIG_FILS + if (sm->fils_completed) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "FILS authentication already completed - do not start 4-way handshake"); + /* Go to PTKINITDONE state to allow GTK rekeying */ + sm->wpa_ptk_state = WPA_PTK_PTKINITDONE; + sm->Pair = TRUE; return 0; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_FILS */ if (sm->started) { os_memset(&sm->key_replay, 0, sizeof(sm->key_replay)); @@ -660,10 +662,10 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm) sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP os_free(sm->assoc_resp_ftie); wpabuf_free(sm->ft_pending_req_ies); -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ os_free(sm->last_rx_eapol_key); os_free(sm->wpa_ie); wpa_group_put(sm->wpa_auth, sm->group); @@ -680,15 +682,19 @@ void wpa_auth_sta_deinit(struct wpa_state_machine *sm) wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "strict rekeying - force GTK rekey since STA " "is leaving"); - eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL); - eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth, - NULL); + if (eloop_deplete_timeout(0, 500000, wpa_rekey_gtk, + sm->wpa_auth, NULL) == -1) + eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth, + NULL); } eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_sm_call_step, sm, NULL); eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); +#ifdef CONFIG_IEEE80211R_AP + wpa_ft_sta_deinit(sm); +#endif /* CONFIG_IEEE80211R_AP */ if (sm->in_step_loop) { /* Must not free state machine while wpa_sm_step() is running. * Freeing will be completed in the end of wpa_sm_step(). */ @@ -739,7 +745,7 @@ static void wpa_replay_counter_mark_invalid(struct wpa_key_replay_counter *ctr, } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_ie_parse *kde) @@ -786,7 +792,7 @@ static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth, return 0; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth, @@ -828,29 +834,38 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data, struct wpa_ptk PTK; int ok = 0; const u8 *pmk = NULL; - unsigned int pmk_len; + size_t pmk_len; + os_memset(&PTK, 0, sizeof(PTK)); for (;;) { - if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && + !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) { pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, - sm->p2p_dev_addr, pmk); + sm->p2p_dev_addr, pmk, &pmk_len); if (pmk == NULL) break; - pmk_len = PMK_LEN; +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) { + os_memcpy(sm->xxkey, pmk, pmk_len); + sm->xxkey_len = pmk_len; + } +#endif /* CONFIG_IEEE80211R_AP */ } else { pmk = sm->PMK; pmk_len = sm->pmk_len; } - wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK); + if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK) < 0) + break; - if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len) - == 0) { + if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK, + data, data_len) == 0) { ok = 1; break; } - if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) + if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || + wpa_key_mgmt_sae(sm->wpa_key_mgmt)) break; } @@ -877,39 +892,41 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; - struct wpa_eapol_key_192 *key192; u16 key_info, key_data_length; - enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST, - SMK_M1, SMK_M3, SMK_ERROR } msg; + enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg; char *msgtxt; struct wpa_eapol_ie_parse kde; - int ft; - const u8 *eapol_key_ie, *key_data; - size_t eapol_key_ie_len, keyhdrlen, mic_len; + const u8 *key_data; + size_t keyhdrlen, mic_len; + u8 *mic; if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) return; + wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len); - mic_len = wpa_mic_len(sm->wpa_key_mgmt); - keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key); + mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); + keyhdrlen = sizeof(*key) + mic_len + 2; - if (data_len < sizeof(*hdr) + keyhdrlen) + if (data_len < sizeof(*hdr) + keyhdrlen) { + wpa_printf(MSG_DEBUG, "WPA: Ignore too short EAPOL-Key frame"); return; + } hdr = (struct ieee802_1x_hdr *) data; key = (struct wpa_eapol_key *) (hdr + 1); - key192 = (struct wpa_eapol_key_192 *) (hdr + 1); + mic = (u8 *) (key + 1); key_info = WPA_GET_BE16(key->key_info); - if (mic_len == 24) { - key_data = (const u8 *) (key192 + 1); - key_data_length = WPA_GET_BE16(key192->key_data_length); - } else { - key_data = (const u8 *) (key + 1); - key_data_length = WPA_GET_BE16(key->key_data_length); - } + key_data = mic + mic_len + 2; + key_data_length = WPA_GET_BE16(mic + mic_len); wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR - " key_info=0x%x type=%u key_data_length=%u", - MAC2STR(sm->addr), key_info, key->type, key_data_length); + " key_info=0x%x type=%u mic_len=%u key_data_length=%u", + MAC2STR(sm->addr), key_info, key->type, + (unsigned int) mic_len, key_data_length); + wpa_hexdump(MSG_MSGDUMP, + "WPA: EAPOL-Key header (ending before Key MIC)", + key, sizeof(*key)); + wpa_hexdump(MSG_MSGDUMP, "WPA: EAPOL-Key Key MIC", + mic, mic_len); if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) { wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - " "key_data overflow (%d > %lu)", @@ -950,25 +967,20 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys * are set */ - if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) == - (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) { - if (key_info & WPA_KEY_INFO_ERROR) { - msg = SMK_ERROR; - msgtxt = "SMK Error"; - } else { - msg = SMK_M1; - msgtxt = "SMK M1"; - } - } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { - msg = SMK_M3; - msgtxt = "SMK M3"; - } else if (key_info & WPA_KEY_INFO_REQUEST) { + if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { + wpa_printf(MSG_DEBUG, "WPA: Ignore SMK message"); + return; + } + + if (key_info & WPA_KEY_INFO_REQUEST) { msg = REQUEST; msgtxt = "Request"; } else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) { msg = GROUP_2; msgtxt = "2/2 Group"; - } else if (key_data_length == 0) { + } else if (key_data_length == 0 || + (mic_len == 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && + key_data_length == AES_BLOCK_SIZE)) { msg = PAIRWISE_4; msgtxt = "4/4 Pairwise"; } else { @@ -976,15 +988,13 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, msgtxt = "2/4 Pairwise"; } - /* TODO: key_info type validation for PeerKey */ if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 || msg == GROUP_2) { u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK; if (sm->pairwise == WPA_CIPHER_CCMP || sm->pairwise == WPA_CIPHER_GCMP) { - if (wpa_use_aes_cmac(sm) && - sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN && - !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) && + if (wpa_use_cmac(sm->wpa_key_mgmt) && + !wpa_use_akm_defined(sm->wpa_key_mgmt) && ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, @@ -994,7 +1004,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, return; } - if (!wpa_use_aes_cmac(sm) && + if (!wpa_use_cmac(sm->wpa_key_mgmt) && + !wpa_use_akm_defined(sm->wpa_key_mgmt) && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, @@ -1004,7 +1015,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } } - if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) && + if (wpa_use_akm_defined(sm->wpa_key_mgmt) && ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases"); @@ -1092,6 +1103,15 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, } continue_processing: +#ifdef CONFIG_FILS + if (sm->wpa == WPA_VERSION_WPA2 && mic_len == 0 && + !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "WPA: Encr Key Data bit not set even though AEAD cipher is supposed to be used - drop frame"); + return; + } +#endif /* CONFIG_FILS */ + switch (msg) { case PAIRWISE_2: if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && @@ -1119,70 +1139,10 @@ continue_processing: "collect more entropy for random number " "generation"); random_mark_pool_ready(); - wpa_sta_disconnect(wpa_auth, sm->addr); - return; - } - if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) { - wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key msg 2/4 with " - "invalid Key Data contents"); - return; - } - if (kde.rsn_ie) { - eapol_key_ie = kde.rsn_ie; - eapol_key_ie_len = kde.rsn_ie_len; - } else if (kde.osen) { - eapol_key_ie = kde.osen; - eapol_key_ie_len = kde.osen_len; - } else { - eapol_key_ie = kde.wpa_ie; - eapol_key_ie_len = kde.wpa_ie_len; - } - ft = sm->wpa == WPA_VERSION_WPA2 && - wpa_key_mgmt_ft(sm->wpa_key_mgmt); - if (sm->wpa_ie == NULL || - wpa_compare_rsn_ie(ft, - sm->wpa_ie, sm->wpa_ie_len, - eapol_key_ie, eapol_key_ie_len)) { - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, - "WPA IE from (Re)AssocReq did not " - "match with msg 2/4"); - if (sm->wpa_ie) { - wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq", - sm->wpa_ie, sm->wpa_ie_len); - } - wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4", - eapol_key_ie, eapol_key_ie_len); - /* MLME-DEAUTHENTICATE.request */ - wpa_sta_disconnect(wpa_auth, sm->addr); + wpa_sta_disconnect(wpa_auth, sm->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); return; } -#ifdef CONFIG_IEEE80211R - if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) { - wpa_sta_disconnect(wpa_auth, sm->addr); - return; - } -#endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_P2P - if (kde.ip_addr_req && kde.ip_addr_req[0] && - wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) { - int idx; - wpa_printf(MSG_DEBUG, "P2P: IP address requested in " - "EAPOL-Key exchange"); - idx = bitfield_get_first_zero(wpa_auth->ip_pool); - if (idx >= 0) { - u32 start = WPA_GET_BE32(wpa_auth->conf. - ip_addr_start); - bitfield_set(wpa_auth->ip_pool, idx); - WPA_PUT_BE32(sm->ip_addr, start + idx); - wpa_printf(MSG_DEBUG, "P2P: Assigned IP " - "address %u.%u.%u.%u to " MACSTR, - sm->ip_addr[0], sm->ip_addr[1], - sm->ip_addr[2], sm->ip_addr[3], - MAC2STR(sm->addr)); - } - } -#endif /* CONFIG_P2P */ break; case PAIRWISE_4: if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING || @@ -1204,28 +1164,6 @@ continue_processing: return; } break; -#ifdef CONFIG_PEERKEY - case SMK_M1: - case SMK_M3: - case SMK_ERROR: - if (!wpa_auth->conf.peerkey) { - wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but " - "PeerKey use disabled - ignoring message"); - return; - } - if (!sm->PTK_valid) { - wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, - "received EAPOL-Key msg SMK in " - "invalid state - dropped"); - return; - } - break; -#else /* CONFIG_PEERKEY */ - case SMK_M1: - case SMK_M3: - case SMK_ERROR: - return; /* STSL disabled - ignore SMK messages */ -#endif /* CONFIG_PEERKEY */ case REQUEST: break; } @@ -1239,22 +1177,42 @@ continue_processing: return; } - if (!(key_info & WPA_KEY_INFO_MIC)) { + if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt) && + !(key_info & WPA_KEY_INFO_MIC)) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received invalid EAPOL-Key: Key MIC not set"); return; } +#ifdef CONFIG_FILS + if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) && + (key_info & WPA_KEY_INFO_MIC)) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received invalid EAPOL-Key: Key MIC set"); + return; + } +#endif /* CONFIG_FILS */ + sm->MICVerified = FALSE; if (sm->PTK_valid && !sm->update_snonce) { - if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data, - data_len) && + if (mic_len && + wpa_verify_key_mic(sm->wpa_key_mgmt, sm->pmk_len, &sm->PTK, + data, data_len) && (msg != PAIRWISE_4 || !sm->alt_snonce_valid || wpa_try_alt_snonce(sm, data, data_len))) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, "received EAPOL-Key with invalid MIC"); return; } +#ifdef CONFIG_FILS + if (!mic_len && + wpa_aead_decrypt(sm, &sm->PTK, data, data_len, + &key_data_length) < 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key with invalid MIC"); + return; + } +#endif /* CONFIG_FILS */ sm->MICVerified = TRUE; eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); sm->pending_1_of_4_timeout = 0; @@ -1277,12 +1235,7 @@ continue_processing: * even though MAC address KDE is not normally encrypted, * supplicant is allowed to encrypt it. */ - if (msg == SMK_ERROR) { -#ifdef CONFIG_PEERKEY - wpa_smk_error(wpa_auth, sm, key_data, key_data_length); -#endif /* CONFIG_PEERKEY */ - return; - } else if (key_info & WPA_KEY_INFO_ERROR) { + if (key_info & WPA_KEY_INFO_ERROR) { if (wpa_receive_error_report( wpa_auth, sm, !(key_info & WPA_KEY_INFO_KEY_TYPE)) > 0) @@ -1292,11 +1245,6 @@ continue_processing: "received EAPOL-Key Request for new " "4-Way Handshake"); wpa_request_new_ptk(sm); -#ifdef CONFIG_PEERKEY - } else if (msg == SMK_M1) { - wpa_smk_m1(wpa_auth, sm, key, key_data, - key_data_length); -#endif /* CONFIG_PEERKEY */ } else if (key_data_length > 0 && wpa_parse_kde_ies(key_data, key_data_length, &kde) == 0 && @@ -1335,18 +1283,10 @@ continue_processing: wpa_replay_counter_mark_invalid(sm->key_replay, NULL); } -#ifdef CONFIG_PEERKEY - if (msg == SMK_M3) { - wpa_smk_m3(wpa_auth, sm, key, key_data, key_data_length); - return; - } -#endif /* CONFIG_PEERKEY */ - os_free(sm->last_rx_eapol_key); - sm->last_rx_eapol_key = os_malloc(data_len); + sm->last_rx_eapol_key = os_memdup(data, data_len); if (sm->last_rx_eapol_key == NULL) return; - os_memcpy(sm->last_rx_eapol_key, data, data_len); sm->last_rx_eapol_key_len = data_len; sm->rx_eapol_key_secure = !!(key_info & WPA_KEY_INFO_SECURE); @@ -1361,7 +1301,7 @@ continue_processing: static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr, const u8 *gnonce, u8 *gtk, size_t gtk_len) { - u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16]; + u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + WPA_GTK_MAX_LEN]; u8 *pos; int ret = 0; @@ -1372,21 +1312,30 @@ static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr, * is done only at the Authenticator and as such, does not need to be * exactly same. */ + os_memset(data, 0, sizeof(data)); os_memcpy(data, addr, ETH_ALEN); os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN); pos = data + ETH_ALEN + WPA_NONCE_LEN; wpa_get_ntp_timestamp(pos); pos += 8; - if (random_get_bytes(pos, 16) < 0) + if (random_get_bytes(pos, gtk_len) < 0) ret = -1; -#ifdef CONFIG_IEEE80211W - sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len); -#else /* CONFIG_IEEE80211W */ - if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len) - < 0) +#ifdef CONFIG_SHA384 + if (sha384_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), + gtk, gtk_len) < 0) ret = -1; -#endif /* CONFIG_IEEE80211W */ +#else /* CONFIG_SHA384 */ +#ifdef CONFIG_SHA256 + if (sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), + gtk, gtk_len) < 0) + ret = -1; +#else /* CONFIG_SHA256 */ + if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), + gtk, gtk_len) < 0) + ret = -1; +#endif /* CONFIG_SHA256 */ +#endif /* CONFIG_SHA384 */ return ret; } @@ -1412,26 +1361,24 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; - struct wpa_eapol_key_192 *key192; size_t len, mic_len, keyhdrlen; int alg; int key_data_len, pad_len = 0; u8 *buf, *pos; int version, pairwise; int i; - u8 *key_data; + u8 *key_mic, *key_data; - mic_len = wpa_mic_len(sm->wpa_key_mgmt); - keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key); + mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); + keyhdrlen = sizeof(*key) + mic_len + 2; len = sizeof(struct ieee802_1x_hdr) + keyhdrlen; if (force_version) version = force_version; - else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || - wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) + else if (wpa_use_akm_defined(sm->wpa_key_mgmt)) version = WPA_KEY_INFO_TYPE_AKM_DEFINED; - else if (wpa_use_aes_cmac(sm)) + else if (wpa_use_cmac(sm->wpa_key_mgmt)) version = WPA_KEY_INFO_TYPE_AES_128_CMAC; else if (sm->pairwise != WPA_CIPHER_TKIP) version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; @@ -1453,8 +1400,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, key_data_len = kde_len; if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || - sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || - wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || + wpa_use_aes_key_wrap(sm->wpa_key_mgmt) || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) { pad_len = key_data_len % 8; if (pad_len) @@ -1463,6 +1409,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, } len += key_data_len; + if (!mic_len && encr) + len += AES_BLOCK_SIZE; hdr = os_zalloc(len); if (hdr == NULL) @@ -1471,7 +1419,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; hdr->length = host_to_be16(len - sizeof(*hdr)); key = (struct wpa_eapol_key *) (hdr + 1); - key192 = (struct wpa_eapol_key_192 *) (hdr + 1); + key_mic = (u8 *) (key + 1); key_data = ((u8 *) (hdr + 1)) + keyhdrlen; key->type = sm->wpa == WPA_VERSION_WPA2 ? @@ -1484,11 +1432,11 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, WPA_PUT_BE16(key->key_info, key_info); alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group; - WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg)); - if (key_info & WPA_KEY_INFO_SMK_MESSAGE) + if (sm->wpa == WPA_VERSION_WPA2 && !pairwise) WPA_PUT_BE16(key->key_length, 0); + else + WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg)); - /* FIX: STSL: what to use as key_replay_counter? */ for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) { sm->key_replay[i].valid = sm->key_replay[i - 1].valid; os_memcpy(sm->key_replay[i].counter, @@ -1510,10 +1458,31 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, if (kde && !encr) { os_memcpy(key_data, kde, kde_len); - if (mic_len == 24) - WPA_PUT_BE16(key192->key_data_length, kde_len); - else - WPA_PUT_BE16(key->key_data_length, kde_len); + WPA_PUT_BE16(key_mic + mic_len, kde_len); +#ifdef CONFIG_FILS + } else if (!mic_len && kde) { + const u8 *aad[1]; + size_t aad_len[1]; + + WPA_PUT_BE16(key_mic, AES_BLOCK_SIZE + kde_len); + wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", + kde, kde_len); + + wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", + sm->PTK.kek, sm->PTK.kek_len); + /* AES-SIV AAD from EAPOL protocol version field (inclusive) to + * to Key Data (exclusive). */ + aad[0] = (u8 *) hdr; + aad_len[0] = key_mic + 2 - (u8 *) hdr; + if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, kde, kde_len, + 1, aad, aad_len, key_mic + 2) < 0) { + wpa_printf(MSG_DEBUG, "WPA: AES-SIV encryption failed"); + return; + } + + wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV", + key_mic + 2, AES_BLOCK_SIZE + kde_len); +#endif /* CONFIG_FILS */ } else if (encr && kde) { buf = os_zalloc(key_data_len); if (buf == NULL) { @@ -1530,24 +1499,24 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", buf, key_data_len); if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || - sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN || - wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) || + wpa_use_aes_key_wrap(sm->wpa_key_mgmt) || version == WPA_KEY_INFO_TYPE_AES_128_CMAC) { + wpa_printf(MSG_DEBUG, + "WPA: Encrypt Key Data using AES-WRAP (KEK length %u)", + (unsigned int) sm->PTK.kek_len); if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, (key_data_len - 8) / 8, buf, key_data)) { os_free(hdr); os_free(buf); return; } - if (mic_len == 24) - WPA_PUT_BE16(key192->key_data_length, - key_data_len); - else - WPA_PUT_BE16(key->key_data_length, - key_data_len); + WPA_PUT_BE16(key_mic + mic_len, key_data_len); #ifndef CONFIG_NO_RC4 } else if (sm->PTK.kek_len == 16) { u8 ek[32]; + + wpa_printf(MSG_DEBUG, + "WPA: Encrypt Key Data using RC4"); os_memcpy(key->key_iv, sm->group->Counter + WPA_NONCE_LEN - 16, 16); inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); @@ -1555,12 +1524,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len); os_memcpy(key_data, buf, key_data_len); rc4_skip(ek, 32, 256, key_data, key_data_len); - if (mic_len == 24) - WPA_PUT_BE16(key192->key_data_length, - key_data_len); - else - WPA_PUT_BE16(key->key_data_length, - key_data_len); + WPA_PUT_BE16(key_mic + mic_len, key_data_len); #endif /* CONFIG_NO_RC4 */ } else { os_free(hdr); @@ -1571,9 +1535,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, } if (key_info & WPA_KEY_INFO_MIC) { - u8 *key_mic; - - if (!sm->PTK_valid) { + if (!sm->PTK_valid || !mic_len) { wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "PTK not valid when sending EAPOL-Key " "frame"); @@ -1581,10 +1543,12 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, return; } - key_mic = key192->key_mic; /* same offset for key and key192 */ - wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len, - sm->wpa_key_mgmt, version, - (u8 *) hdr, len, key_mic); + if (wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len, + sm->wpa_key_mgmt, version, + (u8 *) hdr, len, key_mic) < 0) { + os_free(hdr); + return; + } #ifdef CONFIG_TESTING_OPTIONS if (!pairwise && wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 && @@ -1613,7 +1577,7 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, { int timeout_ms; int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; - int ctr; + u32 ctr; if (sm == NULL) return; @@ -1627,41 +1591,43 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, eapol_key_timeout_first_group; else timeout_ms = eapol_key_timeout_subseq; + if (wpa_auth->conf.wpa_disable_eapol_key_retries && + (!pairwise || (key_info & WPA_KEY_INFO_MIC))) + timeout_ms = eapol_key_timeout_no_retrans; if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC)) sm->pending_1_of_4_timeout = 1; wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry " - "counter %d)", timeout_ms, ctr); + "counter %u)", timeout_ms, ctr); eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, wpa_send_eapol_timeout, wpa_auth, sm); } -static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data, - size_t data_len) +static int wpa_verify_key_mic(int akmp, size_t pmk_len, struct wpa_ptk *PTK, + u8 *data, size_t data_len) { struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; - struct wpa_eapol_key_192 *key192; u16 key_info; int ret = 0; - u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; - size_t mic_len = wpa_mic_len(akmp); + u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos; + size_t mic_len = wpa_mic_len(akmp, pmk_len); if (data_len < sizeof(*hdr) + sizeof(*key)) return -1; hdr = (struct ieee802_1x_hdr *) data; key = (struct wpa_eapol_key *) (hdr + 1); - key192 = (struct wpa_eapol_key_192 *) (hdr + 1); + mic_pos = (u8 *) (key + 1); key_info = WPA_GET_BE16(key->key_info); - os_memcpy(mic, key192->key_mic, mic_len); - os_memset(key192->key_mic, 0, mic_len); + os_memcpy(mic, mic_pos, mic_len); + os_memset(mic_pos, 0, mic_len); if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp, key_info & WPA_KEY_INFO_TYPE_MASK, - data, data_len, key192->key_mic) || - os_memcmp_const(mic, key192->key_mic, mic_len) != 0) + data, data_len, mic_pos) || + os_memcmp_const(mic, mic_pos, mic_len) != 0) ret = -1; - os_memcpy(key192->key_mic, mic, mic_len); + os_memcpy(mic_pos, mic, mic_len); return ret; } @@ -1670,7 +1636,10 @@ void wpa_remove_ptk(struct wpa_state_machine *sm) { sm->PTK_valid = FALSE; os_memset(&sm->PTK, 0, sizeof(sm->PTK)); - wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0); + if (wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, + 0)) + wpa_printf(MSG_DEBUG, + "RSN: PTK removal from the driver failed"); sm->pairwise_set = FALSE; eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); } @@ -1734,7 +1703,7 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) sm->ReAuthenticationRequest = TRUE; break; case WPA_ASSOC_FT: -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration " "after association"); wpa_ft_install_ptk(sm); @@ -1742,22 +1711,37 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) /* Using FT protocol, not WPA auth state machine */ sm->ft_completed = 1; return 0; -#else /* CONFIG_IEEE80211R */ +#else /* CONFIG_IEEE80211R_AP */ + break; +#endif /* CONFIG_IEEE80211R_AP */ + case WPA_ASSOC_FILS: +#ifdef CONFIG_FILS + wpa_printf(MSG_DEBUG, + "FILS: TK configuration after association"); + fils_set_tk(sm); + sm->fils_completed = 1; + return 0; +#else /* CONFIG_FILS */ break; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_FILS */ case WPA_DRV_STA_REMOVED: sm->tk_already_set = FALSE; return 0; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP sm->ft_completed = 0; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W if (sm->mgmt_frame_prot && event == WPA_AUTH) remove_ptk = 0; #endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_FILS + if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) && + (event == WPA_AUTH || event == WPA_ASSOC)) + remove_ptk = 0; +#endif /* CONFIG_FILS */ if (remove_ptk) { sm->PTK_valid = FALSE; @@ -1802,7 +1786,9 @@ SM_STATE(WPA_PTK, INITIALIZE) wpa_remove_ptk(sm); wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0); sm->TimeoutCtr = 0; - if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || + sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) { wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_authorized, 0); } @@ -1811,9 +1797,14 @@ SM_STATE(WPA_PTK, INITIALIZE) SM_STATE(WPA_PTK, DISCONNECT) { + u16 reason = sm->disconnect_reason; + SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk); sm->Disconnect = FALSE; - wpa_sta_disconnect(sm->wpa_auth, sm->addr); + sm->disconnect_reason = 0; + if (!reason) + reason = WLAN_REASON_PREV_AUTH_NOT_VALID; + wpa_sta_disconnect(sm->wpa_auth, sm->addr, reason); } @@ -1922,17 +1913,25 @@ SM_STATE(WPA_PTK, INITPMK) size_t len = 2 * PMK_LEN; SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP sm->xxkey_len = 0; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if (sm->pmksa) { wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len); sm->pmk_len = sm->pmksa->pmk_len; +#ifdef CONFIG_DPP + } else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) { + wpa_printf(MSG_DEBUG, + "DPP: No PMKSA cache entry for STA - reject connection"); + sm->Disconnect = TRUE; + sm->disconnect_reason = WLAN_REASON_INVALID_PMKID; + return; +#endif /* CONFIG_DPP */ } else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) { unsigned int pmk_len; - if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) pmk_len = PMK_LEN_SUITE_B_192; else pmk_len = PMK_LEN; @@ -1948,15 +1947,20 @@ SM_STATE(WPA_PTK, INITPMK) } os_memcpy(sm->PMK, msk, pmk_len); sm->pmk_len = pmk_len; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (len >= 2 * PMK_LEN) { - os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN); - sm->xxkey_len = PMK_LEN; + if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) { + os_memcpy(sm->xxkey, msk, SHA384_MAC_LEN); + sm->xxkey_len = SHA384_MAC_LEN; + } else { + os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN); + sm->xxkey_len = PMK_LEN; + } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ } else { wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p", - sm->wpa_auth->cb.get_msk); + sm->wpa_auth->cb->get_msk); sm->Disconnect = TRUE; return; } @@ -1978,16 +1982,26 @@ SM_STATE(WPA_PTK, INITPMK) SM_STATE(WPA_PTK, INITPSK) { const u8 *psk; + size_t psk_len; + SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); - psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL); + psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL, + &psk_len); if (psk) { - os_memcpy(sm->PMK, psk, PMK_LEN); - sm->pmk_len = PMK_LEN; -#ifdef CONFIG_IEEE80211R + os_memcpy(sm->PMK, psk, psk_len); + sm->pmk_len = psk_len; +#ifdef CONFIG_IEEE80211R_AP os_memcpy(sm->xxkey, psk, PMK_LEN); sm->xxkey_len = PMK_LEN; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ + } +#ifdef CONFIG_SAE + if (wpa_auth_uses_sae(sm) && sm->pmksa) { + wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache"); + os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len); + sm->pmk_len = sm->pmksa->pmk_len; } +#endif /* CONFIG_SAE */ sm->req_replay_counter_used = 0; } @@ -2003,7 +2017,7 @@ SM_STATE(WPA_PTK, PTKSTART) sm->alt_snonce_valid = FALSE; sm->TimeoutCtr++; - if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { + if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ return; @@ -2012,11 +2026,23 @@ SM_STATE(WPA_PTK, PTKSTART) wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "sending 1/4 msg of 4-Way Handshake"); /* - * TODO: Could add PMKID even with WPA2-PSK, but only if there is only - * one possible PSK for this STA. + * For infrastructure BSS cases, it is better for the AP not to include + * the PMKID KDE in EAPOL-Key msg 1/4 since it could be used to initiate + * offline search for the passphrase/PSK without having to be able to + * capture a 4-way handshake from a STA that has access to the network. + * + * For IBSS cases, addition of PMKID KDE could be considered even with + * WPA2-PSK cases that use multiple PSKs, but only if there is a single + * possible PSK for this STA. However, this should not be done unless + * there is support for using that information on the supplicant side. + * The concern about exposing PMKID unnecessarily in infrastructure BSS + * cases would also apply here, but at least in the IBSS case, this + * would cover a potential real use case. */ if (sm->wpa == WPA_VERSION_WPA2 && - wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) && + (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) || + (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && sm->pmksa) || + wpa_key_mgmt_sae(sm->wpa_key_mgmt)) && sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) { pmkid = buf; pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; @@ -2024,11 +2050,31 @@ SM_STATE(WPA_PTK, PTKSTART) pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN; RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID); if (sm->pmksa) { + wpa_hexdump(MSG_DEBUG, + "RSN: Message 1/4 PMKID from PMKSA entry", + sm->pmksa->pmkid, PMKID_LEN); os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN], sm->pmksa->pmkid, PMKID_LEN); } else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) { /* No KCK available to derive PMKID */ + wpa_printf(MSG_DEBUG, + "RSN: No KCK available to derive PMKID for message 1/4"); pmkid = NULL; +#ifdef CONFIG_SAE + } else if (wpa_key_mgmt_sae(sm->wpa_key_mgmt)) { + if (sm->pmkid_set) { + wpa_hexdump(MSG_DEBUG, + "RSN: Message 1/4 PMKID from SAE", + sm->pmkid, PMKID_LEN); + os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN], + sm->pmkid, PMKID_LEN); + } else { + /* No PMKID available */ + wpa_printf(MSG_DEBUG, + "RSN: No SAE PMKID available for message 1/4"); + pmkid = NULL; + } +#endif /* CONFIG_SAE */ } else { /* * Calculate PMKID since no PMKSA cache entry was @@ -2036,7 +2082,10 @@ SM_STATE(WPA_PTK, PTKSTART) */ rsn_pmkid(sm->PMK, sm->pmk_len, sm->wpa_auth->addr, sm->addr, &pmkid[2 + RSN_SELECTOR_LEN], - wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); + sm->wpa_key_mgmt); + wpa_hexdump(MSG_DEBUG, + "RSN: Message 1/4 PMKID derived from PMK", + &pmkid[2 + RSN_SELECTOR_LEN], PMKID_LEN); } } wpa_send_eapol(sm->wpa_auth, sm, @@ -2049,10 +2098,10 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, const u8 *pmk, unsigned int pmk_len, struct wpa_ptk *ptk) { -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) return wpa_auth_derive_ptk_ft(sm, pmk, ptk); -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion", sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce, @@ -2060,43 +2109,587 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce, } +#ifdef CONFIG_FILS + +int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk, + size_t pmk_len, const u8 *snonce, const u8 *anonce, + const u8 *dhss, size_t dhss_len, + struct wpabuf *g_sta, struct wpabuf *g_ap) +{ + u8 ick[FILS_ICK_MAX_LEN]; + size_t ick_len; + int res; + u8 fils_ft[FILS_FT_MAX_LEN]; + size_t fils_ft_len = 0; + + res = fils_pmk_to_ptk(pmk, pmk_len, sm->addr, sm->wpa_auth->addr, + snonce, anonce, dhss, dhss_len, + &sm->PTK, ick, &ick_len, + sm->wpa_key_mgmt, sm->pairwise, + fils_ft, &fils_ft_len); + if (res < 0) + return res; + sm->PTK_valid = TRUE; + sm->tk_already_set = FALSE; + +#ifdef CONFIG_IEEE80211R_AP + if (fils_ft_len) { + struct wpa_authenticator *wpa_auth = sm->wpa_auth; + struct wpa_auth_config *conf = &wpa_auth->conf; + u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN]; + int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt); + size_t pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN; + + if (wpa_derive_pmk_r0(fils_ft, fils_ft_len, + conf->ssid, conf->ssid_len, + conf->mobility_domain, + conf->r0_key_holder, + conf->r0_key_holder_len, + sm->addr, pmk_r0, pmk_r0_name, + use_sha384) < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0", + pmk_r0, pmk_r0_len); + wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name", + pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name); + os_memset(fils_ft, 0, sizeof(fils_ft)); + } +#endif /* CONFIG_IEEE80211R_AP */ + + res = fils_key_auth_sk(ick, ick_len, snonce, anonce, + sm->addr, sm->wpa_auth->addr, + g_sta ? wpabuf_head(g_sta) : NULL, + g_sta ? wpabuf_len(g_sta) : 0, + g_ap ? wpabuf_head(g_ap) : NULL, + g_ap ? wpabuf_len(g_ap) : 0, + sm->wpa_key_mgmt, sm->fils_key_auth_sta, + sm->fils_key_auth_ap, + &sm->fils_key_auth_len); + os_memset(ick, 0, sizeof(ick)); + + /* Store nonces for (Re)Association Request/Response frame processing */ + os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN); + os_memcpy(sm->ANonce, anonce, FILS_NONCE_LEN); + + return res; +} + + +static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk, + u8 *buf, size_t buf_len, u16 *_key_data_len) +{ + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + u8 *pos; + u16 key_data_len; + u8 *tmp; + const u8 *aad[1]; + size_t aad_len[1]; + + hdr = (struct ieee802_1x_hdr *) buf; + key = (struct wpa_eapol_key *) (hdr + 1); + pos = (u8 *) (key + 1); + key_data_len = WPA_GET_BE16(pos); + if (key_data_len < AES_BLOCK_SIZE || + key_data_len > buf_len - sizeof(*hdr) - sizeof(*key) - 2) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "No room for AES-SIV data in the frame"); + return -1; + } + pos += 2; /* Pointing at the Encrypted Key Data field */ + + tmp = os_malloc(key_data_len); + if (!tmp) + return -1; + + /* AES-SIV AAD from EAPOL protocol version field (inclusive) to + * to Key Data (exclusive). */ + aad[0] = buf; + aad_len[0] = pos - buf; + if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, key_data_len, + 1, aad, aad_len, tmp) < 0) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "Invalid AES-SIV data in the frame"); + bin_clear_free(tmp, key_data_len); + return -1; + } + + /* AEAD decryption and validation completed successfully */ + key_data_len -= AES_BLOCK_SIZE; + wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data", + tmp, key_data_len); + + /* Replace Key Data field with the decrypted version */ + os_memcpy(pos, tmp, key_data_len); + pos -= 2; /* Key Data Length field */ + WPA_PUT_BE16(pos, key_data_len); + bin_clear_free(tmp, key_data_len); + if (_key_data_len) + *_key_data_len = key_data_len; + return 0; +} + + +const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm, + const u8 *ies, size_t ies_len, + const u8 *fils_session) +{ + const u8 *ie, *end; + const u8 *session = NULL; + + if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + wpa_printf(MSG_DEBUG, + "FILS: Not a FILS AKM - reject association"); + return NULL; + } + + /* Verify Session element */ + ie = ies; + end = ((const u8 *) ie) + ies_len; + while (ie + 1 < end) { + if (ie + 2 + ie[1] > end) + break; + if (ie[0] == WLAN_EID_EXTENSION && + ie[1] >= 1 + FILS_SESSION_LEN && + ie[2] == WLAN_EID_EXT_FILS_SESSION) { + session = ie; + break; + } + ie += 2 + ie[1]; + } + + if (!session) { + wpa_printf(MSG_DEBUG, + "FILS: %s: Could not find FILS Session element in Assoc Req - reject", + __func__); + return NULL; + } + + if (!fils_session) { + wpa_printf(MSG_DEBUG, + "FILS: %s: Could not find FILS Session element in STA entry - reject", + __func__); + return NULL; + } + + if (os_memcmp(fils_session, session + 3, FILS_SESSION_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FILS: Session mismatch"); + wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session", + fils_session, FILS_SESSION_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session", + session + 3, FILS_SESSION_LEN); + return NULL; + } + return session; +} + + +int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies, + size_t ies_len) +{ + struct ieee802_11_elems elems; + + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, + "FILS: Failed to parse decrypted elements"); + return -1; + } + + if (!elems.fils_session) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Session element"); + return -1; + } + + if (!elems.fils_key_confirm) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element"); + return -1; + } + + if (elems.fils_key_confirm_len != sm->fils_key_auth_len) { + wpa_printf(MSG_DEBUG, + "FILS: Unexpected Key-Auth length %d (expected %d)", + elems.fils_key_confirm_len, + (int) sm->fils_key_auth_len); + return -1; + } + + if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_sta, + sm->fils_key_auth_len) != 0) { + wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch"); + wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth", + elems.fils_key_confirm, elems.fils_key_confirm_len); + wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth", + sm->fils_key_auth_sta, sm->fils_key_auth_len); + return -1; + } + + return 0; +} + + +int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session, + const struct ieee80211_mgmt *mgmt, size_t frame_len, + u8 *pos, size_t left) +{ + u16 fc, stype; + const u8 *end, *ie_start, *ie, *session, *crypt; + const u8 *aad[5]; + size_t aad_len[5]; + + if (!sm || !sm->PTK_valid) { + wpa_printf(MSG_DEBUG, + "FILS: No KEK to decrypt Assocication Request frame"); + return -1; + } + + if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + wpa_printf(MSG_DEBUG, + "FILS: Not a FILS AKM - reject association"); + return -1; + } + + end = ((const u8 *) mgmt) + frame_len; + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); + if (stype == WLAN_FC_STYPE_REASSOC_REQ) + ie_start = mgmt->u.reassoc_req.variable; + else + ie_start = mgmt->u.assoc_req.variable; + ie = ie_start; + + /* + * Find FILS Session element which is the last unencrypted element in + * the frame. + */ + session = wpa_fils_validate_fils_session(sm, ie, end - ie, + fils_session); + if (!session) { + wpa_printf(MSG_DEBUG, "FILS: Session validation failed"); + return -1; + } + + crypt = session + 2 + session[1]; + + if (end - crypt < AES_BLOCK_SIZE) { + wpa_printf(MSG_DEBUG, + "FILS: Too short frame to include AES-SIV data"); + return -1; + } + + /* AES-SIV AAD vectors */ + + /* The STA's MAC address */ + aad[0] = mgmt->sa; + aad_len[0] = ETH_ALEN; + /* The AP's BSSID */ + aad[1] = mgmt->da; + aad_len[1] = ETH_ALEN; + /* The STA's nonce */ + aad[2] = sm->SNonce; + aad_len[2] = FILS_NONCE_LEN; + /* The AP's nonce */ + aad[3] = sm->ANonce; + aad_len[3] = FILS_NONCE_LEN; + /* + * The (Re)Association Request frame from the Capability Information + * field to the FILS Session element (both inclusive). + */ + aad[4] = (const u8 *) &mgmt->u.assoc_req.capab_info; + aad_len[4] = crypt - aad[4]; + + if (aes_siv_decrypt(sm->PTK.kek, sm->PTK.kek_len, crypt, end - crypt, + 5, aad, aad_len, pos + (crypt - ie_start)) < 0) { + wpa_printf(MSG_DEBUG, + "FILS: Invalid AES-SIV data in the frame"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "FILS: Decrypted Association Request elements", + pos, left - AES_BLOCK_SIZE); + + if (wpa_fils_validate_key_confirm(sm, pos, left - AES_BLOCK_SIZE) < 0) { + wpa_printf(MSG_DEBUG, "FILS: Key Confirm validation failed"); + return -1; + } + + return left - AES_BLOCK_SIZE; +} + + +int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf, + size_t current_len, size_t max_len, + const struct wpabuf *hlp) +{ + u8 *end = buf + max_len; + u8 *pos = buf + current_len; + struct ieee80211_mgmt *mgmt; + struct wpabuf *plain; + const u8 *aad[5]; + size_t aad_len[5]; + + if (!sm || !sm->PTK_valid) + return -1; + + wpa_hexdump(MSG_DEBUG, + "FILS: Association Response frame before FILS processing", + buf, current_len); + + mgmt = (struct ieee80211_mgmt *) buf; + + /* AES-SIV AAD vectors */ + + /* The AP's BSSID */ + aad[0] = mgmt->sa; + aad_len[0] = ETH_ALEN; + /* The STA's MAC address */ + aad[1] = mgmt->da; + aad_len[1] = ETH_ALEN; + /* The AP's nonce */ + aad[2] = sm->ANonce; + aad_len[2] = FILS_NONCE_LEN; + /* The STA's nonce */ + aad[3] = sm->SNonce; + aad_len[3] = FILS_NONCE_LEN; + /* + * The (Re)Association Response frame from the Capability Information + * field (the same offset in both Association and Reassociation + * Response frames) to the FILS Session element (both inclusive). + */ + aad[4] = (const u8 *) &mgmt->u.assoc_resp.capab_info; + aad_len[4] = pos - aad[4]; + + /* The following elements will be encrypted with AES-SIV */ + plain = fils_prepare_plainbuf(sm, hlp); + if (!plain) { + wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed"); + return -1; + } + + if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) { + wpa_printf(MSG_DEBUG, + "FILS: Not enough room for FILS elements"); + wpabuf_free(plain); + return -1; + } + + wpa_hexdump_buf_key(MSG_DEBUG, "FILS: Association Response plaintext", + plain); + + if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, + wpabuf_head(plain), wpabuf_len(plain), + 5, aad, aad_len, pos) < 0) { + wpabuf_free(plain); + return -1; + } + + wpa_hexdump(MSG_DEBUG, + "FILS: Encrypted Association Response elements", + pos, AES_BLOCK_SIZE + wpabuf_len(plain)); + current_len += wpabuf_len(plain) + AES_BLOCK_SIZE; + wpabuf_free(plain); + + sm->fils_completed = 1; + + return current_len; +} + + +static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm, + const struct wpabuf *hlp) +{ + struct wpabuf *plain; + u8 *len, *tmp, *tmp2; + u8 hdr[2]; + u8 *gtk, dummy_gtk[32]; + size_t gtk_len; + struct wpa_group *gsm; + + plain = wpabuf_alloc(1000); + if (!plain) + return NULL; + + /* TODO: FILS Public Key */ + + /* FILS Key Confirmation */ + wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(plain, 1 + sm->fils_key_auth_len); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(plain, WLAN_EID_EXT_FILS_KEY_CONFIRM); + wpabuf_put_data(plain, sm->fils_key_auth_ap, sm->fils_key_auth_len); + + /* FILS HLP Container */ + if (hlp) + wpabuf_put_buf(plain, hlp); + + /* TODO: FILS IP Address Assignment */ + + /* Key Delivery */ + gsm = sm->group; + wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */ + len = wpabuf_put(plain, 1); + wpabuf_put_u8(plain, WLAN_EID_EXT_KEY_DELIVERY); + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, + wpabuf_put(plain, WPA_KEY_RSC_LEN)); + /* GTK KDE */ + gtk = gsm->GTK[gsm->GN - 1]; + gtk_len = gsm->GTK_len; + if (sm->wpa_auth->conf.disable_gtk || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { + /* + * Provide unique random GTK to each STA to prevent use + * of GTK in the BSS. + */ + if (random_get_bytes(dummy_gtk, gtk_len) < 0) { + wpabuf_free(plain); + return NULL; + } + gtk = dummy_gtk; + } + hdr[0] = gsm->GN & 0x03; + hdr[1] = 0; + tmp = wpabuf_put(plain, 0); + tmp2 = wpa_add_kde(tmp, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gtk, gtk_len); + wpabuf_put(plain, tmp2 - tmp); + + /* IGTK KDE */ + tmp = wpabuf_put(plain, 0); + tmp2 = ieee80211w_kde_add(sm, tmp); + wpabuf_put(plain, tmp2 - tmp); + + *len = (u8 *) wpabuf_put(plain, 0) - len - 1; + return plain; +} + + +int fils_set_tk(struct wpa_state_machine *sm) +{ + enum wpa_alg alg; + int klen; + + if (!sm || !sm->PTK_valid) { + wpa_printf(MSG_DEBUG, "FILS: No valid PTK available to set TK"); + return -1; + } + if (sm->tk_already_set) { + wpa_printf(MSG_DEBUG, "FILS: TK already set to the driver"); + return -1; + } + + alg = wpa_cipher_to_alg(sm->pairwise); + klen = wpa_cipher_key_len(sm->pairwise); + + wpa_printf(MSG_DEBUG, "FILS: Configure TK to the driver"); + if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, + sm->PTK.tk, klen)) { + wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver"); + return -1; + } + sm->tk_already_set = TRUE; + + return 0; +} + + +u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *buf, + const u8 *fils_session, struct wpabuf *hlp) +{ + struct wpabuf *plain; + u8 *pos = buf; + + /* FILS Session */ + *pos++ = WLAN_EID_EXTENSION; /* Element ID */ + *pos++ = 1 + FILS_SESSION_LEN; /* Length */ + *pos++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */ + os_memcpy(pos, fils_session, FILS_SESSION_LEN); + pos += FILS_SESSION_LEN; + + plain = fils_prepare_plainbuf(sm, hlp); + if (!plain) { + wpa_printf(MSG_DEBUG, "FILS: Plain buffer prep failed"); + return NULL; + } + + os_memcpy(pos, wpabuf_head(plain), wpabuf_len(plain)); + pos += wpabuf_len(plain); + + wpa_printf(MSG_DEBUG, "%s: plain buf_len: %u", __func__, + (unsigned int) wpabuf_len(plain)); + wpabuf_free(plain); + sm->fils_completed = 1; + return pos; +} + +#endif /* CONFIG_FILS */ + + SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) { + struct wpa_authenticator *wpa_auth = sm->wpa_auth; struct wpa_ptk PTK; int ok = 0, psk_found = 0; const u8 *pmk = NULL; - unsigned int pmk_len; + size_t pmk_len; + int ft; + const u8 *eapol_key_ie, *key_data, *mic; + u16 key_data_length; + size_t mic_len, eapol_key_ie_len; + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + struct wpa_eapol_ie_parse kde; SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); sm->EAPOLKeyReceived = FALSE; sm->update_snonce = FALSE; + os_memset(&PTK, 0, sizeof(PTK)); + + mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); /* WPA with IEEE 802.1X: use the derived PMK from EAP * WPA-PSK: iterate through possible PSKs and select the one matching * the packet */ for (;;) { - if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) && + !wpa_key_mgmt_sae(sm->wpa_key_mgmt)) { pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, - sm->p2p_dev_addr, pmk); + sm->p2p_dev_addr, pmk, &pmk_len); if (pmk == NULL) break; psk_found = 1; - pmk_len = PMK_LEN; +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) { + os_memcpy(sm->xxkey, pmk, pmk_len); + sm->xxkey_len = pmk_len; + } +#endif /* CONFIG_IEEE80211R_AP */ } else { pmk = sm->PMK; pmk_len = sm->pmk_len; } - wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK); + if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK) < 0) + break; - if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, + if (mic_len && + wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK, sm->last_rx_eapol_key, sm->last_rx_eapol_key_len) == 0) { ok = 1; break; } - if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) +#ifdef CONFIG_FILS + if (!mic_len && + wpa_aead_decrypt(sm, &PTK, sm->last_rx_eapol_key, + sm->last_rx_eapol_key_len, NULL) == 0) { + ok = 1; + break; + } +#endif /* CONFIG_FILS */ + + if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || + wpa_key_mgmt_sae(sm->wpa_key_mgmt)) break; } @@ -2108,7 +2701,79 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) return; } -#ifdef CONFIG_IEEE80211R + /* + * Note: last_rx_eapol_key length fields have already been validated in + * wpa_receive(). + */ + hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key; + key = (struct wpa_eapol_key *) (hdr + 1); + mic = (u8 *) (key + 1); + key_data = mic + mic_len + 2; + key_data_length = WPA_GET_BE16(mic + mic_len); + if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) - + sizeof(*key) - mic_len - 2) + return; + + if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 2/4 with invalid Key Data contents"); + return; + } + if (kde.rsn_ie) { + eapol_key_ie = kde.rsn_ie; + eapol_key_ie_len = kde.rsn_ie_len; + } else if (kde.osen) { + eapol_key_ie = kde.osen; + eapol_key_ie_len = kde.osen_len; + } else { + eapol_key_ie = kde.wpa_ie; + eapol_key_ie_len = kde.wpa_ie_len; + } + ft = sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt); + if (sm->wpa_ie == NULL || + wpa_compare_rsn_ie(ft, sm->wpa_ie, sm->wpa_ie_len, + eapol_key_ie, eapol_key_ie_len)) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "WPA IE from (Re)AssocReq did not match with msg 2/4"); + if (sm->wpa_ie) { + wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq", + sm->wpa_ie, sm->wpa_ie_len); + } + wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4", + eapol_key_ie, eapol_key_ie_len); + /* MLME-DEAUTHENTICATE.request */ + wpa_sta_disconnect(wpa_auth, sm->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + return; + } +#ifdef CONFIG_IEEE80211R_AP + if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) { + wpa_sta_disconnect(wpa_auth, sm->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + return; + } +#endif /* CONFIG_IEEE80211R_AP */ +#ifdef CONFIG_P2P + if (kde.ip_addr_req && kde.ip_addr_req[0] && + wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) { + int idx; + wpa_printf(MSG_DEBUG, + "P2P: IP address requested in EAPOL-Key exchange"); + idx = bitfield_get_first_zero(wpa_auth->ip_pool); + if (idx >= 0) { + u32 start = WPA_GET_BE32(wpa_auth->conf.ip_addr_start); + bitfield_set(wpa_auth->ip_pool, idx); + WPA_PUT_BE32(sm->ip_addr, start + idx); + wpa_printf(MSG_DEBUG, + "P2P: Assigned IP address %u.%u.%u.%u to " + MACSTR, sm->ip_addr[0], sm->ip_addr[1], + sm->ip_addr[2], sm->ip_addr[3], + MAC2STR(sm->addr)); + } + } +#endif /* CONFIG_P2P */ + +#ifdef CONFIG_IEEE80211R_AP if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { /* * Verify that PMKR1Name from EAPOL-Key message 2/4 matches @@ -2127,7 +2792,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) return; } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ sm->pending_1_of_4_timeout = 0; eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); @@ -2186,7 +2851,8 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) else os_memcpy(igtk.pn, rsc, sizeof(igtk.pn)); os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len); - if (sm->wpa_auth->conf.disable_gtk) { + if (sm->wpa_auth->conf.disable_gtk || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { /* * Provide unique random IGTK to each STA to prevent use of * IGTK in the BSS. @@ -2229,7 +2895,12 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) sm->TimeoutEvt = FALSE; sm->TimeoutCtr++; - if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { + if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && + sm->TimeoutCtr > 1) { + /* Do not allow retransmission of EAPOL-Key msg 3/4 */ + return; + } + if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ return; @@ -2259,7 +2930,8 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) secure = 1; gtk = gsm->GTK[gsm->GN - 1]; gtk_len = gsm->GTK_len; - if (sm->wpa_auth->conf.disable_gtk) { + if (sm->wpa_auth->conf.disable_gtk || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { /* * Provide unique random GTK to each STA to prevent use * of GTK in the BSS. @@ -2297,12 +2969,12 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) kde_len = wpa_ie_len + ieee80211w_kde_len(sm); if (gtk) kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */ kde_len += 300; /* FTIE + 2 * TIE */ } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_P2P if (WPA_GET_BE32(sm->ip_addr) > 0) kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4; @@ -2314,7 +2986,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) pos = kde; os_memcpy(pos, wpa_ie, wpa_ie_len); pos += wpa_ie_len; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { int res; size_t elen; @@ -2330,7 +3002,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) pos -= wpa_ie_len; pos += elen; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if (gtk) { u8 hdr[2]; hdr[0] = keyidx & 0x03; @@ -2340,7 +3012,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) } pos = ieee80211w_kde_add(sm, pos); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { int res; struct wpa_auth_config *conf; @@ -2352,7 +3024,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) 2 + sm->assoc_resp_ftie[1]); res = 2 + sm->assoc_resp_ftie[1]; } else { - res = wpa_write_ftie(conf, conf->r0_key_holder, + int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt); + + res = wpa_write_ftie(conf, use_sha384, + conf->r0_key_holder, conf->r0_key_holder_len, NULL, NULL, pos, kde + kde_len - pos, @@ -2377,10 +3052,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) *pos++ = WLAN_EID_TIMEOUT_INTERVAL; *pos++ = 5; *pos++ = WLAN_TIMEOUT_KEY_LIFETIME; - WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60); + WPA_PUT_LE32(pos, conf->r0_key_lifetime); pos += 4; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_P2P if (WPA_GET_BE32(sm->ip_addr) > 0) { u8 addr[3 * 4]; @@ -2393,7 +3068,9 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) #endif /* CONFIG_P2P */ wpa_send_eapol(sm->wpa_auth, sm, - (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC | + (secure ? WPA_KEY_INFO_SECURE : 0) | + (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? + WPA_KEY_INFO_MIC : 0) | WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_KEY_TYPE, _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); @@ -2410,7 +3087,8 @@ SM_STATE(WPA_PTK, PTKINITDONE) int klen = wpa_cipher_key_len(sm->pairwise); if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, sm->PTK.tk, klen)) { - wpa_sta_disconnect(sm->wpa_auth, sm->addr); + wpa_sta_disconnect(sm->wpa_auth, sm->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); return; } /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ @@ -2423,7 +3101,9 @@ SM_STATE(WPA_PTK, PTKINITDONE) sm->wpa_auth, sm); } - if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || + sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE) { wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_authorized, 1); } @@ -2449,9 +3129,9 @@ SM_STATE(WPA_PTK, PTKINITDONE) "pairwise key handshake completed (%s)", sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr); -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ } @@ -2495,15 +3175,22 @@ SM_STEP(WPA_PTK) wpa_auth_get_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun) > 0) SM_ENTER(WPA_PTK, INITPMK); - else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) + else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE /* FIX: && 802.1X::keyRun */) SM_ENTER(WPA_PTK, INITPSK); + else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP) + SM_ENTER(WPA_PTK, INITPMK); break; case WPA_PTK_INITPMK: if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr, - WPA_EAPOL_keyAvailable) > 0) + WPA_EAPOL_keyAvailable) > 0) { + SM_ENTER(WPA_PTK, PTKSTART); +#ifdef CONFIG_DPP + } else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->pmksa) { SM_ENTER(WPA_PTK, PTKSTART); - else { +#endif /* CONFIG_DPP */ + } else { wpa_auth->dot11RSNA4WayHandshakeFailures++; wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, "INITPMK - keyAvailable = false"); @@ -2512,9 +3199,13 @@ SM_STEP(WPA_PTK) break; case WPA_PTK_INITPSK: if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, - NULL)) + NULL, NULL)) { + SM_ENTER(WPA_PTK, PTKSTART); +#ifdef CONFIG_SAE + } else if (wpa_auth_uses_sae(sm) && sm->pmksa) { SM_ENTER(WPA_PTK, PTKSTART); - else { +#endif /* CONFIG_SAE */ + } else { wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, "no PSK configured for the STA"); wpa_auth->dot11RSNA4WayHandshakeFailures++; @@ -2526,11 +3217,12 @@ SM_STEP(WPA_PTK) sm->EAPOLKeyPairwise) SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); else if (sm->TimeoutCtr > - (int) dot11RSNAConfigPairwiseUpdateCount) { + sm->wpa_auth->conf.wpa_pairwise_update_count) { wpa_auth->dot11RSNA4WayHandshakeFailures++; - wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "PTKSTART: Retry limit %d reached", - dot11RSNAConfigPairwiseUpdateCount); + wpa_auth_vlogger( + sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "PTKSTART: Retry limit %u reached", + sm->wpa_auth->conf.wpa_pairwise_update_count); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKSTART); @@ -2554,12 +3246,14 @@ SM_STEP(WPA_PTK) sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK, PTKINITDONE); else if (sm->TimeoutCtr > - (int) dot11RSNAConfigPairwiseUpdateCount) { + sm->wpa_auth->conf.wpa_pairwise_update_count || + (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && + sm->TimeoutCtr > 1)) { wpa_auth->dot11RSNA4WayHandshakeFailures++; - wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, - "PTKINITNEGOTIATING: Retry limit %d " - "reached", - dot11RSNAConfigPairwiseUpdateCount); + wpa_auth_vlogger( + sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "PTKINITNEGOTIATING: Retry limit %u reached", + sm->wpa_auth->conf.wpa_pairwise_update_count); SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); @@ -2594,7 +3288,12 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); sm->GTimeoutCtr++; - if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) { + if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && + sm->GTimeoutCtr > 1) { + /* Do not allow retransmission of EAPOL-Key group msg 1/2 */ + return; + } + if (sm->GTimeoutCtr > sm->wpa_auth->conf.wpa_group_update_count) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ return; @@ -2611,7 +3310,8 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) "sending 1/2 msg of Group Key Handshake"); gtk = gsm->GTK[gsm->GN - 1]; - if (sm->wpa_auth->conf.disable_gtk) { + if (sm->wpa_auth->conf.disable_gtk || + sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN) { /* * Provide unique random GTK to each STA to prevent use * of GTK in the BSS. @@ -2640,10 +3340,12 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) } wpa_send_eapol(sm->wpa_auth, sm, - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SECURE | + (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? + WPA_KEY_INFO_MIC : 0) | WPA_KEY_INFO_ACK | (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), - rsc, gsm->GNonce, kde, kde_len, gsm->GN, 1); + rsc, NULL, kde, kde_len, gsm->GN, 1); os_free(kde_buf); } @@ -2672,6 +3374,10 @@ SM_STATE(WPA_PTK_GROUP, KEYERROR) sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; sm->Disconnect = TRUE; + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "group key handshake failed (%s) after %u tries", + sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN", + sm->wpa_auth->conf.wpa_group_update_count); } @@ -2691,7 +3397,9 @@ SM_STEP(WPA_PTK_GROUP) !sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED); else if (sm->GTimeoutCtr > - (int) dot11RSNAConfigGroupUpdateCount) + sm->wpa_auth->conf.wpa_group_update_count || + (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && + sm->GTimeoutCtr > 1)) SM_ENTER(WPA_PTK_GROUP, KEYERROR); else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); @@ -2794,7 +3502,7 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) } -#ifdef CONFIG_WNM +#ifdef CONFIG_WNM_AP /* update GTK when exiting WNM-Sleep Mode */ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm) { @@ -2873,7 +3581,7 @@ int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos) return pos - start; } #endif /* CONFIG_IEEE80211W */ -#endif /* CONFIG_WNM */ +#endif /* CONFIG_WNM_AP */ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, @@ -3151,8 +3859,8 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", RSN_VERSION, !!wpa_auth->conf.wpa_strict_rekey, - dot11RSNAConfigGroupUpdateCount, - dot11RSNAConfigPairwiseUpdateCount, + wpa_auth->conf.wpa_group_update_count, + wpa_auth->conf.wpa_pairwise_update_count, wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8, dot11RSNAConfigPMKLifetime, dot11RSNAConfigPMKReauthThreshold, @@ -3279,6 +3987,14 @@ int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm) } +int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm) +{ + if (!sm || !wpa_key_mgmt_fils(sm->wpa_key_mgmt)) + return 0; + return sm->tk_already_set; +} + + int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, struct rsn_pmksa_cache_entry *entry) { @@ -3320,7 +4036,7 @@ int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, sm->wpa_auth->conf.disable_pmksa_caching) return -1; - if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { + if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) { if (pmk_len > PMK_LEN_SUITE_B_192) pmk_len = PMK_LEN_SUITE_B_192; } else if (pmk_len > PMK_LEN) { @@ -3372,6 +4088,29 @@ int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, } +void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid) +{ + os_memcpy(sm->pmkid, pmkid, PMKID_LEN); + sm->pmkid_set = 1; +} + + +int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *pmk, size_t pmk_len, const u8 *pmkid, + int session_timeout, int akmp) +{ + if (wpa_auth->conf.disable_pmksa_caching) + return -1; + + if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid, + NULL, 0, wpa_auth->addr, addr, session_timeout, + NULL, akmp)) + return 0; + + return -1; +} + + void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) { @@ -3404,12 +4143,65 @@ void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth) } +#ifdef CONFIG_PMKSA_CACHE_EXTERNAL +#ifdef CONFIG_MESH + +int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr, + char *buf, size_t len) +{ + if (!wpa_auth || !wpa_auth->pmksa) + return 0; + + return pmksa_cache_auth_list_mesh(wpa_auth->pmksa, addr, buf, len); +} + + struct rsn_pmksa_cache_entry * -wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) +wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk, + const u8 *pmkid, int expiration) +{ + struct rsn_pmksa_cache_entry *entry; + struct os_reltime now; + + entry = pmksa_cache_auth_create_entry(pmk, PMK_LEN, pmkid, NULL, 0, aa, + spa, 0, NULL, WPA_KEY_MGMT_SAE); + if (!entry) + return NULL; + + os_get_reltime(&now); + entry->expiration = now.sec + expiration; + return entry; +} + + +int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth, + struct rsn_pmksa_cache_entry *entry) +{ + int ret; + + if (!wpa_auth || !wpa_auth->pmksa) + return -1; + + ret = pmksa_cache_auth_add_entry(wpa_auth->pmksa, entry); + if (ret < 0) + wpa_printf(MSG_DEBUG, + "RSN: Failed to store external PMKSA cache for " + MACSTR, MAC2STR(entry->spa)); + + return ret; +} + +#endif /* CONFIG_MESH */ +#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */ + + +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + const u8 *pmkid) { if (!wpa_auth || !wpa_auth->pmksa) return NULL; - return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL); + return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, pmkid); } @@ -3662,6 +4454,14 @@ void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, (timeout_ms % 1000) * 1000, wpa_send_eapol_timeout, wpa_auth, sm); } + +#ifdef CONFIG_TESTING_OPTIONS + if (sm->eapol_status_cb) { + sm->eapol_status_cb(sm->eapol_status_cb_ctx1, + sm->eapol_status_cb_ctx2); + sm->eapol_status_cb = NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ } @@ -3708,3 +4508,343 @@ void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth) for (group = wpa_auth->group; group; group = group->next) wpa_group_config_group_keys(wpa_auth, group); } + + +#ifdef CONFIG_FILS + +struct wpa_auth_fils_iter_data { + struct wpa_authenticator *auth; + const u8 *cache_id; + struct rsn_pmksa_cache_entry *pmksa; + const u8 *spa; + const u8 *pmkid; +}; + + +static int wpa_auth_fils_iter(struct wpa_authenticator *a, void *ctx) +{ + struct wpa_auth_fils_iter_data *data = ctx; + + if (a == data->auth || !a->conf.fils_cache_id_set || + os_memcmp(a->conf.fils_cache_id, data->cache_id, + FILS_CACHE_ID_LEN) != 0) + return 0; + data->pmksa = pmksa_cache_auth_get(a->pmksa, data->spa, data->pmkid); + return data->pmksa != NULL; +} + + +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, const u8 *pmkid) +{ + struct wpa_auth_fils_iter_data idata; + + if (!wpa_auth->conf.fils_cache_id_set) + return NULL; + idata.auth = wpa_auth; + idata.cache_id = wpa_auth->conf.fils_cache_id; + idata.pmksa = NULL; + idata.spa = sta_addr; + idata.pmkid = pmkid; + wpa_auth_for_each_auth(wpa_auth, wpa_auth_fils_iter, &idata); + return idata.pmksa; +} + + +#ifdef CONFIG_IEEE80211R_AP +int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384, + u8 *buf, size_t len) +{ + struct wpa_auth_config *conf = &wpa_auth->conf; + + return wpa_write_ftie(conf, use_sha384, conf->r0_key_holder, + conf->r0_key_holder_len, + NULL, NULL, buf, len, NULL, 0); +} +#endif /* CONFIG_IEEE80211R_AP */ + + +void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm, + u8 *fils_anonce, u8 *fils_snonce, + u8 *fils_kek, size_t *fils_kek_len) +{ + os_memcpy(fils_anonce, sm->ANonce, WPA_NONCE_LEN); + os_memcpy(fils_snonce, sm->SNonce, WPA_NONCE_LEN); + os_memcpy(fils_kek, sm->PTK.kek, WPA_KEK_MAX_LEN); + *fils_kek_len = sm->PTK.kek_len; +} + +#endif /* CONFIG_FILS */ + + +#ifdef CONFIG_TESTING_OPTIONS + +int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce, + void (*cb)(void *ctx1, void *ctx2), + void *ctx1, void *ctx2) +{ + const u8 *anonce = sm->ANonce; + u8 anonce_buf[WPA_NONCE_LEN]; + + if (change_anonce) { + if (random_get_bytes(anonce_buf, WPA_NONCE_LEN)) + return -1; + anonce = anonce_buf; + } + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/4 msg of 4-Way Handshake (TESTING)"); + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL, + anonce, NULL, 0, 0, 0); + return 0; +} + + +int wpa_auth_resend_m3(struct wpa_state_machine *sm, + void (*cb)(void *ctx1, void *ctx2), + void *ctx1, void *ctx2) +{ + u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos; +#ifdef CONFIG_IEEE80211W + u8 *opos; +#endif /* CONFIG_IEEE80211W */ + size_t gtk_len, kde_len; + struct wpa_group *gsm = sm->group; + u8 *wpa_ie; + int wpa_ie_len, secure, keyidx, encr = 0; + + /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE], + GTK[GN], IGTK, [FTIE], [TIE * 2]) + */ + + /* Use 0 RSC */ + os_memset(rsc, 0, WPA_KEY_RSC_LEN); + /* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */ + wpa_ie = sm->wpa_auth->wpa_ie; + wpa_ie_len = sm->wpa_auth->wpa_ie_len; + if (sm->wpa == WPA_VERSION_WPA && + (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) && + wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) { + /* WPA-only STA, remove RSN IE and possible MDIE */ + wpa_ie = wpa_ie + wpa_ie[1] + 2; + if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN) + wpa_ie = wpa_ie + wpa_ie[1] + 2; + wpa_ie_len = wpa_ie[1] + 2; + } + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 3/4 msg of 4-Way Handshake (TESTING)"); + if (sm->wpa == WPA_VERSION_WPA2) { + /* WPA2 send GTK in the 4-way handshake */ + secure = 1; + gtk = gsm->GTK[gsm->GN - 1]; + gtk_len = gsm->GTK_len; + keyidx = gsm->GN; + _rsc = rsc; + encr = 1; + } else { + /* WPA does not include GTK in msg 3/4 */ + secure = 0; + gtk = NULL; + gtk_len = 0; + keyidx = 0; + _rsc = NULL; + if (sm->rx_eapol_key_secure) { + /* + * It looks like Windows 7 supplicant tries to use + * Secure bit in msg 2/4 after having reported Michael + * MIC failure and it then rejects the 4-way handshake + * if msg 3/4 does not set Secure bit. Work around this + * by setting the Secure bit here even in the case of + * WPA if the supplicant used it first. + */ + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "STA used Secure bit in WPA msg 2/4 - " + "set Secure for 3/4 as workaround"); + secure = 1; + } + } + + kde_len = wpa_ie_len + ieee80211w_kde_len(sm); + if (gtk) + kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */ + kde_len += 300; /* FTIE + 2 * TIE */ + } +#endif /* CONFIG_IEEE80211R_AP */ + kde = os_malloc(kde_len); + if (kde == NULL) + return -1; + + pos = kde; + os_memcpy(pos, wpa_ie, wpa_ie_len); + pos += wpa_ie_len; +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + int res; + size_t elen; + + elen = pos - kde; + res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name); + if (res < 0) { + wpa_printf(MSG_ERROR, "FT: Failed to insert " + "PMKR1Name into RSN IE in EAPOL-Key data"); + os_free(kde); + return -1; + } + pos -= wpa_ie_len; + pos += elen; + } +#endif /* CONFIG_IEEE80211R_AP */ + if (gtk) { + u8 hdr[2]; + hdr[0] = keyidx & 0x03; + hdr[1] = 0; + pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gtk, gtk_len); + } +#ifdef CONFIG_IEEE80211W + opos = pos; + pos = ieee80211w_kde_add(sm, pos); + if (pos - opos >= 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) { + /* skip KDE header and keyid */ + opos += 2 + RSN_SELECTOR_LEN + 2; + os_memset(opos, 0, 6); /* clear PN */ + } +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + int res; + struct wpa_auth_config *conf; + + conf = &sm->wpa_auth->conf; + if (sm->assoc_resp_ftie && + kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) { + os_memcpy(pos, sm->assoc_resp_ftie, + 2 + sm->assoc_resp_ftie[1]); + res = 2 + sm->assoc_resp_ftie[1]; + } else { + int use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt); + + res = wpa_write_ftie(conf, use_sha384, + conf->r0_key_holder, + conf->r0_key_holder_len, + NULL, NULL, pos, + kde + kde_len - pos, + NULL, 0); + } + if (res < 0) { + wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE " + "into EAPOL-Key Key Data"); + os_free(kde); + return -1; + } + pos += res; + + /* TIE[ReassociationDeadline] (TU) */ + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE; + WPA_PUT_LE32(pos, conf->reassociation_deadline); + pos += 4; + + /* TIE[KeyLifetime] (seconds) */ + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_KEY_LIFETIME; + WPA_PUT_LE32(pos, conf->r0_key_lifetime); + pos += 4; + } +#endif /* CONFIG_IEEE80211R_AP */ + + wpa_send_eapol(sm->wpa_auth, sm, + (secure ? WPA_KEY_INFO_SECURE : 0) | + (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? + WPA_KEY_INFO_MIC : 0) | + WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | + WPA_KEY_INFO_KEY_TYPE, + _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); + os_free(kde); + return 0; +} + + +int wpa_auth_resend_group_m1(struct wpa_state_machine *sm, + void (*cb)(void *ctx1, void *ctx2), + void *ctx1, void *ctx2) +{ + u8 rsc[WPA_KEY_RSC_LEN]; + struct wpa_group *gsm = sm->group; + const u8 *kde; + u8 *kde_buf = NULL, *pos, hdr[2]; +#ifdef CONFIG_IEEE80211W + u8 *opos; +#endif /* CONFIG_IEEE80211W */ + size_t kde_len; + u8 *gtk; + + /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */ + os_memset(rsc, 0, WPA_KEY_RSC_LEN); + /* Use 0 RSC */ + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/2 msg of Group Key Handshake (TESTING)"); + + gtk = gsm->GTK[gsm->GN - 1]; + if (sm->wpa == WPA_VERSION_WPA2) { + kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + + ieee80211w_kde_len(sm); + kde_buf = os_malloc(kde_len); + if (kde_buf == NULL) + return -1; + + kde = pos = kde_buf; + hdr[0] = gsm->GN & 0x03; + hdr[1] = 0; + pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gtk, gsm->GTK_len); +#ifdef CONFIG_IEEE80211W + opos = pos; + pos = ieee80211w_kde_add(sm, pos); + if (pos - opos >= + 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN) { + /* skip KDE header and keyid */ + opos += 2 + RSN_SELECTOR_LEN + 2; + os_memset(opos, 0, 6); /* clear PN */ + } +#endif /* CONFIG_IEEE80211W */ + kde_len = pos - kde; + } else { + kde = gtk; + kde_len = gsm->GTK_len; + } + + sm->eapol_status_cb = cb; + sm->eapol_status_cb_ctx1 = ctx1; + sm->eapol_status_cb_ctx2 = ctx2; + + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_SECURE | + (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? + WPA_KEY_INFO_MIC : 0) | + WPA_KEY_INFO_ACK | + (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), + rsc, NULL, kde, kde_len, gsm->GN, 1); + + os_free(kde_buf); + return 0; +} + + +int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth) +{ + if (!wpa_auth) + return -1; + eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); + return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL); +} + +#endif /* CONFIG_TESTING_OPTIONS */ diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 97461b029d240..fad5536f740e9 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -1,6 +1,6 @@ /* * hostapd - IEEE 802.11i-2004 / WPA Authenticator - * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -14,6 +14,8 @@ #include "common/wpa_common.h" #include "common/ieee802_11_defs.h" +struct vlan_description; + #define MAX_OWN_IE_OVERRIDE 256 #ifdef _MSC_VER @@ -37,74 +39,100 @@ struct ft_rrb_frame { #define FT_PACKET_REQUEST 0 #define FT_PACKET_RESPONSE 1 -/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */ -#define FT_PACKET_R0KH_R1KH_PULL 200 -#define FT_PACKET_R0KH_R1KH_RESP 201 -#define FT_PACKET_R0KH_R1KH_PUSH 202 - -#define FT_R0KH_R1KH_PULL_NONCE_LEN 16 -#define FT_R0KH_R1KH_PULL_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \ - WPA_PMK_NAME_LEN + FT_R1KH_ID_LEN + \ - ETH_ALEN) -#define FT_R0KH_R1KH_PULL_PAD_LEN ((8 - FT_R0KH_R1KH_PULL_DATA_LEN % 8) % 8) - -struct ft_r0kh_r1kh_pull_frame { - u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ - u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */ - le16 data_length; /* little endian length of data (44) */ - u8 ap_address[ETH_ALEN]; - u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; - u8 pmk_r0_name[WPA_PMK_NAME_LEN]; - u8 r1kh_id[FT_R1KH_ID_LEN]; - u8 s1kh_id[ETH_ALEN]; - u8 pad[FT_R0KH_R1KH_PULL_PAD_LEN]; /* 8-octet boundary for AES block */ - u8 key_wrap_extra[8]; -} STRUCT_PACKED; +/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r. These + * use OUI Extended EtherType as the encapsulating format. */ +#define FT_PACKET_R0KH_R1KH_PULL 0x01 +#define FT_PACKET_R0KH_R1KH_RESP 0x02 +#define FT_PACKET_R0KH_R1KH_PUSH 0x03 +#define FT_PACKET_R0KH_R1KH_SEQ_REQ 0x04 +#define FT_PACKET_R0KH_R1KH_SEQ_RESP 0x05 + +/* packet layout + * IEEE 802 extended OUI ethertype frame header + * u16 authlen (little endian) + * multiple of struct ft_rrb_tlv (authenticated only, length = authlen) + * multiple of struct ft_rrb_tlv (AES-SIV encrypted, AES-SIV needs an extra + * blocksize length) + * + * AES-SIV AAD; + * source MAC address (6) + * authenticated-only TLVs (authlen) + * subtype (1; FT_PACKET_*) + */ -#define FT_R0KH_R1KH_RESP_DATA_LEN (FT_R0KH_R1KH_PULL_NONCE_LEN + \ - FT_R1KH_ID_LEN + ETH_ALEN + PMK_LEN + \ - WPA_PMK_NAME_LEN + 2) -#define FT_R0KH_R1KH_RESP_PAD_LEN ((8 - FT_R0KH_R1KH_RESP_DATA_LEN % 8) % 8) -struct ft_r0kh_r1kh_resp_frame { - u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ - u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */ - le16 data_length; /* little endian length of data (78) */ - u8 ap_address[ETH_ALEN]; +#define FT_RRB_NONCE_LEN 16 - u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */ - u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */ - u8 s1kh_id[ETH_ALEN]; /* copied from pull */ - u8 pmk_r1[PMK_LEN]; - u8 pmk_r1_name[WPA_PMK_NAME_LEN]; - le16 pairwise; - u8 pad[FT_R0KH_R1KH_RESP_PAD_LEN]; /* 8-octet boundary for AES block */ - u8 key_wrap_extra[8]; -} STRUCT_PACKED; +#define FT_RRB_LAST_EMPTY 0 /* placeholder or padding */ -#define FT_R0KH_R1KH_PUSH_DATA_LEN (4 + FT_R1KH_ID_LEN + ETH_ALEN + \ - WPA_PMK_NAME_LEN + PMK_LEN + \ - WPA_PMK_NAME_LEN + 2) -#define FT_R0KH_R1KH_PUSH_PAD_LEN ((8 - FT_R0KH_R1KH_PUSH_DATA_LEN % 8) % 8) -struct ft_r0kh_r1kh_push_frame { - u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ - u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */ - le16 data_length; /* little endian length of data (82) */ - u8 ap_address[ETH_ALEN]; +#define FT_RRB_SEQ 1 /* struct ft_rrb_seq */ +#define FT_RRB_NONCE 2 /* size FT_RRB_NONCE_LEN */ +#define FT_RRB_TIMESTAMP 3 /* le32 unix seconds */ + +#define FT_RRB_R0KH_ID 4 /* FT_R0KH_ID_MAX_LEN */ +#define FT_RRB_R1KH_ID 5 /* FT_R1KH_ID_LEN */ +#define FT_RRB_S1KH_ID 6 /* ETH_ALEN */ + +#define FT_RRB_PMK_R0_NAME 7 /* WPA_PMK_NAME_LEN */ +#define FT_RRB_PMK_R0 8 /* PMK_LEN */ +#define FT_RRB_PMK_R1_NAME 9 /* WPA_PMK_NAME_LEN */ +#define FT_RRB_PMK_R1 10 /* PMK_LEN */ + +#define FT_RRB_PAIRWISE 11 /* le16 */ +#define FT_RRB_EXPIRES_IN 12 /* le16 seconds */ + +#define FT_RRB_VLAN_UNTAGGED 13 /* le16 */ +#define FT_RRB_VLAN_TAGGED 14 /* n times le16 */ - /* Encrypted with AES key-wrap */ - u8 timestamp[4]; /* current time in seconds since unix epoch, little - * endian */ - u8 r1kh_id[FT_R1KH_ID_LEN]; - u8 s1kh_id[ETH_ALEN]; - u8 pmk_r0_name[WPA_PMK_NAME_LEN]; - u8 pmk_r1[PMK_LEN]; - u8 pmk_r1_name[WPA_PMK_NAME_LEN]; - le16 pairwise; - u8 pad[FT_R0KH_R1KH_PUSH_PAD_LEN]; /* 8-octet boundary for AES block */ - u8 key_wrap_extra[8]; +#define FT_RRB_IDENTITY 15 +#define FT_RRB_RADIUS_CUI 16 +#define FT_RRB_SESSION_TIMEOUT 17 /* le32 seconds */ + +struct ft_rrb_tlv { + le16 type; + le16 len; + /* followed by data of length len */ +} STRUCT_PACKED; + +struct ft_rrb_seq { + le32 dom; + le32 seq; + le32 ts; } STRUCT_PACKED; +/* session TLVs: + * required: PMK_R1, PMK_R1_NAME, PAIRWISE + * optional: VLAN_UNTAGGED, VLAN_TAGGED, EXPIRES_IN, IDENTITY, RADIUS_CUI, + * SESSION_TIMEOUT + * + * pull frame TLVs: + * auth: + * required: SEQ, NONCE, R0KH_ID, R1KH_ID + * encrypted: + * required: PMK_R0_NAME, S1KH_ID + * + * response frame TLVs: + * auth: + * required: SEQ, NONCE, R0KH_ID, R1KH_ID + * encrypted: + * required: S1KH_ID + * optional: session TLVs + * + * push frame TLVs: + * auth: + * required: SEQ, R0KH_ID, R1KH_ID + * encrypted: + * required: S1KH_ID, PMK_R0_NAME, session TLVs + * + * sequence number request frame TLVs: + * auth: + * required: R0KH_ID, R1KH_ID, NONCE + * + * sequence number response frame TLVs: + * auth: + * required: SEQ, NONCE, R0KH_ID, R1KH_ID + */ + #ifdef _MSC_VER #pragma pack(pop) #endif /* _MSC_VER */ @@ -116,6 +144,7 @@ struct wpa_authenticator; struct wpa_state_machine; struct rsn_pmksa_cache_entry; struct eapol_state_machine; +struct ft_remote_seq; struct ft_remote_r0kh { @@ -123,7 +152,8 @@ struct ft_remote_r0kh { u8 addr[ETH_ALEN]; u8 id[FT_R0KH_ID_MAX_LEN]; size_t id_len; - u8 key[16]; + u8 key[32]; + struct ft_remote_seq *seq; }; @@ -131,7 +161,8 @@ struct ft_remote_r1kh { struct ft_remote_r1kh *next; u8 addr[ETH_ALEN]; u8 id[FT_R1KH_ID_LEN]; - u8 key[16]; + u8 key[32]; + struct ft_remote_seq *seq; }; @@ -144,10 +175,12 @@ struct wpa_auth_config { int wpa_strict_rekey; int wpa_gmk_rekey; int wpa_ptk_rekey; + u32 wpa_group_update_count; + u32 wpa_pairwise_update_count; + int wpa_disable_eapol_key_retries; int rsn_pairwise; int rsn_preauth; int eapol_version; - int peerkey; int wmm_enabled; int wmm_uapsd; int disable_pmksa_caching; @@ -156,21 +189,28 @@ struct wpa_auth_config { #ifdef CONFIG_IEEE80211W enum mfp_options ieee80211w; int group_mgmt_cipher; + int sae_require_mfp; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP u8 ssid[SSID_MAX_LEN]; size_t ssid_len; u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 r0_key_holder[FT_R0KH_ID_MAX_LEN]; size_t r0_key_holder_len; u8 r1_key_holder[FT_R1KH_ID_LEN]; - u32 r0_key_lifetime; + u32 r0_key_lifetime; /* PMK-R0 lifetime seconds */ + int rkh_pos_timeout; + int rkh_neg_timeout; + int rkh_pull_timeout; /* ms */ + int rkh_pull_retries; + int r1_max_key_lifetime; u32 reassociation_deadline; - struct ft_remote_r0kh *r0kh_list; - struct ft_remote_r1kh *r1kh_list; + struct ft_remote_r0kh **r0kh_list; + struct ft_remote_r1kh **r1kh_list; int pmk_r1_push; int ft_over_ds; -#endif /* CONFIG_IEEE80211R */ + int ft_psk_generate_local; +#endif /* CONFIG_IEEE80211R_AP */ int disable_gtk; int ap_mlme; #ifdef CONFIG_TESTING_OPTIONS @@ -184,6 +224,10 @@ struct wpa_auth_config { u8 ip_addr_start[4]; u8 ip_addr_end[4]; #endif /* CONFIG_P2P */ +#ifdef CONFIG_FILS + unsigned int fils_cache_id_set:1; + u8 fils_cache_id[FILS_CACHE_ID_LEN]; +#endif /* CONFIG_FILS */ }; typedef enum { @@ -197,7 +241,6 @@ typedef enum { } wpa_eapol_variable; struct wpa_auth_callbacks { - void *ctx; void (*logger)(void *ctx, const u8 *addr, logger_level level, const char *txt); void (*disconnect)(void *ctx, const u8 *addr, u16 reason); @@ -207,7 +250,7 @@ struct wpa_auth_callbacks { int value); int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var); const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, - const u8 *prev_psk); + const u8 *prev_psk, size_t *psk_len); int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len); int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len); @@ -220,13 +263,29 @@ struct wpa_auth_callbacks { void *ctx), void *cb_ctx); int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data, size_t data_len); -#ifdef CONFIG_IEEE80211R + int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data, + size_t data_len); +#ifdef CONFIG_IEEE80211R_AP struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); + int (*set_vlan)(void *ctx, const u8 *sta_addr, + struct vlan_description *vlan); + int (*get_vlan)(void *ctx, const u8 *sta_addr, + struct vlan_description *vlan); + int (*set_identity)(void *ctx, const u8 *sta_addr, + const u8 *identity, size_t identity_len); + size_t (*get_identity)(void *ctx, const u8 *sta_addr, const u8 **buf); + int (*set_radius_cui)(void *ctx, const u8 *sta_addr, + const u8 *radius_cui, size_t radius_cui_len); + size_t (*get_radius_cui)(void *ctx, const u8 *sta_addr, const u8 **buf); + void (*set_session_timeout)(void *ctx, const u8 *sta_addr, + int session_timeout); + int (*get_session_timeout)(void *ctx, const u8 *sta_addr); + int (*send_ft_action)(void *ctx, const u8 *dst, const u8 *data, size_t data_len); int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie, size_t tspec_ielen); -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_MESH int (*start_ampe)(void *ctx, const u8 *sta_addr); #endif /* CONFIG_MESH */ @@ -234,7 +293,8 @@ struct wpa_auth_callbacks { struct wpa_authenticator * wpa_init(const u8 *addr, struct wpa_auth_config *conf, - struct wpa_auth_callbacks *cb); + const struct wpa_auth_callbacks *cb, + void *cb_ctx); int wpa_init_keys(struct wpa_authenticator *wpa_auth); void wpa_deinit(struct wpa_authenticator *wpa_auth); int wpa_reconfig(struct wpa_authenticator *wpa_auth, @@ -244,13 +304,14 @@ enum { WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE, WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL, WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER, - WPA_INVALID_MDIE, WPA_INVALID_PROTO + WPA_INVALID_MDIE, WPA_INVALID_PROTO, WPA_INVALID_PMKID }; - + int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, const u8 *wpa_ie, size_t wpa_ie_len, - const u8 *mdie, size_t mdie_len); + const u8 *mdie, size_t mdie_len, + const u8 *owe_dh, size_t owe_dh_len); int wpa_validate_osen(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, const u8 *osen_ie, size_t osen_ie_len); @@ -267,7 +328,7 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, u8 *data, size_t data_len); enum wpa_event { WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH, - WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_DRV_STA_REMOVED + WPA_REAUTH_EAPOL, WPA_ASSOC_FT, WPA_ASSOC_FILS, WPA_DRV_STA_REMOVED }; void wpa_remove_ptk(struct wpa_state_machine *sm); int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event); @@ -281,6 +342,7 @@ int wpa_auth_get_pairwise(struct wpa_state_machine *sm); int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm); int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm); int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm); +int wpa_auth_sta_fils_tk_already_set(struct wpa_state_machine *sm); int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, struct rsn_pmksa_cache_entry *entry); struct rsn_pmksa_cache_entry * @@ -297,13 +359,28 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, struct eapol_state_machine *eapol); int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr, const u8 *pmk, const u8 *pmkid); +void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid); +int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *pmk, size_t pmk_len, const u8 *pmkid, + int session_timeout, int akmp); void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth, const u8 *sta_addr); int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf, size_t len); void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth); +int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr, + char *buf, size_t len); +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk, + const u8 *pmkid, int expiration); +int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth, + struct rsn_pmksa_cache_entry *entry); +struct rsn_pmksa_cache_entry * +wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + const u8 *pmkid); struct rsn_pmksa_cache_entry * -wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr); +wpa_auth_pmksa_get_fils_cache_id(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, const u8 *pmkid); void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa, struct wpa_state_machine *sm, struct wpa_authenticator *wpa_auth, @@ -312,7 +389,7 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, int ack); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, size_t max_len, int auth_alg, const u8 *req_ies, size_t req_ies_len); @@ -327,8 +404,13 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len); int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, const u8 *data, size_t data_len); +void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, + const u8 *dst_addr, u8 oui_suffix, const u8 *data, + size_t data_len); void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr); -#endif /* CONFIG_IEEE80211R */ +void wpa_ft_deinit(struct wpa_authenticator *wpa_auth); +void wpa_ft_sta_deinit(struct wpa_state_machine *sm); +#endif /* CONFIG_IEEE80211R_AP */ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm); void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag); @@ -347,5 +429,44 @@ void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth); int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id); int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id); +int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk, + size_t pmk_len, const u8 *snonce, const u8 *anonce, + const u8 *dhss, size_t dhss_len, + struct wpabuf *g_sta, struct wpabuf *g_ap); +int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session, + const struct ieee80211_mgmt *mgmt, size_t frame_len, + u8 *pos, size_t left); +int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf, + size_t current_len, size_t max_len, + const struct wpabuf *hlp); +int fils_set_tk(struct wpa_state_machine *sm); +u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *eid, + const u8 *fils_session, + struct wpabuf *fils_hlp_resp); +const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm, + const u8 *ies, size_t ies_len, + const u8 *fils_session); +int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies, + size_t ies_len); + +int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384, + u8 *buf, size_t len); +void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm, + u8 *fils_anonce, u8 *fils_snonce, + u8 *fils_kek, size_t *fils_kek_len); +u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, + u8 *pos, size_t max_len, + const u8 *req_ies, size_t req_ies_len); + +int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce, + void (*cb)(void *ctx1, void *ctx2), + void *ctx1, void *ctx2); +int wpa_auth_resend_m3(struct wpa_state_machine *sm, + void (*cb)(void *ctx1, void *ctx2), + void *ctx1, void *ctx2); +int wpa_auth_resend_group_m1(struct wpa_state_machine *sm, + void (*cb)(void *ctx1, void *ctx2), + void *ctx1, void *ctx2); +int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth); #endif /* WPA_AUTH_H */ diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c index e63b99ad20345..f6792e00f32d8 100644 --- a/src/ap/wpa_auth_ft.c +++ b/src/ap/wpa_auth_ft.c @@ -1,6 +1,6 @@ /* * hostapd - IEEE 802.11r - Fast BSS Transition - * Copyright (c) 2004-2015, 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. @@ -13,7 +13,10 @@ #include "utils/list.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "crypto/aes.h" +#include "crypto/aes_siv.h" #include "crypto/aes_wrap.h" +#include "crypto/sha384.h" #include "crypto/random.h" #include "ap_config.h" #include "ieee802_11.h" @@ -22,41 +25,692 @@ #include "wpa_auth_i.h" -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP + +const unsigned int ftRRBseqTimeout = 10; +const unsigned int ftRRBmaxQueueLen = 100; + static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm, const u8 *current_ap, const u8 *sta_addr, u16 status, const u8 *resp_ies, size_t resp_ies_len); +static void ft_finish_pull(struct wpa_state_machine *sm); +static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx); +static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx); + +struct tlv_list { + u16 type; + size_t len; + const u8 *data; +}; + + +/** + * wpa_ft_rrb_decrypt - Decrypt FT RRB message + * @key: AES-SIV key for AEAD + * @key_len: Length of key in octets + * @enc: Pointer to encrypted TLVs + * @enc_len: Length of encrypted TLVs in octets + * @auth: Pointer to authenticated TLVs + * @auth_len: Length of authenticated TLVs in octets + * @src_addr: MAC address of the frame sender + * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*) + * @plain: Pointer to return the pointer to the allocated plaintext buffer; + * needs to be freed by the caller if not NULL; + * will only be returned on success + * @plain_len: Pointer to return the length of the allocated plaintext buffer + * in octets + * Returns: 0 on success, -1 on error + */ +static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len, + const u8 *enc, const size_t enc_len, + const u8 *auth, const size_t auth_len, + const u8 *src_addr, u8 type, + u8 **plain, size_t *plain_size) +{ + const u8 *ad[3] = { src_addr, auth, &type }; + size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) }; + + wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypt using key", key, key_len); + + if (!key) { /* skip decryption */ + *plain = os_memdup(enc, enc_len); + if (enc_len > 0 && !*plain) + goto err; + + *plain_size = enc_len; + + return 0; + } + + *plain = NULL; + + /* SIV overhead */ + if (enc_len < AES_BLOCK_SIZE) + goto err; + + *plain = os_zalloc(enc_len - AES_BLOCK_SIZE); + if (!*plain) + goto err; + + if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len, + *plain) < 0) + goto err; + + *plain_size = enc_len - AES_BLOCK_SIZE; + wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypted TLVs", + *plain, *plain_size); + return 0; +err: + os_free(*plain); + *plain = NULL; + *plain_size = 0; + + wpa_printf(MSG_ERROR, "FT(RRB): Failed to decrypt"); + + return -1; +} + + +/* get first tlv record in packet matching type + * @data (decrypted) packet + * @return 0 on success else -1 + */ +static int wpa_ft_rrb_get_tlv(const u8 *plain, size_t plain_len, + u16 type, size_t *tlv_len, const u8 **tlv_data) +{ + const struct ft_rrb_tlv *f; + size_t left; + le16 type16; + size_t len; + + left = plain_len; + type16 = host_to_le16(type); + + while (left >= sizeof(*f)) { + f = (const struct ft_rrb_tlv *) plain; + + left -= sizeof(*f); + plain += sizeof(*f); + len = le_to_host16(f->len); + + if (left < len) { + wpa_printf(MSG_DEBUG, "FT: RRB message truncated"); + break; + } + + if (f->type == type16) { + *tlv_len = len; + *tlv_data = plain; + return 0; + } + + left -= len; + plain += len; + } + + return -1; +} + + +static void wpa_ft_rrb_dump(const u8 *plain, const size_t plain_len) +{ + const struct ft_rrb_tlv *f; + size_t left; + size_t len; + + left = plain_len; + + wpa_printf(MSG_DEBUG, "FT: RRB dump message"); + while (left >= sizeof(*f)) { + f = (const struct ft_rrb_tlv *) plain; + + left -= sizeof(*f); + plain += sizeof(*f); + len = le_to_host16(f->len); + + wpa_printf(MSG_DEBUG, "FT: RRB TLV type = %d, len = %zu", + le_to_host16(f->type), len); + + if (left < len) { + wpa_printf(MSG_DEBUG, + "FT: RRB message truncated: left %zu bytes, need %zu", + left, len); + break; + } + + wpa_hexdump(MSG_DEBUG, "FT: RRB TLV data", plain, len); + + left -= len; + plain += len; + } + + if (left > 0) + wpa_hexdump(MSG_DEBUG, "FT: RRB TLV padding", plain, left); + + wpa_printf(MSG_DEBUG, "FT: RRB dump message end"); +} + + +static int cmp_int(const void *a, const void *b) +{ + int x, y; + + x = *((int *) a); + y = *((int *) b); + return x - y; +} + + +static int wpa_ft_rrb_get_tlv_vlan(const u8 *plain, const size_t plain_len, + struct vlan_description *vlan) +{ + struct ft_rrb_tlv *f; + size_t left; + size_t len; + int taggedidx; + int vlan_id; + int type; + + left = plain_len; + taggedidx = 0; + os_memset(vlan, 0, sizeof(*vlan)); + + while (left >= sizeof(*f)) { + f = (struct ft_rrb_tlv *) plain; + + left -= sizeof(*f); + plain += sizeof(*f); + + len = le_to_host16(f->len); + type = le_to_host16(f->type); + + if (left < len) { + wpa_printf(MSG_DEBUG, "FT: RRB message truncated"); + return -1; + } + + if (type != FT_RRB_VLAN_UNTAGGED && type != FT_RRB_VLAN_TAGGED) + goto skip; + + if (type == FT_RRB_VLAN_UNTAGGED && len != sizeof(le16)) { + wpa_printf(MSG_DEBUG, + "FT: RRB VLAN_UNTAGGED invalid length"); + return -1; + } + + if (type == FT_RRB_VLAN_TAGGED && len % sizeof(le16) != 0) { + wpa_printf(MSG_DEBUG, + "FT: RRB VLAN_TAGGED invalid length"); + return -1; + } + + while (len >= sizeof(le16)) { + vlan_id = WPA_GET_LE16(plain); + plain += sizeof(le16); + left -= sizeof(le16); + len -= sizeof(le16); + + if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) { + wpa_printf(MSG_DEBUG, + "FT: RRB VLAN ID invalid %d", + vlan_id); + continue; + } + + if (type == FT_RRB_VLAN_UNTAGGED) + vlan->untagged = vlan_id; + + if (type == FT_RRB_VLAN_TAGGED && + taggedidx < MAX_NUM_TAGGED_VLAN) { + vlan->tagged[taggedidx] = vlan_id; + taggedidx++; + } else if (type == FT_RRB_VLAN_TAGGED) { + wpa_printf(MSG_DEBUG, "FT: RRB too many VLANs"); + } + } + + skip: + left -= len; + plain += len; + } + + if (taggedidx) + qsort(vlan->tagged, taggedidx, sizeof(int), cmp_int); + + vlan->notempty = vlan->untagged || vlan->tagged[0]; + + return 0; +} + + +static size_t wpa_ft_tlv_len(const struct tlv_list *tlvs) +{ + size_t tlv_len = 0; + int i; + + if (!tlvs) + return 0; + + for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) { + tlv_len += sizeof(struct ft_rrb_tlv); + tlv_len += tlvs[i].len; + } + + return tlv_len; +} + + +static size_t wpa_ft_tlv_lin(const struct tlv_list *tlvs, u8 *start, + u8 *endpos) +{ + int i; + size_t tlv_len; + struct ft_rrb_tlv *hdr; + u8 *pos; + + if (!tlvs) + return 0; + + tlv_len = 0; + pos = start; + for (i = 0; tlvs[i].type != FT_RRB_LAST_EMPTY; i++) { + if (tlv_len + sizeof(*hdr) > (size_t) (endpos - start)) + return tlv_len; + tlv_len += sizeof(*hdr); + hdr = (struct ft_rrb_tlv *) pos; + hdr->type = host_to_le16(tlvs[i].type); + hdr->len = host_to_le16(tlvs[i].len); + pos = start + tlv_len; + + if (tlv_len + tlvs[i].len > (size_t) (endpos - start)) + return tlv_len; + if (tlvs[i].len == 0) + continue; + tlv_len += tlvs[i].len; + os_memcpy(pos, tlvs[i].data, tlvs[i].len); + pos = start + tlv_len; + } + + return tlv_len; +} + + +static size_t wpa_ft_vlan_len(const struct vlan_description *vlan) +{ + size_t tlv_len = 0; + int i; + + if (!vlan || !vlan->notempty) + return 0; + + if (vlan->untagged) { + tlv_len += sizeof(struct ft_rrb_tlv); + tlv_len += sizeof(le16); + } + if (vlan->tagged[0]) + tlv_len += sizeof(struct ft_rrb_tlv); + for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++) + tlv_len += sizeof(le16); + + return tlv_len; +} + + +static size_t wpa_ft_vlan_lin(const struct vlan_description *vlan, + u8 *start, u8 *endpos) +{ + size_t tlv_len; + int i, len; + struct ft_rrb_tlv *hdr; + u8 *pos = start; + + if (!vlan || !vlan->notempty) + return 0; + + tlv_len = 0; + if (vlan->untagged) { + tlv_len += sizeof(*hdr); + if (start + tlv_len > endpos) + return tlv_len; + hdr = (struct ft_rrb_tlv *) pos; + hdr->type = host_to_le16(FT_RRB_VLAN_UNTAGGED); + hdr->len = host_to_le16(sizeof(le16)); + pos = start + tlv_len; + + tlv_len += sizeof(le16); + if (start + tlv_len > endpos) + return tlv_len; + WPA_PUT_LE16(pos, vlan->untagged); + pos = start + tlv_len; + } + + if (!vlan->tagged[0]) + return tlv_len; + + tlv_len += sizeof(*hdr); + if (start + tlv_len > endpos) + return tlv_len; + hdr = (struct ft_rrb_tlv *) pos; + hdr->type = host_to_le16(FT_RRB_VLAN_TAGGED); + len = 0; /* len is computed below */ + pos = start + tlv_len; + + for (i = 0; i < MAX_NUM_TAGGED_VLAN && vlan->tagged[i]; i++) { + tlv_len += sizeof(le16); + if (start + tlv_len > endpos) + break; + len += sizeof(le16); + WPA_PUT_LE16(pos, vlan->tagged[i]); + pos = start + tlv_len; + } + + hdr->len = host_to_le16(len); + + return tlv_len; +} +static int wpa_ft_rrb_lin(const struct tlv_list *tlvs1, + const struct tlv_list *tlvs2, + const struct vlan_description *vlan, + u8 **plain, size_t *plain_len) +{ + u8 *pos, *endpos; + size_t tlv_len; + + tlv_len = wpa_ft_tlv_len(tlvs1); + tlv_len += wpa_ft_tlv_len(tlvs2); + tlv_len += wpa_ft_vlan_len(vlan); + + *plain_len = tlv_len; + *plain = os_zalloc(tlv_len); + if (!*plain) { + wpa_printf(MSG_ERROR, "FT: Failed to allocate plaintext"); + goto err; + } + + pos = *plain; + endpos = *plain + tlv_len; + pos += wpa_ft_tlv_lin(tlvs1, pos, endpos); + pos += wpa_ft_tlv_lin(tlvs2, pos, endpos); + pos += wpa_ft_vlan_lin(vlan, pos, endpos); + + /* sanity check */ + if (pos != endpos) { + wpa_printf(MSG_ERROR, "FT: Length error building RRB"); + goto err; + } + + return 0; + +err: + os_free(*plain); + *plain = NULL; + *plain_len = 0; + return -1; +} + + +static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len, + const u8 *plain, const size_t plain_len, + const u8 *auth, const size_t auth_len, + const u8 *src_addr, u8 type, u8 *enc) +{ + const u8 *ad[3] = { src_addr, auth, &type }; + size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) }; + + wpa_hexdump_key(MSG_DEBUG, "FT(RRB): plaintext message", + plain, plain_len); + wpa_hexdump_key(MSG_DEBUG, "FT(RRB): encrypt using key", key, key_len); + + if (!key) { + /* encryption not needed, return plaintext as packet */ + os_memcpy(enc, plain, plain_len); + } else if (aes_siv_encrypt(key, key_len, plain, plain_len, + 3, ad, ad_len, enc) < 0) { + wpa_printf(MSG_ERROR, "FT: Failed to encrypt RRB-OUI message"); + return -1; + } + + return 0; +} + + +/** + * wpa_ft_rrb_build - Build and encrypt an FT RRB message + * @key: AES-SIV key for AEAD + * @key_len: Length of key in octets + * @tlvs_enc0: First set of to-be-encrypted TLVs + * @tlvs_enc1: Second set of to-be-encrypted TLVs + * @tlvs_auth: Set of to-be-authenticated TLVs + * @src_addr: MAC address of the frame sender + * @type: Vendor-specific subtype of the RRB frame (FT_PACKET_*) + * @packet Pointer to return the pointer to the allocated packet buffer; + * needs to be freed by the caller if not null; + * will only be returned on success + * @packet_len: Pointer to return the length of the allocated buffer in octets + * Returns: 0 on success, -1 on error + */ +static int wpa_ft_rrb_build(const u8 *key, const size_t key_len, + const struct tlv_list *tlvs_enc0, + const struct tlv_list *tlvs_enc1, + const struct tlv_list *tlvs_auth, + const struct vlan_description *vlan, + const u8 *src_addr, u8 type, + u8 **packet, size_t *packet_len) +{ + u8 *plain = NULL, *auth = NULL, *pos; + size_t plain_len = 0, auth_len = 0; + int ret = -1; + + *packet = NULL; + if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, vlan, &plain, &plain_len) < 0) + goto out; + + if (wpa_ft_rrb_lin(tlvs_auth, NULL, NULL, &auth, &auth_len) < 0) + goto out; + + *packet_len = sizeof(u16) + auth_len + plain_len; + if (key) + *packet_len += AES_BLOCK_SIZE; + *packet = os_zalloc(*packet_len); + if (!*packet) + goto out; + + pos = *packet; + WPA_PUT_LE16(pos, auth_len); + pos += 2; + os_memcpy(pos, auth, auth_len); + pos += auth_len; + if (wpa_ft_rrb_encrypt(key, key_len, plain, plain_len, auth, + auth_len, src_addr, type, pos) < 0) + goto out; + + ret = 0; + +out: + bin_clear_free(plain, plain_len); + os_free(auth); + + if (ret) { + wpa_printf(MSG_ERROR, "FT: Failed to build RRB-OUI message"); + os_free(*packet); + *packet = NULL; + *packet_len = 0; + } + + return ret; +} + + +#define RRB_GET_SRC(srcfield, type, field, txt, checklength) do { \ + if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \ + &f_##field##_len, &f_##field) < 0 || \ + (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \ + wpa_printf(MSG_INFO, "FT: Missing required " #field \ + " in %s from " MACSTR, txt, MAC2STR(src_addr)); \ + wpa_ft_rrb_dump(srcfield, srcfield##_len); \ + goto out; \ + } \ +} while (0) + +#define RRB_GET(type, field, txt, checklength) \ + RRB_GET_SRC(plain, type, field, txt, checklength) +#define RRB_GET_AUTH(type, field, txt, checklength) \ + RRB_GET_SRC(auth, type, field, txt, checklength) + +#define RRB_GET_OPTIONAL_SRC(srcfield, type, field, txt, checklength) do { \ + if (wpa_ft_rrb_get_tlv(srcfield, srcfield##_len, type, \ + &f_##field##_len, &f_##field) < 0 || \ + (checklength > 0 && ((size_t) checklength) != f_##field##_len)) { \ + wpa_printf(MSG_DEBUG, "FT: Missing optional " #field \ + " in %s from " MACSTR, txt, MAC2STR(src_addr)); \ + f_##field##_len = 0; \ + f_##field = NULL; \ + } \ +} while (0) + +#define RRB_GET_OPTIONAL(type, field, txt, checklength) \ + RRB_GET_OPTIONAL_SRC(plain, type, field, txt, checklength) +#define RRB_GET_OPTIONAL_AUTH(type, field, txt, checklength) \ + RRB_GET_OPTIONAL_SRC(auth, type, field, txt, checklength) + static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst, const u8 *data, size_t data_len) { - if (wpa_auth->cb.send_ether == NULL) + if (wpa_auth->cb->send_ether == NULL) return -1; wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst)); - return wpa_auth->cb.send_ether(wpa_auth->cb.ctx, dst, ETH_P_RRB, - data, data_len); + return wpa_auth->cb->send_ether(wpa_auth->cb_ctx, dst, ETH_P_RRB, + data, data_len); +} + + +static int wpa_ft_rrb_oui_send(struct wpa_authenticator *wpa_auth, + const u8 *dst, u8 oui_suffix, + const u8 *data, size_t data_len) +{ + if (!wpa_auth->cb->send_oui) + return -1; + wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR, + oui_suffix, MAC2STR(dst)); + return wpa_auth->cb->send_oui(wpa_auth->cb_ctx, dst, oui_suffix, data, + data_len); } static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth, const u8 *dst, const u8 *data, size_t data_len) { - if (wpa_auth->cb.send_ft_action == NULL) + if (wpa_auth->cb->send_ft_action == NULL) return -1; - return wpa_auth->cb.send_ft_action(wpa_auth->cb.ctx, dst, - data, data_len); + return wpa_auth->cb->send_ft_action(wpa_auth->cb_ctx, dst, + data, data_len); +} + + +static const u8 * wpa_ft_get_psk(struct wpa_authenticator *wpa_auth, + const u8 *addr, const u8 *p2p_dev_addr, + const u8 *prev_psk) +{ + if (wpa_auth->cb->get_psk == NULL) + return NULL; + return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr, + prev_psk, NULL); } static struct wpa_state_machine * wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) { - if (wpa_auth->cb.add_sta == NULL) + if (wpa_auth->cb->add_sta == NULL) return NULL; - return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr); + return wpa_auth->cb->add_sta(wpa_auth->cb_ctx, sta_addr); +} + + +static int wpa_ft_set_vlan(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, struct vlan_description *vlan) +{ + if (!wpa_auth->cb->set_vlan) + return -1; + return wpa_auth->cb->set_vlan(wpa_auth->cb_ctx, sta_addr, vlan); +} + + +static int wpa_ft_get_vlan(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, struct vlan_description *vlan) +{ + if (!wpa_auth->cb->get_vlan) + return -1; + return wpa_auth->cb->get_vlan(wpa_auth->cb_ctx, sta_addr, vlan); +} + + +static int +wpa_ft_set_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + const u8 *identity, size_t identity_len) +{ + if (!wpa_auth->cb->set_identity) + return -1; + return wpa_auth->cb->set_identity(wpa_auth->cb_ctx, sta_addr, identity, + identity_len); +} + + +static size_t +wpa_ft_get_identity(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + const u8 **buf) +{ + *buf = NULL; + if (!wpa_auth->cb->get_identity) + return 0; + return wpa_auth->cb->get_identity(wpa_auth->cb_ctx, sta_addr, buf); +} + + +static int +wpa_ft_set_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + const u8 *radius_cui, size_t radius_cui_len) +{ + if (!wpa_auth->cb->set_radius_cui) + return -1; + return wpa_auth->cb->set_radius_cui(wpa_auth->cb_ctx, sta_addr, + radius_cui, radius_cui_len); +} + + +static size_t +wpa_ft_get_radius_cui(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, + const u8 **buf) +{ + *buf = NULL; + if (!wpa_auth->cb->get_radius_cui) + return 0; + return wpa_auth->cb->get_radius_cui(wpa_auth->cb_ctx, sta_addr, buf); +} + + +static void +wpa_ft_set_session_timeout(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr, int session_timeout) +{ + if (!wpa_auth->cb->set_session_timeout) + return; + wpa_auth->cb->set_session_timeout(wpa_auth->cb_ctx, sta_addr, + session_timeout); +} + + +static int +wpa_ft_get_session_timeout(struct wpa_authenticator *wpa_auth, + const u8 *sta_addr) +{ + if (!wpa_auth->cb->get_session_timeout) + return 0; + return wpa_auth->cb->get_session_timeout(wpa_auth->cb_ctx, sta_addr); } @@ -64,12 +718,12 @@ static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth, const u8 *sta_addr, u8 *tspec_ie, size_t tspec_ielen) { - if (wpa_auth->cb.add_tspec == NULL) { + if (wpa_auth->cb->add_tspec == NULL) { wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized"); return -1; } - return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie, - tspec_ielen); + return wpa_auth->cb->add_tspec(wpa_auth->cb_ctx, sta_addr, tspec_ie, + tspec_ielen); } @@ -93,30 +747,44 @@ int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len) } -int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, - size_t r0kh_id_len, +int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384, + const u8 *r0kh_id, size_t r0kh_id_len, const u8 *anonce, const u8 *snonce, u8 *buf, size_t len, const u8 *subelem, size_t subelem_len) { u8 *pos = buf, *ielen; - struct rsn_ftie *hdr; + size_t hdrlen = use_sha384 ? sizeof(struct rsn_ftie_sha384) : + sizeof(struct rsn_ftie); - if (len < 2 + sizeof(*hdr) + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len + + if (len < 2 + hdrlen + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len + subelem_len) return -1; *pos++ = WLAN_EID_FAST_BSS_TRANSITION; ielen = pos++; - hdr = (struct rsn_ftie *) pos; - os_memset(hdr, 0, sizeof(*hdr)); - pos += sizeof(*hdr); - WPA_PUT_LE16(hdr->mic_control, 0); - if (anonce) - os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN); - if (snonce) - os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN); + if (use_sha384) { + struct rsn_ftie_sha384 *hdr = (struct rsn_ftie_sha384 *) pos; + + os_memset(hdr, 0, sizeof(*hdr)); + pos += sizeof(*hdr); + WPA_PUT_LE16(hdr->mic_control, 0); + if (anonce) + os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN); + if (snonce) + os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN); + } else { + struct rsn_ftie *hdr = (struct rsn_ftie *) pos; + + os_memset(hdr, 0, sizeof(*hdr)); + pos += sizeof(*hdr); + WPA_PUT_LE16(hdr->mic_control, 0); + if (anonce) + os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN); + if (snonce) + os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN); + } /* Optional Parameters */ *pos++ = FTIE_SUBELEM_R1KH_ID; @@ -142,35 +810,432 @@ int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, } +/* A packet to be handled after seq response */ +struct ft_remote_item { + struct dl_list list; + + u8 nonce[FT_RRB_NONCE_LEN]; + struct os_reltime nonce_ts; + + u8 src_addr[ETH_ALEN]; + u8 *enc; + size_t enc_len; + u8 *auth; + size_t auth_len; + int (*cb)(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer); +}; + + +static void wpa_ft_rrb_seq_free(struct ft_remote_item *item) +{ + eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, ELOOP_ALL_CTX, item); + dl_list_del(&item->list); + bin_clear_free(item->enc, item->enc_len); + os_free(item->auth); + os_free(item); +} + + +static void wpa_ft_rrb_seq_flush(struct wpa_authenticator *wpa_auth, + struct ft_remote_seq *rkh_seq, int cb) +{ + struct ft_remote_item *item, *n; + + dl_list_for_each_safe(item, n, &rkh_seq->rx.queue, + struct ft_remote_item, list) { + if (cb && item->cb) + item->cb(wpa_auth, item->src_addr, item->enc, + item->enc_len, item->auth, item->auth_len, 1); + wpa_ft_rrb_seq_free(item); + } +} + + +static void wpa_ft_rrb_seq_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct ft_remote_item *item = timeout_ctx; + + wpa_ft_rrb_seq_free(item); +} + + +static int +wpa_ft_rrb_seq_req(struct wpa_authenticator *wpa_auth, + struct ft_remote_seq *rkh_seq, const u8 *src_addr, + const u8 *f_r0kh_id, size_t f_r0kh_id_len, + const u8 *f_r1kh_id, const u8 *key, size_t key_len, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int (*cb)(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer)) +{ + struct ft_remote_item *item = NULL; + u8 *packet = NULL; + size_t packet_len; + struct tlv_list seq_req_auth[] = { + { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN, + .data = NULL /* to be filled: item->nonce */ }, + { .type = FT_RRB_R0KH_ID, .len = f_r0kh_id_len, + .data = f_r0kh_id }, + { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN, + .data = f_r1kh_id }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + + if (dl_list_len(&rkh_seq->rx.queue) >= ftRRBmaxQueueLen) { + wpa_printf(MSG_DEBUG, "FT: Sequence number queue too long"); + goto err; + } + + item = os_zalloc(sizeof(*item)); + if (!item) + goto err; + + os_memcpy(item->src_addr, src_addr, ETH_ALEN); + item->cb = cb; + + if (random_get_bytes(item->nonce, FT_RRB_NONCE_LEN) < 0) { + wpa_printf(MSG_DEBUG, "FT: Seq num nonce: out of random bytes"); + goto err; + } + + if (os_get_reltime(&item->nonce_ts) < 0) + goto err; + + if (enc && enc_len > 0) { + item->enc = os_memdup(enc, enc_len); + item->enc_len = enc_len; + if (!item->enc) + goto err; + } + + if (auth && auth_len > 0) { + item->auth = os_memdup(auth, auth_len); + item->auth_len = auth_len; + if (!item->auth) + goto err; + } + + eloop_register_timeout(ftRRBseqTimeout, 0, wpa_ft_rrb_seq_timeout, + wpa_auth, item); + + seq_req_auth[0].data = item->nonce; + + if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_req_auth, NULL, + wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_REQ, + &packet, &packet_len) < 0) { + item = NULL; /* some other seq resp might still accept this */ + goto err; + } + + dl_list_add(&rkh_seq->rx.queue, &item->list); + + wpa_ft_rrb_oui_send(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ, + packet, packet_len); + + os_free(packet); + + return 0; +err: + wpa_printf(MSG_DEBUG, "FT: Failed to send sequence number request"); + if (item) { + os_free(item->auth); + bin_clear_free(item->enc, item->enc_len); + os_free(item); + } + + return -1; +} + + +#define FT_RRB_SEQ_OK 0 +#define FT_RRB_SEQ_DROP 1 +#define FT_RRB_SEQ_DEFER 2 + +static int +wpa_ft_rrb_seq_chk(struct ft_remote_seq *rkh_seq, const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + const char *msgtype, int no_defer) +{ + const u8 *f_seq; + size_t f_seq_len; + const struct ft_rrb_seq *msg_both; + u32 msg_seq, msg_off, rkh_off; + struct os_reltime now; + unsigned int i; + + RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both)); + wpa_hexdump(MSG_DEBUG, "FT: sequence number", f_seq, f_seq_len); + msg_both = (const struct ft_rrb_seq *) f_seq; + + if (rkh_seq->rx.num_last == 0) { + /* first packet from remote */ + goto defer; + } + + if (le_to_host32(msg_both->dom) != rkh_seq->rx.dom) { + /* remote might have rebooted */ + goto defer; + } + + if (os_get_reltime(&now) == 0) { + u32 msg_ts_now_remote, msg_ts_off; + struct os_reltime now_remote; + + os_reltime_sub(&now, &rkh_seq->rx.time_offset, &now_remote); + msg_ts_now_remote = now_remote.sec; + msg_ts_off = le_to_host32(msg_both->ts) - + (msg_ts_now_remote - ftRRBseqTimeout); + if (msg_ts_off > 2 * ftRRBseqTimeout) + goto defer; + } + + msg_seq = le_to_host32(msg_both->seq); + rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx]; + msg_off = msg_seq - rkh_off; + if (msg_off > 0xC0000000) + goto out; /* too old message, drop it */ + + if (msg_off <= 0x40000000) { + for (i = 0; i < rkh_seq->rx.num_last; i++) { + if (rkh_seq->rx.last[i] == msg_seq) + goto out; /* duplicate message, drop it */ + } + + return FT_RRB_SEQ_OK; + } + +defer: + if (no_defer) + goto out; + + wpa_printf(MSG_DEBUG, "FT: Possibly invalid sequence number in %s from " + MACSTR, msgtype, MAC2STR(src_addr)); + + return FT_RRB_SEQ_DEFER; +out: + wpa_printf(MSG_DEBUG, "FT: Invalid sequence number in %s from " MACSTR, + msgtype, MAC2STR(src_addr)); + + return FT_RRB_SEQ_DROP; +} + + +static void +wpa_ft_rrb_seq_accept(struct wpa_authenticator *wpa_auth, + struct ft_remote_seq *rkh_seq, const u8 *src_addr, + const u8 *auth, size_t auth_len, + const char *msgtype) +{ + const u8 *f_seq; + size_t f_seq_len; + const struct ft_rrb_seq *msg_both; + u32 msg_seq, msg_off, min_off, rkh_off; + int minidx = 0; + unsigned int i; + + RRB_GET_AUTH(FT_RRB_SEQ, seq, msgtype, sizeof(*msg_both)); + msg_both = (const struct ft_rrb_seq *) f_seq; + + msg_seq = le_to_host32(msg_both->seq); + + if (rkh_seq->rx.num_last < FT_REMOTE_SEQ_BACKLOG) { + rkh_seq->rx.last[rkh_seq->rx.num_last] = msg_seq; + rkh_seq->rx.num_last++; + return; + } + + rkh_off = rkh_seq->rx.last[rkh_seq->rx.offsetidx]; + for (i = 0; i < rkh_seq->rx.num_last; i++) { + msg_off = rkh_seq->rx.last[i] - rkh_off; + min_off = rkh_seq->rx.last[minidx] - rkh_off; + if (msg_off < min_off && i != rkh_seq->rx.offsetidx) + minidx = i; + } + rkh_seq->rx.last[rkh_seq->rx.offsetidx] = msg_seq; + rkh_seq->rx.offsetidx = minidx; + + return; +out: + /* RRB_GET_AUTH should never fail here as + * wpa_ft_rrb_seq_chk() verified FT_RRB_SEQ presence. */ + wpa_printf(MSG_ERROR, "FT: %s() failed", __func__); +} + + +static int wpa_ft_new_seq(struct ft_remote_seq *rkh_seq, + struct ft_rrb_seq *f_seq) +{ + struct os_reltime now; + + if (os_get_reltime(&now) < 0) + return -1; + + if (!rkh_seq->tx.dom) { + if (random_get_bytes((u8 *) &rkh_seq->tx.seq, + sizeof(rkh_seq->tx.seq))) { + wpa_printf(MSG_ERROR, + "FT: Failed to get random data for sequence number initialization"); + rkh_seq->tx.seq = now.usec; + } + if (random_get_bytes((u8 *) &rkh_seq->tx.dom, + sizeof(rkh_seq->tx.dom))) { + wpa_printf(MSG_ERROR, + "FT: Failed to get random data for sequence number initialization"); + rkh_seq->tx.dom = now.usec; + } + rkh_seq->tx.dom |= 1; + } + + f_seq->dom = host_to_le32(rkh_seq->tx.dom); + f_seq->seq = host_to_le32(rkh_seq->tx.seq); + f_seq->ts = host_to_le32(now.sec); + + rkh_seq->tx.seq++; + + return 0; +} + + struct wpa_ft_pmk_r0_sa { - struct wpa_ft_pmk_r0_sa *next; - u8 pmk_r0[PMK_LEN]; + struct dl_list list; + u8 pmk_r0[PMK_LEN_MAX]; + size_t pmk_r0_len; u8 pmk_r0_name[WPA_PMK_NAME_LEN]; u8 spa[ETH_ALEN]; int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ - /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ + struct vlan_description *vlan; + os_time_t expiration; /* 0 for no expiration */ + u8 *identity; + size_t identity_len; + u8 *radius_cui; + size_t radius_cui_len; + os_time_t session_timeout; /* 0 for no expiration */ + /* TODO: radius_class, EAP type */ int pmk_r1_pushed; }; struct wpa_ft_pmk_r1_sa { - struct wpa_ft_pmk_r1_sa *next; - u8 pmk_r1[PMK_LEN]; + struct dl_list list; + u8 pmk_r1[PMK_LEN_MAX]; + size_t pmk_r1_len; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; u8 spa[ETH_ALEN]; int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ - /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ + struct vlan_description *vlan; + u8 *identity; + size_t identity_len; + u8 *radius_cui; + size_t radius_cui_len; + os_time_t session_timeout; /* 0 for no expiration */ + /* TODO: radius_class, EAP type */ }; struct wpa_ft_pmk_cache { - struct wpa_ft_pmk_r0_sa *pmk_r0; - struct wpa_ft_pmk_r1_sa *pmk_r1; + struct dl_list pmk_r0; /* struct wpa_ft_pmk_r0_sa */ + struct dl_list pmk_r1; /* struct wpa_ft_pmk_r1_sa */ }; + +static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx); +static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx); + + +static void wpa_ft_free_pmk_r0(struct wpa_ft_pmk_r0_sa *r0) +{ + if (!r0) + return; + + dl_list_del(&r0->list); + eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL); + + os_memset(r0->pmk_r0, 0, PMK_LEN_MAX); + os_free(r0->vlan); + os_free(r0->identity); + os_free(r0->radius_cui); + os_free(r0); +} + + +static void wpa_ft_expire_pmk_r0(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_ft_pmk_r0_sa *r0 = eloop_ctx; + struct os_reltime now; + int expires_in; + int session_timeout; + + os_get_reltime(&now); + + if (!r0) + return; + + expires_in = r0->expiration - now.sec; + session_timeout = r0->session_timeout - now.sec; + /* conditions to remove from cache: + * a) r0->expiration is set and hit + * -or- + * b) r0->session_timeout is set and hit + */ + if ((!r0->expiration || expires_in > 0) && + (!r0->session_timeout || session_timeout > 0)) { + wpa_printf(MSG_ERROR, + "FT: %s() called for non-expired entry %p", + __func__, r0); + eloop_cancel_timeout(wpa_ft_expire_pmk_r0, r0, NULL); + if (r0->expiration && expires_in > 0) + eloop_register_timeout(expires_in + 1, 0, + wpa_ft_expire_pmk_r0, r0, NULL); + if (r0->session_timeout && session_timeout > 0) + eloop_register_timeout(session_timeout + 1, 0, + wpa_ft_expire_pmk_r0, r0, NULL); + return; + } + + wpa_ft_free_pmk_r0(r0); +} + + +static void wpa_ft_free_pmk_r1(struct wpa_ft_pmk_r1_sa *r1) +{ + if (!r1) + return; + + dl_list_del(&r1->list); + eloop_cancel_timeout(wpa_ft_expire_pmk_r1, r1, NULL); + + os_memset(r1->pmk_r1, 0, PMK_LEN_MAX); + os_free(r1->vlan); + os_free(r1->identity); + os_free(r1->radius_cui); + os_free(r1); +} + + +static void wpa_ft_expire_pmk_r1(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_ft_pmk_r1_sa *r1 = eloop_ctx; + + wpa_ft_free_pmk_r1(r1); +} + + struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void) { struct wpa_ft_pmk_cache *cache; cache = os_zalloc(sizeof(*cache)); + if (cache) { + dl_list_init(&cache->pmk_r0); + dl_list_init(&cache->pmk_r1); + } return cache; } @@ -181,21 +1246,13 @@ void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache) struct wpa_ft_pmk_r0_sa *r0, *r0prev; struct wpa_ft_pmk_r1_sa *r1, *r1prev; - r0 = cache->pmk_r0; - while (r0) { - r0prev = r0; - r0 = r0->next; - os_memset(r0prev->pmk_r0, 0, PMK_LEN); - os_free(r0prev); - } + dl_list_for_each_safe(r0, r0prev, &cache->pmk_r0, + struct wpa_ft_pmk_r0_sa, list) + wpa_ft_free_pmk_r0(r0); - r1 = cache->pmk_r1; - while (r1) { - r1prev = r1; - r1 = r1->next; - os_memset(r1prev->pmk_r1, 0, PMK_LEN); - os_free(r1prev); - } + dl_list_for_each_safe(r1, r1prev, &cache->pmk_r1, + struct wpa_ft_pmk_r1_sa, list) + wpa_ft_free_pmk_r1(r1); os_free(cache); } @@ -203,24 +1260,63 @@ void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache) static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r0, - const u8 *pmk_r0_name, int pairwise) + size_t pmk_r0_len, + const u8 *pmk_r0_name, int pairwise, + const struct vlan_description *vlan, + int expires_in, int session_timeout, + const u8 *identity, size_t identity_len, + const u8 *radius_cui, size_t radius_cui_len) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r0_sa *r0; + struct os_reltime now; - /* TODO: add expiration and limit on number of entries in cache */ + /* TODO: add limit on number of entries in cache */ + os_get_reltime(&now); r0 = os_zalloc(sizeof(*r0)); if (r0 == NULL) return -1; - os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN); + os_memcpy(r0->pmk_r0, pmk_r0, pmk_r0_len); + r0->pmk_r0_len = pmk_r0_len; os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); os_memcpy(r0->spa, spa, ETH_ALEN); r0->pairwise = pairwise; + if (expires_in > 0) + r0->expiration = now.sec + expires_in; + if (vlan && vlan->notempty) { + r0->vlan = os_zalloc(sizeof(*vlan)); + if (!r0->vlan) { + bin_clear_free(r0, sizeof(*r0)); + return -1; + } + *r0->vlan = *vlan; + } + if (identity) { + r0->identity = os_malloc(identity_len); + if (r0->identity) { + os_memcpy(r0->identity, identity, identity_len); + r0->identity_len = identity_len; + } + } + if (radius_cui) { + r0->radius_cui = os_malloc(radius_cui_len); + if (r0->radius_cui) { + os_memcpy(r0->radius_cui, radius_cui, radius_cui_len); + r0->radius_cui_len = radius_cui_len; + } + } + if (session_timeout > 0) + r0->session_timeout = now.sec + session_timeout; - r0->next = cache->pmk_r0; - cache->pmk_r0 = r0; + dl_list_add(&cache->pmk_r0, &r0->list); + if (expires_in > 0) + eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r0, + r0, NULL); + if (session_timeout > 0) + eloop_register_timeout(session_timeout + 1, 0, + wpa_ft_expire_pmk_r0, r0, NULL); return 0; } @@ -228,49 +1324,89 @@ static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth, static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r0_name, - u8 *pmk_r0, int *pairwise) + const struct wpa_ft_pmk_r0_sa **r0_out) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r0_sa *r0; + struct os_reltime now; - r0 = cache->pmk_r0; - while (r0) { + os_get_reltime(&now); + dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) { if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 && os_memcmp_const(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN) == 0) { - os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN); - if (pairwise) - *pairwise = r0->pairwise; + *r0_out = r0; return 0; } - - r0 = r0->next; } + *r0_out = NULL; return -1; } static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r1, - const u8 *pmk_r1_name, int pairwise) + size_t pmk_r1_len, + const u8 *pmk_r1_name, int pairwise, + const struct vlan_description *vlan, + int expires_in, int session_timeout, + const u8 *identity, size_t identity_len, + const u8 *radius_cui, size_t radius_cui_len) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + int max_expires_in = wpa_auth->conf.r1_max_key_lifetime; struct wpa_ft_pmk_r1_sa *r1; + struct os_reltime now; - /* TODO: add expiration and limit on number of entries in cache */ + /* TODO: limit on number of entries in cache */ + os_get_reltime(&now); + + if (max_expires_in && (max_expires_in < expires_in || expires_in == 0)) + expires_in = max_expires_in; r1 = os_zalloc(sizeof(*r1)); if (r1 == NULL) return -1; - os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN); + os_memcpy(r1->pmk_r1, pmk_r1, pmk_r1_len); + r1->pmk_r1_len = pmk_r1_len; os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); os_memcpy(r1->spa, spa, ETH_ALEN); r1->pairwise = pairwise; + if (vlan && vlan->notempty) { + r1->vlan = os_zalloc(sizeof(*vlan)); + if (!r1->vlan) { + bin_clear_free(r1, sizeof(*r1)); + return -1; + } + *r1->vlan = *vlan; + } + if (identity) { + r1->identity = os_malloc(identity_len); + if (r1->identity) { + os_memcpy(r1->identity, identity, identity_len); + r1->identity_len = identity_len; + } + } + if (radius_cui) { + r1->radius_cui = os_malloc(radius_cui_len); + if (r1->radius_cui) { + os_memcpy(r1->radius_cui, radius_cui, radius_cui_len); + r1->radius_cui_len = radius_cui_len; + } + } + if (session_timeout > 0) + r1->session_timeout = now.sec + session_timeout; + + dl_list_add(&cache->pmk_r1, &r1->list); - r1->next = cache->pmk_r1; - cache->pmk_r1 = r1; + if (expires_in > 0) + eloop_register_timeout(expires_in + 1, 0, wpa_ft_expire_pmk_r1, + r1, NULL); + if (session_timeout > 0) + eloop_register_timeout(session_timeout + 1, 0, + wpa_ft_expire_pmk_r1, r1, NULL); return 0; } @@ -278,94 +1414,616 @@ static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth, static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *spa, const u8 *pmk_r1_name, - u8 *pmk_r1, int *pairwise) + u8 *pmk_r1, size_t *pmk_r1_len, int *pairwise, + struct vlan_description *vlan, + const u8 **identity, size_t *identity_len, + const u8 **radius_cui, size_t *radius_cui_len, + int *session_timeout) { struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; struct wpa_ft_pmk_r1_sa *r1; + struct os_reltime now; - r1 = cache->pmk_r1; - while (r1) { + os_get_reltime(&now); + + dl_list_for_each(r1, &cache->pmk_r1, struct wpa_ft_pmk_r1_sa, list) { if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 && os_memcmp_const(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN) == 0) { - os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN); + os_memcpy(pmk_r1, r1->pmk_r1, r1->pmk_r1_len); + *pmk_r1_len = r1->pmk_r1_len; if (pairwise) *pairwise = r1->pairwise; + if (vlan && r1->vlan) + *vlan = *r1->vlan; + if (vlan && !r1->vlan) + os_memset(vlan, 0, sizeof(*vlan)); + if (identity && identity_len) { + *identity = r1->identity; + *identity_len = r1->identity_len; + } + if (radius_cui && radius_cui_len) { + *radius_cui = r1->radius_cui; + *radius_cui_len = r1->radius_cui_len; + } + if (session_timeout && r1->session_timeout > now.sec) + *session_timeout = r1->session_timeout - + now.sec; + else if (session_timeout && r1->session_timeout) + *session_timeout = 1; + else if (session_timeout) + *session_timeout = 0; return 0; } - - r1 = r1->next; } return -1; } -static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, - const u8 *ies, size_t ies_len, - const u8 *pmk_r0_name) +static int wpa_ft_rrb_init_r0kh_seq(struct ft_remote_r0kh *r0kh) +{ + if (r0kh->seq) + return 0; + + r0kh->seq = os_zalloc(sizeof(*r0kh->seq)); + if (!r0kh->seq) { + wpa_printf(MSG_DEBUG, "FT: Failed to allocate r0kh->seq"); + return -1; + } + + dl_list_init(&r0kh->seq->rx.queue); + + return 0; +} + + +static void wpa_ft_rrb_lookup_r0kh(struct wpa_authenticator *wpa_auth, + const u8 *f_r0kh_id, size_t f_r0kh_id_len, + struct ft_remote_r0kh **r0kh_out, + struct ft_remote_r0kh **r0kh_wildcard) { struct ft_remote_r0kh *r0kh; - struct ft_r0kh_r1kh_pull_frame frame, f; - r0kh = sm->wpa_auth->conf.r0kh_list; - while (r0kh) { - if (r0kh->id_len == sm->r0kh_id_len && - os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) == - 0) + *r0kh_wildcard = NULL; + *r0kh_out = NULL; + + if (wpa_auth->conf.r0kh_list) + r0kh = *wpa_auth->conf.r0kh_list; + else + r0kh = NULL; + for (; r0kh; r0kh = r0kh->next) { + if (r0kh->id_len == 1 && r0kh->id[0] == '*') + *r0kh_wildcard = r0kh; + if (f_r0kh_id && r0kh->id_len == f_r0kh_id_len && + os_memcmp_const(f_r0kh_id, r0kh->id, f_r0kh_id_len) == 0) + *r0kh_out = r0kh; + } + + if (!*r0kh_out && !*r0kh_wildcard) + wpa_printf(MSG_DEBUG, "FT: No matching R0KH found"); + + if (*r0kh_out && wpa_ft_rrb_init_r0kh_seq(*r0kh_out) < 0) + *r0kh_out = NULL; +} + + +static int wpa_ft_rrb_init_r1kh_seq(struct ft_remote_r1kh *r1kh) +{ + if (r1kh->seq) + return 0; + + r1kh->seq = os_zalloc(sizeof(*r1kh->seq)); + if (!r1kh->seq) { + wpa_printf(MSG_DEBUG, "FT: Failed to allocate r1kh->seq"); + return -1; + } + + dl_list_init(&r1kh->seq->rx.queue); + + return 0; +} + + +static void wpa_ft_rrb_lookup_r1kh(struct wpa_authenticator *wpa_auth, + const u8 *f_r1kh_id, + struct ft_remote_r1kh **r1kh_out, + struct ft_remote_r1kh **r1kh_wildcard) +{ + struct ft_remote_r1kh *r1kh; + + *r1kh_wildcard = NULL; + *r1kh_out = NULL; + + if (wpa_auth->conf.r1kh_list) + r1kh = *wpa_auth->conf.r1kh_list; + else + r1kh = NULL; + for (; r1kh; r1kh = r1kh->next) { + if (is_zero_ether_addr(r1kh->addr) && + is_zero_ether_addr(r1kh->id)) + *r1kh_wildcard = r1kh; + if (f_r1kh_id && + os_memcmp_const(r1kh->id, f_r1kh_id, FT_R1KH_ID_LEN) == 0) + *r1kh_out = r1kh; + } + + if (!*r1kh_out && !*r1kh_wildcard) + wpa_printf(MSG_DEBUG, "FT: No matching R1KH found"); + + if (*r1kh_out && wpa_ft_rrb_init_r1kh_seq(*r1kh_out) < 0) + *r1kh_out = NULL; +} + + +static int wpa_ft_rrb_check_r0kh(struct wpa_authenticator *wpa_auth, + const u8 *f_r0kh_id, size_t f_r0kh_id_len) +{ + if (f_r0kh_id_len != wpa_auth->conf.r0_key_holder_len || + os_memcmp_const(f_r0kh_id, wpa_auth->conf.r0_key_holder, + f_r0kh_id_len) != 0) + return -1; + + return 0; +} + + +static int wpa_ft_rrb_check_r1kh(struct wpa_authenticator *wpa_auth, + const u8 *f_r1kh_id) +{ + if (os_memcmp_const(f_r1kh_id, wpa_auth->conf.r1_key_holder, + FT_R1KH_ID_LEN) != 0) + return -1; + + return 0; +} + + +static void wpa_ft_rrb_del_r0kh(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct ft_remote_r0kh *r0kh, *prev = NULL; + + if (!wpa_auth->conf.r0kh_list) + return; + + for (r0kh = *wpa_auth->conf.r0kh_list; r0kh; r0kh = r0kh->next) { + if (r0kh == timeout_ctx) + break; + prev = r0kh; + } + if (!r0kh) + return; + if (prev) + prev->next = r0kh->next; + else + *wpa_auth->conf.r0kh_list = r0kh->next; + if (r0kh->seq) + wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0); + os_free(r0kh->seq); + os_free(r0kh); +} + + +static void wpa_ft_rrb_r0kh_replenish(struct wpa_authenticator *wpa_auth, + struct ft_remote_r0kh *r0kh, int timeout) +{ + if (timeout > 0) + eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r0kh, + wpa_auth, r0kh); +} + + +static void wpa_ft_rrb_r0kh_timeout(struct wpa_authenticator *wpa_auth, + struct ft_remote_r0kh *r0kh, int timeout) +{ + eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, r0kh); + + if (timeout > 0) + eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh, + wpa_auth, r0kh); +} + + +static struct ft_remote_r0kh * +wpa_ft_rrb_add_r0kh(struct wpa_authenticator *wpa_auth, + struct ft_remote_r0kh *r0kh_wildcard, + const u8 *src_addr, const u8 *r0kh_id, size_t id_len, + int timeout) +{ + struct ft_remote_r0kh *r0kh; + + if (!wpa_auth->conf.r0kh_list) + return NULL; + + r0kh = os_zalloc(sizeof(*r0kh)); + if (!r0kh) + return NULL; + + if (src_addr) + os_memcpy(r0kh->addr, src_addr, sizeof(r0kh->addr)); + + if (id_len > FT_R0KH_ID_MAX_LEN) + id_len = FT_R0KH_ID_MAX_LEN; + os_memcpy(r0kh->id, r0kh_id, id_len); + r0kh->id_len = id_len; + + os_memcpy(r0kh->key, r0kh_wildcard->key, sizeof(r0kh->key)); + + r0kh->next = *wpa_auth->conf.r0kh_list; + *wpa_auth->conf.r0kh_list = r0kh; + + if (timeout > 0) + eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r0kh, + wpa_auth, r0kh); + + if (wpa_ft_rrb_init_r0kh_seq(r0kh) < 0) + return NULL; + + return r0kh; +} + + +static void wpa_ft_rrb_del_r1kh(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct ft_remote_r1kh *r1kh, *prev = NULL; + + if (!wpa_auth->conf.r1kh_list) + return; + + for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) { + if (r1kh == timeout_ctx) break; - r0kh = r0kh->next; + prev = r1kh; + } + if (!r1kh) + return; + if (prev) + prev->next = r1kh->next; + else + *wpa_auth->conf.r1kh_list = r1kh->next; + if (r1kh->seq) + wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0); + os_free(r1kh->seq); + os_free(r1kh); +} + + +static void wpa_ft_rrb_r1kh_replenish(struct wpa_authenticator *wpa_auth, + struct ft_remote_r1kh *r1kh, int timeout) +{ + if (timeout > 0) + eloop_replenish_timeout(timeout, 0, wpa_ft_rrb_del_r1kh, + wpa_auth, r1kh); +} + + +static struct ft_remote_r1kh * +wpa_ft_rrb_add_r1kh(struct wpa_authenticator *wpa_auth, + struct ft_remote_r1kh *r1kh_wildcard, + const u8 *src_addr, const u8 *r1kh_id, int timeout) +{ + struct ft_remote_r1kh *r1kh; + + if (!wpa_auth->conf.r1kh_list) + return NULL; + + r1kh = os_zalloc(sizeof(*r1kh)); + if (!r1kh) + return NULL; + + os_memcpy(r1kh->addr, src_addr, sizeof(r1kh->addr)); + os_memcpy(r1kh->id, r1kh_id, sizeof(r1kh->id)); + os_memcpy(r1kh->key, r1kh_wildcard->key, sizeof(r1kh->key)); + r1kh->next = *wpa_auth->conf.r1kh_list; + *wpa_auth->conf.r1kh_list = r1kh; + + if (timeout > 0) + eloop_register_timeout(timeout, 0, wpa_ft_rrb_del_r1kh, + wpa_auth, r1kh); + + if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0) + return NULL; + + return r1kh; +} + + +void wpa_ft_sta_deinit(struct wpa_state_machine *sm) +{ + eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL); +} + + +static void wpa_ft_deinit_seq(struct wpa_authenticator *wpa_auth) +{ + struct ft_remote_r0kh *r0kh; + struct ft_remote_r1kh *r1kh; + + eloop_cancel_timeout(wpa_ft_rrb_seq_timeout, wpa_auth, ELOOP_ALL_CTX); + + if (wpa_auth->conf.r0kh_list) + r0kh = *wpa_auth->conf.r0kh_list; + else + r0kh = NULL; + for (; r0kh; r0kh = r0kh->next) { + if (!r0kh->seq) + continue; + wpa_ft_rrb_seq_flush(wpa_auth, r0kh->seq, 0); + os_free(r0kh->seq); + r0kh->seq = NULL; + } + + if (wpa_auth->conf.r1kh_list) + r1kh = *wpa_auth->conf.r1kh_list; + else + r1kh = NULL; + for (; r1kh; r1kh = r1kh->next) { + if (!r1kh->seq) + continue; + wpa_ft_rrb_seq_flush(wpa_auth, r1kh->seq, 0); + os_free(r1kh->seq); + r1kh->seq = NULL; + } +} + + +static void wpa_ft_deinit_rkh_tmp(struct wpa_authenticator *wpa_auth) +{ + struct ft_remote_r0kh *r0kh, *r0kh_next, *r0kh_prev = NULL; + struct ft_remote_r1kh *r1kh, *r1kh_next, *r1kh_prev = NULL; + + if (wpa_auth->conf.r0kh_list) + r0kh = *wpa_auth->conf.r0kh_list; + else + r0kh = NULL; + while (r0kh) { + r0kh_next = r0kh->next; + if (eloop_cancel_timeout(wpa_ft_rrb_del_r0kh, wpa_auth, + r0kh) > 0) { + if (r0kh_prev) + r0kh_prev->next = r0kh_next; + else + *wpa_auth->conf.r0kh_list = r0kh_next; + os_free(r0kh); + } else { + r0kh_prev = r0kh; + } + r0kh = r0kh_next; + } + + if (wpa_auth->conf.r1kh_list) + r1kh = *wpa_auth->conf.r1kh_list; + else + r1kh = NULL; + while (r1kh) { + r1kh_next = r1kh->next; + if (eloop_cancel_timeout(wpa_ft_rrb_del_r1kh, wpa_auth, + r1kh) > 0) { + if (r1kh_prev) + r1kh_prev->next = r1kh_next; + else + *wpa_auth->conf.r1kh_list = r1kh_next; + os_free(r1kh); + } else { + r1kh_prev = r1kh; + } + r1kh = r1kh_next; + } +} + + +void wpa_ft_deinit(struct wpa_authenticator *wpa_auth) +{ + wpa_ft_deinit_seq(wpa_auth); + wpa_ft_deinit_rkh_tmp(wpa_auth); +} + + +static void wpa_ft_block_r0kh(struct wpa_authenticator *wpa_auth, + const u8 *f_r0kh_id, size_t f_r0kh_id_len) +{ + struct ft_remote_r0kh *r0kh, *r0kh_wildcard; + + if (!wpa_auth->conf.rkh_neg_timeout) + return; + + wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, + &r0kh, &r0kh_wildcard); + + if (!r0kh_wildcard) { + /* r0kh removed after neg_timeout and might need re-adding */ + return; + } + + wpa_hexdump(MSG_DEBUG, "FT: Blacklist R0KH-ID", + f_r0kh_id, f_r0kh_id_len); + + if (r0kh) { + wpa_ft_rrb_r0kh_timeout(wpa_auth, r0kh, + wpa_auth->conf.rkh_neg_timeout); + os_memset(r0kh->addr, 0, ETH_ALEN); + } else + wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, NULL, f_r0kh_id, + f_r0kh_id_len, + wpa_auth->conf.rkh_neg_timeout); +} + + +static void wpa_ft_expire_pull(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_state_machine *sm = eloop_ctx; + + wpa_printf(MSG_DEBUG, "FT: Timeout pending pull request for " MACSTR, + MAC2STR(sm->addr)); + if (sm->ft_pending_pull_left_retries <= 0) + wpa_ft_block_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len); + + /* cancel multiple timeouts */ + eloop_cancel_timeout(wpa_ft_expire_pull, sm, NULL); + ft_finish_pull(sm); +} + + +static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm, + const u8 *ies, size_t ies_len, + const u8 *pmk_r0_name) +{ + struct ft_remote_r0kh *r0kh, *r0kh_wildcard; + u8 *packet = NULL; + const u8 *key, *f_r1kh_id = sm->wpa_auth->conf.r1_key_holder; + size_t packet_len, key_len; + struct ft_rrb_seq f_seq; + int tsecs, tusecs, first; + struct wpabuf *ft_pending_req_ies; + int r0kh_timeout; + struct tlv_list req_enc[] = { + { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN, + .data = pmk_r0_name }, + { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN, + .data = sm->addr }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + struct tlv_list req_auth[] = { + { .type = FT_RRB_NONCE, .len = FT_RRB_NONCE_LEN, + .data = sm->ft_pending_pull_nonce }, + { .type = FT_RRB_SEQ, .len = sizeof(f_seq), + .data = (u8 *) &f_seq }, + { .type = FT_RRB_R0KH_ID, .len = sm->r0kh_id_len, + .data = sm->r0kh_id }, + { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN, + .data = f_r1kh_id }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + + if (sm->ft_pending_pull_left_retries <= 0) + return -1; + first = sm->ft_pending_pull_left_retries == + sm->wpa_auth->conf.rkh_pull_retries; + sm->ft_pending_pull_left_retries--; + + wpa_ft_rrb_lookup_r0kh(sm->wpa_auth, sm->r0kh_id, sm->r0kh_id_len, + &r0kh, &r0kh_wildcard); + + /* Keep r0kh sufficiently long in the list for seq num check */ + r0kh_timeout = sm->wpa_auth->conf.rkh_pull_timeout / 1000 + + 1 + ftRRBseqTimeout; + if (r0kh) { + wpa_ft_rrb_r0kh_replenish(sm->wpa_auth, r0kh, r0kh_timeout); + } else if (r0kh_wildcard) { + wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID"); + /* r0kh->addr: updated by SEQ_RESP and wpa_ft_expire_pull */ + r0kh = wpa_ft_rrb_add_r0kh(sm->wpa_auth, r0kh_wildcard, + r0kh_wildcard->addr, + sm->r0kh_id, sm->r0kh_id_len, + r0kh_timeout); } if (r0kh == NULL) { wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID", sm->r0kh_id, sm->r0kh_id_len); return -1; } + if (is_zero_ether_addr(r0kh->addr)) { + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID is blacklisted", + sm->r0kh_id, sm->r0kh_id_len); + return -1; + } + if (os_memcmp(r0kh->addr, sm->wpa_auth->addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, + "FT: R0KH-ID points to self - no matching key available"); + return -1; + } + + key = r0kh->key; + key_len = sizeof(r0kh->key); wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH " "address " MACSTR, MAC2STR(r0kh->addr)); - os_memset(&frame, 0, sizeof(frame)); - frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; - frame.packet_type = FT_PACKET_R0KH_R1KH_PULL; - frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN); - os_memcpy(frame.ap_address, sm->wpa_auth->addr, ETH_ALEN); + if (r0kh->seq->rx.num_last == 0) { + /* A sequence request will be sent out anyway when pull + * response is received. Send it out now to avoid one RTT. */ + wpa_ft_rrb_seq_req(sm->wpa_auth, r0kh->seq, r0kh->addr, + r0kh->id, r0kh->id_len, f_r1kh_id, key, + key_len, NULL, 0, NULL, 0, NULL); + } - /* aes_wrap() does not support inplace encryption, so use a temporary - * buffer for the data. */ - if (random_get_bytes(f.nonce, FT_R0KH_R1KH_PULL_NONCE_LEN)) { + if (first && + random_get_bytes(sm->ft_pending_pull_nonce, FT_RRB_NONCE_LEN) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " "nonce"); return -1; } - os_memcpy(sm->ft_pending_pull_nonce, f.nonce, - FT_R0KH_R1KH_PULL_NONCE_LEN); - os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); - os_memcpy(f.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); - os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN); - os_memset(f.pad, 0, sizeof(f.pad)); - if (aes_wrap(r0kh->key, sizeof(r0kh->key), - (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, - f.nonce, frame.nonce) < 0) + if (wpa_ft_new_seq(r0kh->seq, &f_seq) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); + return -1; + } + + if (wpa_ft_rrb_build(key, key_len, req_enc, NULL, req_auth, NULL, + sm->wpa_auth->addr, FT_PACKET_R0KH_R1KH_PULL, + &packet, &packet_len) < 0) return -1; + ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len); wpabuf_free(sm->ft_pending_req_ies); - sm->ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len); - if (sm->ft_pending_req_ies == NULL) + sm->ft_pending_req_ies = ft_pending_req_ies; + if (!sm->ft_pending_req_ies) { + os_free(packet); return -1; + } + + tsecs = sm->wpa_auth->conf.rkh_pull_timeout / 1000; + tusecs = (sm->wpa_auth->conf.rkh_pull_timeout % 1000) * 1000; + eloop_register_timeout(tsecs, tusecs, wpa_ft_expire_pull, sm, NULL); + + wpa_ft_rrb_oui_send(sm->wpa_auth, r0kh->addr, FT_PACKET_R0KH_R1KH_PULL, + packet, packet_len); - wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame)); + os_free(packet); return 0; } +int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, + const u8 *pmk_r0, const u8 *pmk_r0_name) +{ + int expires_in = sm->wpa_auth->conf.r0_key_lifetime; + struct vlan_description vlan; + const u8 *identity, *radius_cui; + size_t identity_len, radius_cui_len; + int session_timeout; + size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ? + SHA384_MAC_LEN : PMK_LEN; + + if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) { + wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR, + MAC2STR(sm->addr)); + return -1; + } + + identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity); + radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr, + &radius_cui); + session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr); + + return wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len, + pmk_r0_name, sm->pairwise, &vlan, expires_in, + session_timeout, identity, identity_len, + radius_cui, radius_cui_len); +} + + int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, struct wpa_ptk *ptk) { - u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN]; - u8 pmk_r1[PMK_LEN]; + u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN]; + size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ? + SHA384_MAC_LEN : PMK_LEN; + size_t pmk_r1_len = pmk_r0_len; + u8 pmk_r1[PMK_LEN_MAX]; u8 ptk_name[WPA_PMK_NAME_LEN]; const u8 *mdid = sm->wpa_auth->conf.mobility_domain; const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder; @@ -373,6 +2031,12 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder; const u8 *ssid = sm->wpa_auth->conf.ssid; size_t ssid_len = sm->wpa_auth->conf.ssid_len; + int psk_local = sm->wpa_auth->conf.ft_psk_generate_local; + int expires_in = sm->wpa_auth->conf.r0_key_lifetime; + struct vlan_description vlan; + const u8 *identity, *radius_cui; + size_t identity_len, radius_cui_len; + int session_timeout; if (sm->xxkey_len == 0) { wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " @@ -380,23 +2044,45 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, return -1; } - wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid, - r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN); - wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name, - sm->pairwise); + if (wpa_ft_get_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) { + wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " MACSTR, + MAC2STR(sm->addr)); + return -1; + } - wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr, - pmk_r1, sm->pmk_r1_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN); + identity_len = wpa_ft_get_identity(sm->wpa_auth, sm->addr, &identity); + radius_cui_len = wpa_ft_get_radius_cui(sm->wpa_auth, sm->addr, + &radius_cui); + session_timeout = wpa_ft_get_session_timeout(sm->wpa_auth, sm->addr); + + if (wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid, + r0kh, r0kh_len, sm->addr, + pmk_r0, pmk_r0_name, + wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_len); + wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN); + if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) + wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_len, + pmk_r0_name, + sm->pairwise, &vlan, expires_in, + session_timeout, identity, identity_len, + radius_cui, radius_cui_len); + + if (wpa_derive_pmk_r1(pmk_r0, pmk_r0_len, pmk_r0_name, r1kh, sm->addr, + pmk_r1, sm->pmk_r1_name) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_len); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); - wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name, - sm->pairwise); - - return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, - sm->wpa_auth->addr, sm->pmk_r1_name, + if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) + wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, pmk_r1_len, + sm->pmk_r1_name, sm->pairwise, &vlan, + expires_in, session_timeout, identity, + identity_len, radius_cui, radius_cui_len); + + return wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce, + sm->addr, sm->wpa_auth->addr, sm->pmk_r1_name, ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise); } @@ -404,9 +2090,9 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, const u8 *addr, int idx, u8 *seq) { - if (wpa_auth->cb.get_seqnum == NULL) + if (wpa_auth->cb->get_seqnum == NULL) return -1; - return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); + return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq); } @@ -418,6 +2104,16 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) const u8 *key; size_t key_len; u8 keybuf[32]; + const u8 *kek; + size_t kek_len; + + if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + kek = sm->PTK.kek2; + kek_len = sm->PTK.kek2_len; + } else { + kek = sm->PTK.kek; + kek_len = sm->PTK.kek_len; + } key_len = gsm->GTK_len; if (key_len > sizeof(keybuf)) @@ -456,8 +2152,10 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03); subelem[4] = gsm->GTK_len; wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5); - if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, key_len / 8, key, - subelem + 13)) { + if (aes_wrap(kek, kek_len, key_len / 8, key, subelem + 13)) { + wpa_printf(MSG_DEBUG, + "FT: GTK subelem encryption failed: kek_len=%d", + (int) kek_len); os_free(subelem); return NULL; } @@ -473,10 +2171,23 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len) u8 *subelem, *pos; struct wpa_group *gsm = sm->group; size_t subelem_len; + const u8 *kek; + size_t kek_len; + size_t igtk_len; + + if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + kek = sm->PTK.kek2; + kek_len = sm->PTK.kek2_len; + } else { + kek = sm->PTK.kek; + kek_len = sm->PTK.kek_len; + } + + igtk_len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher); /* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] | * Key[16+8] */ - subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8; + subelem_len = 1 + 1 + 2 + 6 + 1 + igtk_len + 8; subelem = os_zalloc(subelem_len); if (subelem == NULL) return NULL; @@ -488,9 +2199,12 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len) pos += 2; wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos); pos += 6; - *pos++ = WPA_IGTK_LEN; - if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, WPA_IGTK_LEN / 8, + *pos++ = igtk_len; + if (aes_wrap(kek, kek_len, igtk_len / 8, gsm->IGTK[gsm->GN_igtk - 4], pos)) { + wpa_printf(MSG_DEBUG, + "FT: IGTK subelem encryption failed: kek_len=%d", + (int) kek_len); os_free(subelem); return NULL; } @@ -637,17 +2351,21 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, const u8 *req_ies, size_t req_ies_len) { u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL; + u8 *fte_mic, *elem_count; size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0; int res; struct wpa_auth_config *conf; - struct rsn_ftie *_ftie; struct wpa_ft_ies parse; u8 *ric_start; u8 *anonce, *snonce; + const u8 *kck; + size_t kck_len; + int use_sha384; if (sm == NULL) return pos; + use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt); conf = &sm->wpa_auth->conf; if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt)) @@ -662,7 +2380,7 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, */ res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name); if (res < 0) - return pos; + return NULL; rsnie = pos; rsnie_len = res; pos += res; @@ -671,7 +2389,7 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, /* Mobility Domain Information */ res = wpa_write_mdie(conf, pos, end - pos); if (res < 0) - return pos; + return NULL; mdie = pos; mdie_len = res; pos += res; @@ -679,6 +2397,11 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, /* Fast BSS Transition Information */ if (auth_alg == WLAN_AUTH_FT) { subelem = wpa_ft_gtk_subelem(sm, &subelem_len); + if (!subelem) { + wpa_printf(MSG_DEBUG, + "FT: Failed to add GTK subelement"); + return NULL; + } r0kh_id = sm->r0kh_id; r0kh_id_len = sm->r0kh_id_len; anonce = sm->ANonce; @@ -690,14 +2413,16 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, u8 *nbuf; igtk = wpa_ft_igtk_subelem(sm, &igtk_len); if (igtk == NULL) { + wpa_printf(MSG_DEBUG, + "FT: Failed to add IGTK subelement"); os_free(subelem); - return pos; + return NULL; } nbuf = os_realloc(subelem, subelem_len + igtk_len); if (nbuf == NULL) { os_free(subelem); os_free(igtk); - return pos; + return NULL; } subelem = nbuf; os_memcpy(subelem + subelem_len, igtk, igtk_len); @@ -711,44 +2436,66 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, anonce = NULL; snonce = NULL; } - res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, anonce, snonce, pos, - end - pos, subelem, subelem_len); + res = wpa_write_ftie(conf, use_sha384, r0kh_id, r0kh_id_len, + anonce, snonce, pos, end - pos, + subelem, subelem_len); os_free(subelem); if (res < 0) - return pos; + return NULL; ftie = pos; ftie_len = res; pos += res; - _ftie = (struct rsn_ftie *) (ftie + 2); + if (use_sha384) { + struct rsn_ftie_sha384 *_ftie = + (struct rsn_ftie_sha384 *) (ftie + 2); + + fte_mic = _ftie->mic; + elem_count = &_ftie->mic_control[1]; + } else { + struct rsn_ftie *_ftie = (struct rsn_ftie *) (ftie + 2); + + fte_mic = _ftie->mic; + elem_count = &_ftie->mic_control[1]; + } if (auth_alg == WLAN_AUTH_FT) - _ftie->mic_control[1] = 3; /* Information element count */ + *elem_count = 3; /* Information element count */ ric_start = pos; - if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) { + if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse, use_sha384) == 0 + && parse.ric) { pos = wpa_ft_process_ric(sm, pos, end, parse.ric, parse.ric_len); if (auth_alg == WLAN_AUTH_FT) - _ftie->mic_control[1] += + *elem_count += ieee802_11_ie_count(ric_start, pos - ric_start); } if (ric_start == pos) ric_start = NULL; + if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + kck = sm->PTK.kck2; + kck_len = sm->PTK.kck2_len; + } else { + kck = sm->PTK.kck; + kck_len = sm->PTK.kck_len; + } if (auth_alg == WLAN_AUTH_FT && - wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr, - sm->wpa_auth->addr, 6, + wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 6, mdie, mdie_len, ftie, ftie_len, rsnie, rsnie_len, ric_start, ric_start ? pos - ric_start : 0, - _ftie->mic) < 0) + fte_mic) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); + return NULL; + } os_free(sm->assoc_resp_ftie); sm->assoc_resp_ftie = os_malloc(ftie_len); - if (sm->assoc_resp_ftie) - os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len); + if (!sm->assoc_resp_ftie) + return NULL; + os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len); return pos; } @@ -759,10 +2506,10 @@ static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, enum wpa_alg alg, const u8 *addr, int idx, u8 *key, size_t key_len) { - if (wpa_auth->cb.set_key == NULL) + if (wpa_auth->cb->set_key == NULL) return -1; - return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, - key, key_len); + return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx, + key, key_len); } @@ -804,20 +2551,219 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm) } +/* Derive PMK-R1 from PSK, check all available PSK */ +static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm, + const u8 *req_pmk_r1_name, + u8 *out_pmk_r1, int *out_pairwise, + struct vlan_description *out_vlan, + const u8 **out_identity, size_t *out_identity_len, + const u8 **out_radius_cui, + size_t *out_radius_cui_len, + int *out_session_timeout) +{ + const u8 *pmk = NULL; + u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; + struct wpa_authenticator *wpa_auth = sm->wpa_auth; + const u8 *mdid = wpa_auth->conf.mobility_domain; + const u8 *r0kh = sm->r0kh_id; + size_t r0kh_len = sm->r0kh_id_len; + const u8 *r1kh = wpa_auth->conf.r1_key_holder; + const u8 *ssid = wpa_auth->conf.ssid; + size_t ssid_len = wpa_auth->conf.ssid_len; + int pairwise; + + pairwise = sm->pairwise; + + for (;;) { + pmk = wpa_ft_get_psk(wpa_auth, sm->addr, sm->p2p_dev_addr, + pmk); + if (pmk == NULL) + break; + + if (wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh, + r0kh_len, sm->addr, + pmk_r0, pmk_r0_name, 0) < 0 || + wpa_derive_pmk_r1(pmk_r0, PMK_LEN, pmk_r0_name, r1kh, + sm->addr, pmk_r1, pmk_r1_name) < 0 || + os_memcmp_const(pmk_r1_name, req_pmk_r1_name, + WPA_PMK_NAME_LEN) != 0) + continue; + + /* We found a PSK that matches the requested pmk_r1_name */ + wpa_printf(MSG_DEBUG, + "FT: Found PSK to generate PMK-R1 locally"); + os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN); + if (out_pairwise) + *out_pairwise = pairwise; + if (out_vlan && + wpa_ft_get_vlan(sm->wpa_auth, sm->addr, out_vlan) < 0) { + wpa_printf(MSG_DEBUG, "FT: vlan not available for STA " + MACSTR, MAC2STR(sm->addr)); + return -1; + } + + if (out_identity && out_identity_len) { + *out_identity_len = wpa_ft_get_identity( + sm->wpa_auth, sm->addr, out_identity); + } + + if (out_radius_cui && out_radius_cui_len) { + *out_radius_cui_len = wpa_ft_get_radius_cui( + sm->wpa_auth, sm->addr, out_radius_cui); + } + + if (out_session_timeout) { + *out_session_timeout = wpa_ft_get_session_timeout( + sm->wpa_auth, sm->addr); + } + + return 0; + } + + wpa_printf(MSG_DEBUG, + "FT: Did not find PSK to generate PMK-R1 locally"); + return -1; +} + + +/* Detect the configuration the station asked for. + * Required to detect FT-PSK and pairwise cipher. + */ +static int wpa_ft_set_key_mgmt(struct wpa_state_machine *sm, + struct wpa_ft_ies *parse) +{ + int key_mgmt, ciphers; + + if (sm->wpa_key_mgmt) + return 0; + + key_mgmt = parse->key_mgmt & sm->wpa_auth->conf.wpa_key_mgmt; + if (!key_mgmt) { + wpa_printf(MSG_DEBUG, "FT: Invalid key mgmt (0x%x) from " + MACSTR, parse->key_mgmt, MAC2STR(sm->addr)); + return -1; + } + if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; +#ifdef CONFIG_SHA384 + else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; +#endif /* CONFIG_SHA384 */ + else if (key_mgmt & WPA_KEY_MGMT_FT_PSK) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK; +#ifdef CONFIG_FILS + else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256; + else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384; +#endif /* CONFIG_FILS */ + ciphers = parse->pairwise_cipher & sm->wpa_auth->conf.rsn_pairwise; + if (!ciphers) { + wpa_printf(MSG_DEBUG, "FT: Invalid pairwise cipher (0x%x) from " + MACSTR, + parse->pairwise_cipher, MAC2STR(sm->addr)); + return -1; + } + sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0); + + return 0; +} + + +static int wpa_ft_local_derive_pmk_r1(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const u8 *r0kh_id, size_t r0kh_id_len, + const u8 *req_pmk_r0_name, + const u8 *req_pmk_r1_name, + u8 *out_pmk_r1, int *out_pairwise, + struct vlan_description *vlan, + const u8 **identity, size_t *identity_len, + const u8 **radius_cui, + size_t *radius_cui_len, + int *out_session_timeout) +{ + struct wpa_auth_config *conf = &wpa_auth->conf; + const struct wpa_ft_pmk_r0_sa *r0; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + int expires_in = 0; + int session_timeout = 0; + struct os_reltime now; + + if (conf->r0_key_holder_len != r0kh_id_len || + os_memcmp(conf->r0_key_holder, r0kh_id, conf->r0_key_holder_len) != + 0) + return -1; /* not our R0KH-ID */ + + wpa_printf(MSG_DEBUG, "FT: STA R0KH-ID matching local configuration"); + if (wpa_ft_fetch_pmk_r0(sm->wpa_auth, sm->addr, req_pmk_r0_name, &r0) < + 0) + return -1; /* no matching PMKR0Name in local cache */ + + wpa_printf(MSG_DEBUG, "FT: Requested PMKR0Name found in local cache"); + + if (wpa_derive_pmk_r1(r0->pmk_r0, r0->pmk_r0_len, r0->pmk_r0_name, + conf->r1_key_holder, + sm->addr, out_pmk_r1, pmk_r1_name) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", out_pmk_r1, r0->pmk_r0_len); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); + + os_get_reltime(&now); + if (r0->expiration) + expires_in = r0->expiration - now.sec; + + if (r0->session_timeout) + session_timeout = r0->session_timeout - now.sec; + + wpa_ft_store_pmk_r1(wpa_auth, sm->addr, out_pmk_r1, r0->pmk_r0_len, + pmk_r1_name, + sm->pairwise, r0->vlan, expires_in, session_timeout, + r0->identity, r0->identity_len, + r0->radius_cui, r0->radius_cui_len); + + *out_pairwise = sm->pairwise; + if (vlan) { + if (r0->vlan) + *vlan = *r0->vlan; + else + os_memset(vlan, 0, sizeof(*vlan)); + } + + if (identity && identity_len) { + *identity = r0->identity; + *identity_len = r0->identity_len; + } + + if (radius_cui && radius_cui_len) { + *radius_cui = r0->radius_cui; + *radius_cui_len = r0->radius_cui_len; + } + + *out_session_timeout = session_timeout; + + return 0; +} + + static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, const u8 *ies, size_t ies_len, u8 **resp_ies, size_t *resp_ies_len) { struct rsn_mdie *mdie; - struct rsn_ftie *ftie; - u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 pmk_r1[PMK_LEN_MAX], pmk_r1_name[WPA_PMK_NAME_LEN]; u8 ptk_name[WPA_PMK_NAME_LEN]; struct wpa_auth_config *conf; struct wpa_ft_ies parse; size_t buflen; int ret; u8 *pos, *end; - int pairwise; + int pairwise, session_timeout = 0; + struct vlan_description vlan; + const u8 *identity, *radius_cui; + size_t identity_len = 0, radius_cui_len = 0; + int use_sha384; + size_t pmk_r1_len; *resp_ies = NULL; *resp_ies_len = 0; @@ -828,10 +2774,12 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs", ies, ies_len); - if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + if (wpa_ft_parse_ies(ies, ies_len, &parse, -1)) { wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } + use_sha384 = wpa_key_mgmt_sha384(parse.key_mgmt); + pmk_r1_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN; mdie = (struct rsn_mdie *) parse.mdie; if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || @@ -842,13 +2790,27 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, return WLAN_STATUS_INVALID_MDIE; } - ftie = (struct rsn_ftie *) parse.ftie; - if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { - wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); - return WLAN_STATUS_INVALID_FTIE; - } + if (use_sha384) { + struct rsn_ftie_sha384 *ftie; + + ftie = (struct rsn_ftie_sha384 *) parse.ftie; + if (!ftie || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return WLAN_STATUS_INVALID_FTIE; + } - os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN); + os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN); + } else { + struct rsn_ftie *ftie; + + ftie = (struct rsn_ftie *) parse.ftie; + if (!ftie || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return WLAN_STATUS_INVALID_FTIE; + } + + os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN); + } if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID"); @@ -865,26 +2827,58 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, return WLAN_STATUS_INVALID_PMKID; } + if (wpa_ft_set_key_mgmt(sm, &parse) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name", parse.rsn_pmkid, WPA_PMK_NAME_LEN); - wpa_derive_pmk_r1_name(parse.rsn_pmkid, - sm->wpa_auth->conf.r1_key_holder, sm->addr, - pmk_r1_name); + if (wpa_derive_pmk_r1_name(parse.rsn_pmkid, + sm->wpa_auth->conf.r1_key_holder, sm->addr, + pmk_r1_name, use_sha384) < 0) + return WLAN_STATUS_UNSPECIFIED_FAILURE; wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); - if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1, - &pairwise) < 0) { + if (conf->ft_psk_generate_local && + wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) { + if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise, + &vlan, &identity, &identity_len, + &radius_cui, &radius_cui_len, + &session_timeout) < 0) + return WLAN_STATUS_INVALID_PMKID; + wpa_printf(MSG_DEBUG, + "FT: Generated PMK-R1 for FT-PSK locally"); + } else if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, + pmk_r1, &pmk_r1_len, &pairwise, &vlan, + &identity, &identity_len, &radius_cui, + &radius_cui_len, &session_timeout) < 0) { + wpa_printf(MSG_DEBUG, + "FT: No PMK-R1 available in local cache for the requested PMKR1Name"); + if (wpa_ft_local_derive_pmk_r1(sm->wpa_auth, sm, + parse.r0kh_id, parse.r0kh_id_len, + parse.rsn_pmkid, + pmk_r1_name, pmk_r1, &pairwise, + &vlan, &identity, &identity_len, + &radius_cui, &radius_cui_len, + &session_timeout) == 0) { + wpa_printf(MSG_DEBUG, + "FT: Generated PMK-R1 based on local PMK-R0"); + goto pmk_r1_derived; + } + if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) { - wpa_printf(MSG_DEBUG, "FT: Did not have matching " - "PMK-R1 and unknown R0KH-ID"); + wpa_printf(MSG_DEBUG, + "FT: Did not have matching PMK-R1 and either unknown or blocked R0KH-ID or NAK from R0KH"); return WLAN_STATUS_INVALID_PMKID; } return -1; /* Status pending */ + } else { + wpa_printf(MSG_DEBUG, "FT: Found PMKR1Name from local cache"); } - wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN); +pmk_r1_derived: + wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len); sm->pmk_r1_name_valid = 1; os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); @@ -899,8 +2893,8 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce", sm->ANonce, WPA_NONCE_LEN); - if (wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, - sm->wpa_auth->addr, pmk_r1_name, + if (wpa_pmk_r1_to_ptk(pmk_r1, pmk_r1_len, sm->SNonce, sm->ANonce, + sm->addr, sm->wpa_auth->addr, pmk_r1_name, &sm->PTK, ptk_name, sm->wpa_key_mgmt, pairwise) < 0) return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -910,44 +2904,51 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm, sm->tk_already_set = FALSE; wpa_ft_install_ptk(sm); + if (wpa_ft_set_vlan(sm->wpa_auth, sm->addr, &vlan) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to configure VLAN"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + if (wpa_ft_set_identity(sm->wpa_auth, sm->addr, + identity, identity_len) < 0 || + wpa_ft_set_radius_cui(sm->wpa_auth, sm->addr, + radius_cui, radius_cui_len) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to configure identity/CUI"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + wpa_ft_set_session_timeout(sm->wpa_auth, sm->addr, session_timeout); + buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + 2 + FT_R1KH_ID_LEN + 200; *resp_ies = os_zalloc(buflen); - if (*resp_ies == NULL) { - return WLAN_STATUS_UNSPECIFIED_FAILURE; - } + if (*resp_ies == NULL) + goto fail; pos = *resp_ies; end = *resp_ies + buflen; ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid); - if (ret < 0) { - os_free(*resp_ies); - *resp_ies = NULL; - return WLAN_STATUS_UNSPECIFIED_FAILURE; - } + if (ret < 0) + goto fail; pos += ret; ret = wpa_write_mdie(conf, pos, end - pos); - if (ret < 0) { - os_free(*resp_ies); - *resp_ies = NULL; - return WLAN_STATUS_UNSPECIFIED_FAILURE; - } + if (ret < 0) + goto fail; pos += ret; - ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len, + ret = wpa_write_ftie(conf, use_sha384, parse.r0kh_id, parse.r0kh_id_len, sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0); - if (ret < 0) { - os_free(*resp_ies); - *resp_ies = NULL; - return WLAN_STATUS_UNSPECIFIED_FAILURE; - } + if (ret < 0) + goto fail; pos += ret; *resp_ies_len = pos - *resp_ies; return WLAN_STATUS_SUCCESS; +fail: + os_free(*resp_ies); + *resp_ies = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; } @@ -975,6 +2976,7 @@ void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, sm->ft_pending_cb = cb; sm->ft_pending_cb_ctx = ctx; sm->ft_pending_auth_transaction = auth_transaction; + sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries; res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies, &resp_ies_len); if (res < 0) { @@ -998,17 +3000,23 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, { struct wpa_ft_ies parse; struct rsn_mdie *mdie; - struct rsn_ftie *ftie; u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; size_t mic_len = 16; unsigned int count; + const u8 *kck; + size_t kck_len; + int use_sha384; + const u8 *anonce, *snonce, *fte_mic; + u8 fte_elem_count; if (sm == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; + use_sha384 = wpa_key_mgmt_sha384(sm->wpa_key_mgmt); + wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len); - if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } @@ -1039,34 +3047,56 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, return WLAN_STATUS_INVALID_MDIE; } - ftie = (struct rsn_ftie *) parse.ftie; - if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { - wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); - return WLAN_STATUS_INVALID_FTIE; + if (use_sha384) { + struct rsn_ftie_sha384 *ftie; + + ftie = (struct rsn_ftie_sha384 *) parse.ftie; + if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return WLAN_STATUS_INVALID_FTIE; + } + + anonce = ftie->anonce; + snonce = ftie->snonce; + fte_elem_count = ftie->mic_control[1]; + fte_mic = ftie->mic; + } else { + struct rsn_ftie *ftie; + + ftie = (struct rsn_ftie *) parse.ftie; + if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return WLAN_STATUS_INVALID_FTIE; + } + + anonce = ftie->anonce; + snonce = ftie->snonce; + fte_elem_count = ftie->mic_control[1]; + fte_mic = ftie->mic; } - if (os_memcmp(ftie->snonce, sm->SNonce, WPA_NONCE_LEN) != 0) { + if (os_memcmp(snonce, sm->SNonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", - ftie->snonce, WPA_NONCE_LEN); + snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", sm->SNonce, WPA_NONCE_LEN); - return -1; + return WLAN_STATUS_INVALID_FTIE; } - if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) { + if (os_memcmp(anonce, sm->ANonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received ANonce", - ftie->anonce, WPA_NONCE_LEN); + anonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce", sm->ANonce, WPA_NONCE_LEN); - return -1; + return WLAN_STATUS_INVALID_FTIE; } if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); - return -1; + return WLAN_STATUS_INVALID_FTIE; } if (parse.r0kh_id_len != sm->r0kh_id_len || @@ -1078,12 +3108,12 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, parse.r0kh_id, parse.r0kh_id_len); wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", sm->r0kh_id, sm->r0kh_id_len); - return -1; + return WLAN_STATUS_INVALID_FTIE; } if (parse.r1kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); - return -1; + return WLAN_STATUS_INVALID_FTIE; } if (os_memcmp_const(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder, @@ -1094,7 +3124,7 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, parse.r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID", sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); - return -1; + return WLAN_STATUS_INVALID_FTIE; } if (parse.rsn_pmkid == NULL || @@ -1102,21 +3132,27 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, { wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in " "RSNIE (pmkid=%d)", !!parse.rsn_pmkid); - return -1; + return WLAN_STATUS_INVALID_PMKID; } count = 3; if (parse.ric) count += ieee802_11_ie_count(parse.ric, parse.ric_len); - if (ftie->mic_control[1] != count) { + if (fte_elem_count != count) { wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " "Control: received %u expected %u", - ftie->mic_control[1], count); - return -1; + fte_elem_count, count); + return WLAN_STATUS_UNSPECIFIED_FAILURE; } - if (wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr, - sm->wpa_auth->addr, 5, + if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) { + kck = sm->PTK.kck2; + kck_len = sm->PTK.kck2_len; + } else { + kck = sm->PTK.kck; + kck_len = sm->PTK.kck_len; + } + if (wpa_ft_mic(kck, kck_len, sm->addr, sm->wpa_auth->addr, 5, parse.mdie - 2, parse.mdie_len + 2, parse.ftie - 2, parse.ftie_len + 2, parse.rsn - 2, parse.rsn_len + 2, @@ -1126,12 +3162,12 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, return WLAN_STATUS_UNSPECIFIED_FAILURE; } - if (os_memcmp_const(mic, ftie->mic, mic_len) != 0) { + if (os_memcmp_const(mic, fte_mic, mic_len) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR, MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr)); wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", - ftie->mic, mic_len); + fte_mic, mic_len); wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len); wpa_hexdump(MSG_MSGDUMP, "FT: MDIE", parse.mdie - 2, parse.mdie_len + 2); @@ -1199,6 +3235,11 @@ int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len) wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len); + if (!sm->wpa_auth->conf.ft_over_ds) { + wpa_printf(MSG_DEBUG, "FT: Over-DS option disabled - reject"); + return -1; + } + /* RRB - Forward action frame to the target AP */ frame = os_malloc(sizeof(*frame) + len); if (frame == NULL) @@ -1251,6 +3292,7 @@ static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth, sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb; sm->ft_pending_cb_ctx = sm; os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN); + sm->ft_pending_pull_left_retries = sm->wpa_auth->conf.rkh_pull_retries; res = wpa_ft_process_auth_req(sm, body, len, &resp_ies, &resp_ies_len); if (res < 0) { @@ -1316,112 +3358,431 @@ static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm, } +static int wpa_ft_rrb_build_r0(const u8 *key, const size_t key_len, + const struct tlv_list *tlvs, + const struct wpa_ft_pmk_r0_sa *pmk_r0, + const u8 *r1kh_id, const u8 *s1kh_id, + const struct tlv_list *tlv_auth, + const u8 *src_addr, u8 type, + u8 **packet, size_t *packet_len) +{ + u8 pmk_r1[PMK_LEN_MAX]; + size_t pmk_r1_len = pmk_r0->pmk_r0_len; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 f_pairwise[sizeof(le16)]; + u8 f_expires_in[sizeof(le16)]; + u8 f_session_timeout[sizeof(le32)]; + int expires_in; + int session_timeout; + struct os_reltime now; + int ret; + struct tlv_list sess_tlv[] = { + { .type = FT_RRB_PMK_R1, .len = pmk_r1_len, + .data = pmk_r1 }, + { .type = FT_RRB_PMK_R1_NAME, .len = sizeof(pmk_r1_name), + .data = pmk_r1_name }, + { .type = FT_RRB_PAIRWISE, .len = sizeof(f_pairwise), + .data = f_pairwise }, + { .type = FT_RRB_EXPIRES_IN, .len = sizeof(f_expires_in), + .data = f_expires_in }, + { .type = FT_RRB_IDENTITY, .len = pmk_r0->identity_len, + .data = pmk_r0->identity }, + { .type = FT_RRB_RADIUS_CUI, .len = pmk_r0->radius_cui_len, + .data = pmk_r0->radius_cui }, + { .type = FT_RRB_SESSION_TIMEOUT, + .len = sizeof(f_session_timeout), + .data = f_session_timeout }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + + if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_len, + pmk_r0->pmk_r0_name, r1kh_id, + s1kh_id, pmk_r1, pmk_r1_name) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 (for peer AP)", + pmk_r1, pmk_r1_len); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name (for peer AP)", + pmk_r1_name, WPA_PMK_NAME_LEN); + WPA_PUT_LE16(f_pairwise, pmk_r0->pairwise); + + os_get_reltime(&now); + if (pmk_r0->expiration > now.sec) + expires_in = pmk_r0->expiration - now.sec; + else if (pmk_r0->expiration) + expires_in = 1; + else + expires_in = 0; + WPA_PUT_LE16(f_expires_in, expires_in); + + if (pmk_r0->session_timeout > now.sec) + session_timeout = pmk_r0->session_timeout - now.sec; + else if (pmk_r0->session_timeout) + session_timeout = 1; + else + session_timeout = 0; + WPA_PUT_LE32(f_session_timeout, session_timeout); + + ret = wpa_ft_rrb_build(key, key_len, tlvs, sess_tlv, tlv_auth, + pmk_r0->vlan, src_addr, type, + packet, packet_len); + + os_memset(pmk_r1, 0, sizeof(pmk_r1)); + + return ret; +} + + static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, const u8 *src_addr, - const u8 *data, size_t data_len) + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer) { - struct ft_r0kh_r1kh_pull_frame f; - const u8 *crypt; - u8 *plain; - struct ft_remote_r1kh *r1kh; - struct ft_r0kh_r1kh_resp_frame resp, r; - u8 pmk_r0[PMK_LEN]; - int pairwise; + const char *msgtype = "pull request"; + u8 *plain = NULL, *packet = NULL; + size_t plain_len = 0, packet_len = 0; + struct ft_remote_r1kh *r1kh, *r1kh_wildcard; + const u8 *key; + size_t key_len; + int seq_ret; + const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id, *f_s1kh_id, *f_pmk_r0_name; + size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len, f_s1kh_id_len; + size_t f_pmk_r0_name_len; + const struct wpa_ft_pmk_r0_sa *r0; + int ret; + struct tlv_list resp[2]; + struct tlv_list resp_auth[5]; + struct ft_rrb_seq f_seq; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull"); - if (data_len < sizeof(f)) - return -1; + RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1); + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len); - r1kh = wpa_auth->conf.r1kh_list; - while (r1kh) { - if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0) - break; - r1kh = r1kh->next; + if (wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len)) { + wpa_printf(MSG_DEBUG, "FT: R0KH-ID mismatch"); + goto out; } - if (r1kh == NULL) { - wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for " - "PMK-R1 pull source address " MACSTR, - MAC2STR(src_addr)); - return -1; + + RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN); + wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id)); + + wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, &r1kh_wildcard); + if (r1kh) { + key = r1kh->key; + key_len = sizeof(r1kh->key); + } else if (r1kh_wildcard) { + wpa_printf(MSG_DEBUG, "FT: Using wildcard R1KH-ID"); + key = r1kh_wildcard->key; + key_len = sizeof(r1kh_wildcard->key); + } else { + goto out; } - crypt = data + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce); - os_memset(&f, 0, sizeof(f)); - plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce); - /* aes_unwrap() does not support inplace decryption, so use a temporary - * buffer for the data. */ - if (aes_unwrap(r1kh->key, sizeof(r1kh->key), - (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, - crypt, plain) < 0) { - wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " - "request from " MACSTR, MAC2STR(src_addr)); - return -1; + RRB_GET_AUTH(FT_RRB_NONCE, nonce, "pull request", FT_RRB_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len); + + seq_ret = FT_RRB_SEQ_DROP; + if (r1kh) + seq_ret = wpa_ft_rrb_seq_chk(r1kh->seq, src_addr, enc, enc_len, + auth, auth_len, msgtype, no_defer); + if (!no_defer && r1kh_wildcard && + (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) { + /* wildcard: r1kh-id unknown or changed addr -> do a seq req */ + seq_ret = FT_RRB_SEQ_DEFER; } - wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", - f.nonce, sizeof(f.nonce)); - wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name", - f.pmk_r0_name, WPA_PMK_NAME_LEN); - wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID=" - MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id)); - - os_memset(&resp, 0, sizeof(resp)); - resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; - resp.packet_type = FT_PACKET_R0KH_R1KH_RESP; - resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN); - os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN); - - /* aes_wrap() does not support inplace encryption, so use a temporary - * buffer for the data. */ - os_memcpy(r.nonce, f.nonce, sizeof(f.nonce)); - os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN); - os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN); - if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0, - &pairwise) < 0) { - wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for " - "PMK-R1 pull"); - return -1; + if (seq_ret == FT_RRB_SEQ_DROP) + goto out; + + if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len, + src_addr, FT_PACKET_R0KH_R1KH_PULL, + &plain, &plain_len) < 0) + goto out; + + if (!r1kh) + r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, src_addr, + f_r1kh_id, + wpa_auth->conf.rkh_pos_timeout); + if (!r1kh) + goto out; + + if (seq_ret == FT_RRB_SEQ_DEFER) { + wpa_ft_rrb_seq_req(wpa_auth, r1kh->seq, src_addr, f_r0kh_id, + f_r0kh_id_len, f_r1kh_id, key, key_len, + enc, enc_len, auth, auth_len, + &wpa_ft_rrb_rx_pull); + goto out; } - wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id, - r.pmk_r1, r.pmk_r1_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name, - WPA_PMK_NAME_LEN); - r.pairwise = host_to_le16(pairwise); - os_memset(r.pad, 0, sizeof(r.pad)); + wpa_ft_rrb_seq_accept(wpa_auth, r1kh->seq, src_addr, auth, auth_len, + msgtype); + wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, + wpa_auth->conf.rkh_pos_timeout); - if (aes_wrap(r1kh->key, sizeof(r1kh->key), - (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, - r.nonce, resp.nonce) < 0) { - os_memset(pmk_r0, 0, PMK_LEN); - return -1; + RRB_GET(FT_RRB_PMK_R0_NAME, pmk_r0_name, msgtype, WPA_PMK_NAME_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", f_pmk_r0_name, + f_pmk_r0_name_len); + + RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN); + wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id)); + + if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); + goto out; } - os_memset(pmk_r0, 0, PMK_LEN); + resp[0].type = FT_RRB_S1KH_ID; + resp[0].len = f_s1kh_id_len; + resp[0].data = f_s1kh_id; + resp[1].type = FT_RRB_LAST_EMPTY; + resp[1].len = 0; + resp[1].data = NULL; + + resp_auth[0].type = FT_RRB_NONCE; + resp_auth[0].len = f_nonce_len; + resp_auth[0].data = f_nonce; + resp_auth[1].type = FT_RRB_SEQ; + resp_auth[1].len = sizeof(f_seq); + resp_auth[1].data = (u8 *) &f_seq; + resp_auth[2].type = FT_RRB_R0KH_ID; + resp_auth[2].len = f_r0kh_id_len; + resp_auth[2].data = f_r0kh_id; + resp_auth[3].type = FT_RRB_R1KH_ID; + resp_auth[3].len = f_r1kh_id_len; + resp_auth[3].data = f_r1kh_id; + resp_auth[4].type = FT_RRB_LAST_EMPTY; + resp_auth[4].len = 0; + resp_auth[4].data = NULL; + + if (wpa_ft_fetch_pmk_r0(wpa_auth, f_s1kh_id, f_pmk_r0_name, &r0) < 0) { + wpa_printf(MSG_DEBUG, "FT: No matching PMK-R0-Name found"); + ret = wpa_ft_rrb_build(key, key_len, resp, NULL, resp_auth, + NULL, wpa_auth->addr, + FT_PACKET_R0KH_R1KH_RESP, + &packet, &packet_len); + } else { + ret = wpa_ft_rrb_build_r0(key, key_len, resp, r0, f_r1kh_id, + f_s1kh_id, resp_auth, wpa_auth->addr, + FT_PACKET_R0KH_R1KH_RESP, + &packet, &packet_len); + } - wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp)); + if (!ret) + wpa_ft_rrb_oui_send(wpa_auth, src_addr, + FT_PACKET_R0KH_R1KH_RESP, packet, + packet_len); + +out: + os_free(plain); + os_free(packet); return 0; } -static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx) +/* @returns 0 on success + * -1 on error + * -2 if FR_RRB_PAIRWISE is missing + */ +static int wpa_ft_rrb_rx_r1(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, u8 type, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + const char *msgtype, u8 *s1kh_id_out, + int (*cb)(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer)) +{ + u8 *plain = NULL; + size_t plain_len = 0; + struct ft_remote_r0kh *r0kh, *r0kh_wildcard; + const u8 *key; + size_t key_len; + int seq_ret; + const u8 *f_r1kh_id, *f_s1kh_id, *f_r0kh_id; + const u8 *f_pmk_r1_name, *f_pairwise, *f_pmk_r1; + const u8 *f_expires_in; + size_t f_r1kh_id_len, f_s1kh_id_len, f_r0kh_id_len; + const u8 *f_identity, *f_radius_cui; + const u8 *f_session_timeout; + size_t f_pmk_r1_name_len, f_pairwise_len, f_pmk_r1_len; + size_t f_expires_in_len; + size_t f_identity_len, f_radius_cui_len; + size_t f_session_timeout_len; + int pairwise; + int ret = -1; + int expires_in; + int session_timeout; + struct vlan_description vlan; + size_t pmk_r1_len; + + RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, msgtype, -1); + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", f_r0kh_id, f_r0kh_id_len); + + RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, msgtype, FT_R1KH_ID_LEN); + wpa_printf(MSG_DEBUG, "FT: R1KH-ID=" MACSTR, MAC2STR(f_r1kh_id)); + + if (wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id)) { + wpa_printf(MSG_DEBUG, "FT: R1KH-ID mismatch"); + goto out; + } + + wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, &r0kh, + &r0kh_wildcard); + if (r0kh) { + key = r0kh->key; + key_len = sizeof(r0kh->key); + } else if (r0kh_wildcard) { + wpa_printf(MSG_DEBUG, "FT: Using wildcard R0KH-ID"); + key = r0kh_wildcard->key; + key_len = sizeof(r0kh_wildcard->key); + } else { + goto out; + } + + seq_ret = FT_RRB_SEQ_DROP; + if (r0kh) { + seq_ret = wpa_ft_rrb_seq_chk(r0kh->seq, src_addr, enc, enc_len, + auth, auth_len, msgtype, + cb ? 0 : 1); + } + if (cb && r0kh_wildcard && + (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) { + /* wildcard: r0kh-id unknown or changed addr -> do a seq req */ + seq_ret = FT_RRB_SEQ_DEFER; + } + + if (seq_ret == FT_RRB_SEQ_DROP) + goto out; + + if (wpa_ft_rrb_decrypt(key, key_len, enc, enc_len, auth, auth_len, + src_addr, type, &plain, &plain_len) < 0) + goto out; + + if (!r0kh) + r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, src_addr, + f_r0kh_id, f_r0kh_id_len, + wpa_auth->conf.rkh_pos_timeout); + if (!r0kh) + goto out; + + if (seq_ret == FT_RRB_SEQ_DEFER) { + wpa_ft_rrb_seq_req(wpa_auth, r0kh->seq, src_addr, f_r0kh_id, + f_r0kh_id_len, f_r1kh_id, key, key_len, + enc, enc_len, auth, auth_len, cb); + goto out; + } + + wpa_ft_rrb_seq_accept(wpa_auth, r0kh->seq, src_addr, auth, auth_len, + msgtype); + wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, + wpa_auth->conf.rkh_pos_timeout); + + RRB_GET(FT_RRB_S1KH_ID, s1kh_id, msgtype, ETH_ALEN); + wpa_printf(MSG_DEBUG, "FT: S1KH-ID=" MACSTR, MAC2STR(f_s1kh_id)); + + if (s1kh_id_out) + os_memcpy(s1kh_id_out, f_s1kh_id, ETH_ALEN); + + ret = -2; + RRB_GET(FT_RRB_PAIRWISE, pairwise, msgtype, sizeof(le16)); + wpa_hexdump(MSG_DEBUG, "FT: pairwise", f_pairwise, f_pairwise_len); + + ret = -1; + RRB_GET(FT_RRB_PMK_R1_NAME, pmk_r1_name, msgtype, WPA_PMK_NAME_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", + f_pmk_r1_name, WPA_PMK_NAME_LEN); + + pmk_r1_len = PMK_LEN; + if (wpa_ft_rrb_get_tlv(plain, plain_len, FT_RRB_PMK_R1, &f_pmk_r1_len, + &f_pmk_r1) == 0 && + (f_pmk_r1_len == PMK_LEN || f_pmk_r1_len == SHA384_MAC_LEN)) + pmk_r1_len = f_pmk_r1_len; + RRB_GET(FT_RRB_PMK_R1, pmk_r1, msgtype, pmk_r1_len); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f_pmk_r1, pmk_r1_len); + + pairwise = WPA_GET_LE16(f_pairwise); + + RRB_GET_OPTIONAL(FT_RRB_EXPIRES_IN, expires_in, msgtype, + sizeof(le16)); + if (f_expires_in) + expires_in = WPA_GET_LE16(f_expires_in); + else + expires_in = 0; + + wpa_printf(MSG_DEBUG, "FT: PMK-R1 %s - expires_in=%d", msgtype, + expires_in); + + if (wpa_ft_rrb_get_tlv_vlan(plain, plain_len, &vlan) < 0) { + wpa_printf(MSG_DEBUG, "FT: Cannot parse vlan"); + wpa_ft_rrb_dump(plain, plain_len); + goto out; + } + + wpa_printf(MSG_DEBUG, "FT: vlan %d%s", + le_to_host16(vlan.untagged), vlan.tagged[0] ? "+" : ""); + + RRB_GET_OPTIONAL(FT_RRB_IDENTITY, identity, msgtype, -1); + if (f_identity) + wpa_hexdump_ascii(MSG_DEBUG, "FT: Identity", f_identity, + f_identity_len); + + RRB_GET_OPTIONAL(FT_RRB_RADIUS_CUI, radius_cui, msgtype, -1); + if (f_radius_cui) + wpa_hexdump_ascii(MSG_DEBUG, "FT: CUI", f_radius_cui, + f_radius_cui_len); + + RRB_GET_OPTIONAL(FT_RRB_SESSION_TIMEOUT, session_timeout, msgtype, + sizeof(le32)); + if (f_session_timeout) + session_timeout = WPA_GET_LE32(f_session_timeout); + else + session_timeout = 0; + wpa_printf(MSG_DEBUG, "FT: session_timeout %d", session_timeout); + + if (wpa_ft_store_pmk_r1(wpa_auth, f_s1kh_id, f_pmk_r1, pmk_r1_len, + f_pmk_r1_name, + pairwise, &vlan, expires_in, session_timeout, + f_identity, f_identity_len, f_radius_cui, + f_radius_cui_len) < 0) + goto out; + + ret = 0; +out: + if (plain) { + os_memset(plain, 0, plain_len); + os_free(plain); + } + + return ret; + +} + + +static void ft_finish_pull(struct wpa_state_machine *sm) { - struct wpa_state_machine *sm = eloop_ctx; int res; u8 *resp_ies; size_t resp_ies_len; u16 status; + if (!sm->ft_pending_cb || !sm->ft_pending_req_ies) + return; + res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies), wpabuf_len(sm->ft_pending_req_ies), &resp_ies, &resp_ies_len); + if (res < 0) { + /* this loop is broken by ft_pending_pull_left_retries */ + wpa_printf(MSG_DEBUG, + "FT: Callback postponed until response is available"); + return; + } wpabuf_free(sm->ft_pending_req_ies); sm->ft_pending_req_ies = NULL; - if (res < 0) - res = WLAN_STATUS_UNSPECIFIED_FAILURE; status = res; wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR " - status %u", MAC2STR(sm->addr), status); @@ -1433,171 +3794,383 @@ static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx) } -static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx) +struct ft_get_sta_ctx { + const u8 *nonce; + const u8 *s1kh_id; + struct wpa_state_machine *sm; +}; + + +static int ft_get_sta_cb(struct wpa_state_machine *sm, void *ctx) { - struct ft_r0kh_r1kh_resp_frame *frame = ctx; + struct ft_get_sta_ctx *info = ctx; - if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0) - return 0; - if (os_memcmp(frame->nonce, sm->ft_pending_pull_nonce, - FT_R0KH_R1KH_PULL_NONCE_LEN) != 0) - return 0; - if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL) + if ((info->s1kh_id && + os_memcmp(info->s1kh_id, sm->addr, ETH_ALEN) != 0) || + os_memcmp(info->nonce, sm->ft_pending_pull_nonce, + FT_RRB_NONCE_LEN) != 0 || + sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL) return 0; - wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for " - MACSTR " - process from timeout", MAC2STR(sm->addr)); - eloop_register_timeout(0, 0, ft_pull_resp_cb_finish, sm, NULL); + info->sm = sm; + return 1; } static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, const u8 *src_addr, - const u8 *data, size_t data_len) + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer) { - struct ft_r0kh_r1kh_resp_frame f; - const u8 *crypt; - u8 *plain; - struct ft_remote_r0kh *r0kh; - int pairwise, res; + const char *msgtype = "pull response"; + int nak, ret = -1; + struct ft_get_sta_ctx ctx; + u8 s1kh_id[ETH_ALEN]; + const u8 *f_nonce; + size_t f_nonce_len; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response"); - if (data_len < sizeof(f)) - return -1; + RRB_GET_AUTH(FT_RRB_NONCE, nonce, msgtype, FT_RRB_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: nonce", f_nonce, f_nonce_len); - r0kh = wpa_auth->conf.r0kh_list; - while (r0kh) { - if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) - break; - r0kh = r0kh->next; - } - if (r0kh == NULL) { - wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " - "PMK-R0 pull response source address " MACSTR, - MAC2STR(src_addr)); + os_memset(&ctx, 0, sizeof(ctx)); + ctx.nonce = f_nonce; + if (!wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) { + /* nonce not found */ + wpa_printf(MSG_DEBUG, "FT: Invalid nonce"); return -1; } - crypt = data + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce); - os_memset(&f, 0, sizeof(f)); - plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce); - /* aes_unwrap() does not support inplace decryption, so use a temporary - * buffer for the data. */ - if (aes_unwrap(r0kh->key, sizeof(r0kh->key), - (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, - crypt, plain) < 0) { - wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " - "response from " MACSTR, MAC2STR(src_addr)); - return -1; + ret = wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_RESP, + enc, enc_len, auth, auth_len, msgtype, s1kh_id, + no_defer ? NULL : &wpa_ft_rrb_rx_resp); + if (ret == -2) { + ret = 0; + nak = 1; + } else { + nak = 0; } - - if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder, - FT_R1KH_ID_LEN) != 0) { - wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a " - "matching R1KH-ID"); + if (ret < 0) return -1; - } - pairwise = le_to_host16(f.pairwise); - wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", - f.nonce, sizeof(f.nonce)); - wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID=" - MACSTR " pairwise=0x%x", - MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1", - f.pmk_r1, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name", - f.pmk_r1_name, WPA_PMK_NAME_LEN); - - res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, - pairwise); - wpa_printf(MSG_DEBUG, "FT: Look for pending pull request"); - wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &f); - os_memset(f.pmk_r1, 0, PMK_LEN); + ctx.s1kh_id = s1kh_id; + if (wpa_auth_for_each_sta(wpa_auth, ft_get_sta_cb, &ctx)) { + wpa_printf(MSG_DEBUG, + "FT: Response to a pending pull request for " MACSTR, + MAC2STR(ctx.sm->addr)); + eloop_cancel_timeout(wpa_ft_expire_pull, ctx.sm, NULL); + if (nak) + ctx.sm->ft_pending_pull_left_retries = 0; + ft_finish_pull(ctx.sm); + } - return res ? 0 : -1; +out: + return ret; } static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, const u8 *src_addr, - const u8 *data, size_t data_len) + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, int no_defer) { - struct ft_r0kh_r1kh_push_frame f; - const u8 *crypt; - u8 *plain; - struct ft_remote_r0kh *r0kh; - struct os_time now; - os_time_t tsend; - int pairwise; + const char *msgtype = "push"; wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push"); - if (data_len < sizeof(f)) + if (wpa_ft_rrb_rx_r1(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_PUSH, + enc, enc_len, auth, auth_len, msgtype, NULL, + no_defer ? NULL : wpa_ft_rrb_rx_push) < 0) return -1; - r0kh = wpa_auth->conf.r0kh_list; - while (r0kh) { - if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) - break; - r0kh = r0kh->next; + return 0; +} + + +static int wpa_ft_rrb_rx_seq(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, int type, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + struct ft_remote_seq **rkh_seq, + u8 **key, size_t *key_len, + struct ft_remote_r0kh **r0kh_out, + struct ft_remote_r1kh **r1kh_out, + struct ft_remote_r0kh **r0kh_wildcard_out, + struct ft_remote_r1kh **r1kh_wildcard_out) +{ + struct ft_remote_r0kh *r0kh = NULL; + struct ft_remote_r1kh *r1kh = NULL; + const u8 *f_r0kh_id, *f_r1kh_id; + size_t f_r0kh_id_len, f_r1kh_id_len; + int to_r0kh, to_r1kh; + u8 *plain = NULL; + size_t plain_len = 0; + struct ft_remote_r0kh *r0kh_wildcard; + struct ft_remote_r1kh *r1kh_wildcard; + + RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1); + RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN); + + to_r0kh = !wpa_ft_rrb_check_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len); + to_r1kh = !wpa_ft_rrb_check_r1kh(wpa_auth, f_r1kh_id); + + if (to_r0kh && to_r1kh) { + wpa_printf(MSG_DEBUG, "FT: seq - local R0KH-ID and R1KH-ID"); + goto out; } - if (r0kh == NULL) { - wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " - "PMK-R0 push source address " MACSTR, - MAC2STR(src_addr)); - return -1; + + if (!to_r0kh && !to_r1kh) { + wpa_printf(MSG_DEBUG, "FT: seq - remote R0KH-ID and R1KH-ID"); + goto out; } - crypt = data + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp); - os_memset(&f, 0, sizeof(f)); - plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame, - timestamp); - /* aes_unwrap() does not support inplace decryption, so use a temporary - * buffer for the data. */ - if (aes_unwrap(r0kh->key, sizeof(r0kh->key), - (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, - crypt, plain) < 0) { - wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from " - MACSTR, MAC2STR(src_addr)); - return -1; + if (!to_r0kh) { + wpa_ft_rrb_lookup_r0kh(wpa_auth, f_r0kh_id, f_r0kh_id_len, + &r0kh, &r0kh_wildcard); + if (!r0kh_wildcard && + (!r0kh || os_memcmp(r0kh->addr, src_addr, ETH_ALEN) != 0)) { + wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID", + f_r0kh_id, f_r0kh_id_len); + goto out; + } + if (r0kh) { + *key = r0kh->key; + *key_len = sizeof(r0kh->key); + } else { + *key = r0kh_wildcard->key; + *key_len = sizeof(r0kh_wildcard->key); + } } - os_get_time(&now); - tsend = WPA_GET_LE32(f.timestamp); - if ((now.sec > tsend && now.sec - tsend > 60) || - (now.sec < tsend && tsend - now.sec > 60)) { - wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid " - "timestamp: sender time %d own time %d\n", - (int) tsend, (int) now.sec); - return -1; + if (!to_r1kh) { + wpa_ft_rrb_lookup_r1kh(wpa_auth, f_r1kh_id, &r1kh, + &r1kh_wildcard); + if (!r1kh_wildcard && + (!r1kh || os_memcmp(r1kh->addr, src_addr, ETH_ALEN) != 0)) { + wpa_hexdump(MSG_DEBUG, "FT: Did not find R1KH-ID", + f_r1kh_id, FT_R1KH_ID_LEN); + goto out; + } + if (r1kh) { + *key = r1kh->key; + *key_len = sizeof(r1kh->key); + } else { + *key = r1kh_wildcard->key; + *key_len = sizeof(r1kh_wildcard->key); + } } - if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder, - FT_R1KH_ID_LEN) != 0) { - wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching " - "R1KH-ID (received " MACSTR " own " MACSTR ")", - MAC2STR(f.r1kh_id), - MAC2STR(wpa_auth->conf.r1_key_holder)); - return -1; + if (wpa_ft_rrb_decrypt(*key, *key_len, enc, enc_len, auth, auth_len, + src_addr, type, &plain, &plain_len) < 0) + goto out; + + os_free(plain); + + if (!to_r0kh) { + if (!r0kh) + r0kh = wpa_ft_rrb_add_r0kh(wpa_auth, r0kh_wildcard, + src_addr, f_r0kh_id, + f_r0kh_id_len, + ftRRBseqTimeout); + if (!r0kh) + goto out; + + wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, ftRRBseqTimeout); + *rkh_seq = r0kh->seq; + if (r0kh_out) + *r0kh_out = r0kh; + if (r0kh_wildcard_out) + *r0kh_wildcard_out = r0kh_wildcard; + } + + if (!to_r1kh) { + if (!r1kh) + r1kh = wpa_ft_rrb_add_r1kh(wpa_auth, r1kh_wildcard, + src_addr, f_r1kh_id, + ftRRBseqTimeout); + if (!r1kh) + goto out; + + wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, ftRRBseqTimeout); + *rkh_seq = r1kh->seq; + if (r1kh_out) + *r1kh_out = r1kh; + if (r1kh_wildcard_out) + *r1kh_wildcard_out = r1kh_wildcard; + } + + return 0; +out: + return -1; +} + + +static int wpa_ft_rrb_rx_seq_req(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer) +{ + int ret = -1; + struct ft_rrb_seq f_seq; + const u8 *f_nonce, *f_r0kh_id, *f_r1kh_id; + size_t f_nonce_len, f_r0kh_id_len, f_r1kh_id_len; + struct ft_remote_seq *rkh_seq = NULL; + u8 *packet = NULL, *key = NULL; + size_t packet_len = 0, key_len = 0; + struct tlv_list seq_resp_auth[5]; + + wpa_printf(MSG_DEBUG, "FT: Received sequence number request"); + + if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_REQ, + enc, enc_len, auth, auth_len, &rkh_seq, &key, + &key_len, NULL, NULL, NULL, NULL) < 0) + goto out; + + RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq request", FT_RRB_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: seq request - nonce", f_nonce, f_nonce_len); + + RRB_GET_AUTH(FT_RRB_R0KH_ID, r0kh_id, "seq", -1); + RRB_GET_AUTH(FT_RRB_R1KH_ID, r1kh_id, "seq", FT_R1KH_ID_LEN); + + if (wpa_ft_new_seq(rkh_seq, &f_seq) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); + goto out; + } + + seq_resp_auth[0].type = FT_RRB_NONCE; + seq_resp_auth[0].len = f_nonce_len; + seq_resp_auth[0].data = f_nonce; + seq_resp_auth[1].type = FT_RRB_SEQ; + seq_resp_auth[1].len = sizeof(f_seq); + seq_resp_auth[1].data = (u8 *) &f_seq; + seq_resp_auth[2].type = FT_RRB_R0KH_ID; + seq_resp_auth[2].len = f_r0kh_id_len; + seq_resp_auth[2].data = f_r0kh_id; + seq_resp_auth[3].type = FT_RRB_R1KH_ID; + seq_resp_auth[3].len = FT_R1KH_ID_LEN; + seq_resp_auth[3].data = f_r1kh_id; + seq_resp_auth[4].type = FT_RRB_LAST_EMPTY; + seq_resp_auth[4].len = 0; + seq_resp_auth[4].data = NULL; + + if (wpa_ft_rrb_build(key, key_len, NULL, NULL, seq_resp_auth, NULL, + wpa_auth->addr, FT_PACKET_R0KH_R1KH_SEQ_RESP, + &packet, &packet_len) < 0) + goto out; + + wpa_ft_rrb_oui_send(wpa_auth, src_addr, + FT_PACKET_R0KH_R1KH_SEQ_RESP, packet, + packet_len); + +out: + os_free(packet); + + return ret; +} + + +static int wpa_ft_rrb_rx_seq_resp(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *enc, size_t enc_len, + const u8 *auth, size_t auth_len, + int no_defer) +{ + u8 *key = NULL; + size_t key_len = 0; + struct ft_remote_r0kh *r0kh = NULL, *r0kh_wildcard = NULL; + struct ft_remote_r1kh *r1kh = NULL, *r1kh_wildcard = NULL; + const u8 *f_nonce, *f_seq; + size_t f_nonce_len, f_seq_len; + struct ft_remote_seq *rkh_seq = NULL; + struct ft_remote_item *item; + struct os_reltime now, now_remote; + int seq_ret, found; + const struct ft_rrb_seq *msg_both; + u32 msg_dom, msg_seq; + + wpa_printf(MSG_DEBUG, "FT: Received sequence number response"); + + if (wpa_ft_rrb_rx_seq(wpa_auth, src_addr, FT_PACKET_R0KH_R1KH_SEQ_RESP, + enc, enc_len, auth, auth_len, &rkh_seq, &key, + &key_len, &r0kh, &r1kh, &r0kh_wildcard, + &r1kh_wildcard) < 0) + goto out; + + RRB_GET_AUTH(FT_RRB_NONCE, nonce, "seq response", FT_RRB_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: seq response - nonce", f_nonce, + f_nonce_len); + + found = 0; + dl_list_for_each(item, &rkh_seq->rx.queue, struct ft_remote_item, + list) { + if (os_memcmp_const(f_nonce, item->nonce, + FT_RRB_NONCE_LEN) != 0 || + os_get_reltime(&now) < 0 || + os_reltime_expired(&now, &item->nonce_ts, ftRRBseqTimeout)) + continue; + + found = 1; + break; + } + if (!found) { + wpa_printf(MSG_DEBUG, "FT: seq response - bad nonce"); + goto out; } - pairwise = le_to_host16(f.pairwise); - wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID=" - MACSTR " pairwise=0x%x", - MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 push - PMK-R1", - f.pmk_r1, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name", - f.pmk_r1_name, WPA_PMK_NAME_LEN); + if (r0kh) { + wpa_ft_rrb_r0kh_replenish(wpa_auth, r0kh, + wpa_auth->conf.rkh_pos_timeout); + if (r0kh_wildcard) + os_memcpy(r0kh->addr, src_addr, ETH_ALEN); + } - wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, - pairwise); - os_memset(f.pmk_r1, 0, PMK_LEN); + if (r1kh) { + wpa_ft_rrb_r1kh_replenish(wpa_auth, r1kh, + wpa_auth->conf.rkh_pos_timeout); + if (r1kh_wildcard) + os_memcpy(r1kh->addr, src_addr, ETH_ALEN); + } + + seq_ret = wpa_ft_rrb_seq_chk(rkh_seq, src_addr, enc, enc_len, auth, + auth_len, "seq response", 1); + if (seq_ret == FT_RRB_SEQ_OK) { + wpa_printf(MSG_DEBUG, "FT: seq response - valid seq number"); + wpa_ft_rrb_seq_accept(wpa_auth, rkh_seq, src_addr, auth, + auth_len, "seq response"); + } else { + wpa_printf(MSG_DEBUG, "FT: seq response - reset seq number"); + + RRB_GET_AUTH(FT_RRB_SEQ, seq, "seq response", + sizeof(*msg_both)); + msg_both = (const struct ft_rrb_seq *) f_seq; + + msg_dom = le_to_host32(msg_both->dom); + msg_seq = le_to_host32(msg_both->seq); + now_remote.sec = le_to_host32(msg_both->ts); + now_remote.usec = 0; + + rkh_seq->rx.num_last = 2; + rkh_seq->rx.dom = msg_dom; + rkh_seq->rx.offsetidx = 0; + /* Accept some older, possibly cached packets as well */ + rkh_seq->rx.last[0] = msg_seq - FT_REMOTE_SEQ_BACKLOG - + dl_list_len(&rkh_seq->rx.queue); + rkh_seq->rx.last[1] = msg_seq; + + /* local time - offset = remote time + * <=> local time - remote time = offset */ + os_reltime_sub(&now, &now_remote, &rkh_seq->rx.time_offset); + } + + wpa_ft_rrb_seq_flush(wpa_auth, rkh_seq, 1); return 0; +out: + return -1; } @@ -1642,13 +4215,6 @@ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, return -1; } - if (frame->packet_type == FT_PACKET_R0KH_R1KH_PULL) - return wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len); - if (frame->packet_type == FT_PACKET_R0KH_R1KH_RESP) - return wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len); - if (frame->packet_type == FT_PACKET_R0KH_R1KH_PUSH) - return wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len); - wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen); if (alen < 1 + 1 + 2 * ETH_ALEN) { @@ -1726,65 +4292,137 @@ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, } -static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, - struct wpa_ft_pmk_r0_sa *pmk_r0, - struct ft_remote_r1kh *r1kh, - const u8 *s1kh_id, int pairwise) -{ - struct ft_r0kh_r1kh_push_frame frame, f; - struct os_time now; - const u8 *plain; - u8 *crypt; - - os_memset(&frame, 0, sizeof(frame)); - frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; - frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH; - frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN); - os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN); - - /* aes_wrap() does not support inplace encryption, so use a temporary - * buffer for the data. */ - os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN); - os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); - os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN); - wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id, - s1kh_id, f.pmk_r1, f.pmk_r1_name); - wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id)); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name, - WPA_PMK_NAME_LEN); - os_get_time(&now); - WPA_PUT_LE32(f.timestamp, now.sec); - f.pairwise = host_to_le16(pairwise); - os_memset(f.pad, 0, sizeof(f.pad)); - plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame, - timestamp); - crypt = ((u8 *) &frame) + offsetof(struct ft_r0kh_r1kh_push_frame, - timestamp); - if (aes_wrap(r1kh->key, sizeof(r1kh->key), - (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, - plain, crypt) < 0) +void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, + const u8 *dst_addr, u8 oui_suffix, const u8 *data, + size_t data_len) +{ + const u8 *auth, *enc; + size_t alen, elen; + int no_defer = 0; + + wpa_printf(MSG_DEBUG, "FT: RRB-OUI received frame from remote AP " + MACSTR, MAC2STR(src_addr)); + wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame - oui_suffix=%d", oui_suffix); + + if (is_multicast_ether_addr(src_addr)) { + wpa_printf(MSG_DEBUG, + "FT: RRB-OUI received frame from multicast address " + MACSTR, MAC2STR(src_addr)); return; + } + + if (is_multicast_ether_addr(dst_addr)) { + wpa_printf(MSG_DEBUG, + "FT: RRB-OUI received frame from remote AP " MACSTR + " to multicast address " MACSTR, + MAC2STR(src_addr), MAC2STR(dst_addr)); + no_defer = 1; + } + + if (data_len < sizeof(u16)) { + wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short"); + return; + } - wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame)); + alen = WPA_GET_LE16(data); + if (data_len < sizeof(u16) + alen) { + wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame too short"); + return; + } + + auth = data + sizeof(u16); + enc = data + sizeof(u16) + alen; + elen = data_len - sizeof(u16) - alen; + + switch (oui_suffix) { + case FT_PACKET_R0KH_R1KH_PULL: + wpa_ft_rrb_rx_pull(wpa_auth, src_addr, enc, elen, auth, alen, + no_defer); + break; + case FT_PACKET_R0KH_R1KH_RESP: + wpa_ft_rrb_rx_resp(wpa_auth, src_addr, enc, elen, auth, alen, + no_defer); + break; + case FT_PACKET_R0KH_R1KH_PUSH: + wpa_ft_rrb_rx_push(wpa_auth, src_addr, enc, elen, auth, alen, + no_defer); + break; + case FT_PACKET_R0KH_R1KH_SEQ_REQ: + wpa_ft_rrb_rx_seq_req(wpa_auth, src_addr, enc, elen, auth, alen, + no_defer); + break; + case FT_PACKET_R0KH_R1KH_SEQ_RESP: + wpa_ft_rrb_rx_seq_resp(wpa_auth, src_addr, enc, elen, auth, + alen, no_defer); + break; + } +} + + +static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, + struct wpa_ft_pmk_r0_sa *pmk_r0, + struct ft_remote_r1kh *r1kh, + const u8 *s1kh_id) +{ + u8 *packet; + size_t packet_len; + struct ft_rrb_seq f_seq; + struct tlv_list push[] = { + { .type = FT_RRB_S1KH_ID, .len = ETH_ALEN, + .data = s1kh_id }, + { .type = FT_RRB_PMK_R0_NAME, .len = WPA_PMK_NAME_LEN, + .data = pmk_r0->pmk_r0_name }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + struct tlv_list push_auth[] = { + { .type = FT_RRB_SEQ, .len = sizeof(f_seq), + .data = (u8 *) &f_seq }, + { .type = FT_RRB_R0KH_ID, + .len = wpa_auth->conf.r0_key_holder_len, + .data = wpa_auth->conf.r0_key_holder }, + { .type = FT_RRB_R1KH_ID, .len = FT_R1KH_ID_LEN, + .data = r1kh->id }, + { .type = FT_RRB_LAST_EMPTY, .len = 0, .data = NULL }, + }; + + if (wpa_ft_new_seq(r1kh->seq, &f_seq) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to get seq num"); + return -1; + } + + if (wpa_ft_rrb_build_r0(r1kh->key, sizeof(r1kh->key), push, pmk_r0, + r1kh->id, s1kh_id, push_auth, wpa_auth->addr, + FT_PACKET_R0KH_R1KH_PUSH, + &packet, &packet_len) < 0) + return -1; + + wpa_ft_rrb_oui_send(wpa_auth, r1kh->addr, FT_PACKET_R0KH_R1KH_PUSH, + packet, packet_len); + + os_free(packet); + return 0; } void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr) { - struct wpa_ft_pmk_r0_sa *r0; + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r0_sa *r0, *r0found = NULL; struct ft_remote_r1kh *r1kh; if (!wpa_auth->conf.pmk_r1_push) return; + if (!wpa_auth->conf.r1kh_list) + return; - r0 = wpa_auth->ft_pmk_cache->pmk_r0; - while (r0) { - if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) + dl_list_for_each(r0, &cache->pmk_r0, struct wpa_ft_pmk_r0_sa, list) { + if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) { + r0found = r0; break; - r0 = r0->next; + } } + r0 = r0found; if (r0 == NULL || r0->pmk_r1_pushed) return; r0->pmk_r1_pushed = 1; @@ -1792,11 +4430,14 @@ void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr) wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs " "for STA " MACSTR, MAC2STR(addr)); - r1kh = wpa_auth->conf.r1kh_list; - while (r1kh) { - wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise); - r1kh = r1kh->next; + for (r1kh = *wpa_auth->conf.r1kh_list; r1kh; r1kh = r1kh->next) { + if (is_zero_ether_addr(r1kh->addr) || + is_zero_ether_addr(r1kh->id)) + continue; + if (wpa_ft_rrb_init_r1kh_seq(r1kh) < 0) + continue; + wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr); } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 21424147e443e..812740301c8b2 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -9,6 +9,8 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" +#include "utils/list.h" #include "common/ieee802_11_defs.h" #include "common/sae.h" #include "common/wpa_ctrl.h" @@ -17,6 +19,7 @@ #include "eapol_auth/eapol_auth_sm_i.h" #include "eap_server/eap.h" #include "l2_packet/l2_packet.h" +#include "eth_p_oui.h" #include "hostapd.h" #include "ieee802_1x.h" #include "preauth_auth.h" @@ -24,6 +27,7 @@ #include "tkip_countermeasures.h" #include "ap_drv_ops.h" #include "ap_config.h" +#include "pmksa_cache_auth.h" #include "wpa_auth.h" #include "wpa_auth_glue.h" @@ -41,10 +45,13 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wconf->wpa_strict_rekey = conf->wpa_strict_rekey; wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey; wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey; + wconf->wpa_group_update_count = conf->wpa_group_update_count; + wconf->wpa_disable_eapol_key_retries = + conf->wpa_disable_eapol_key_retries; + wconf->wpa_pairwise_update_count = conf->wpa_pairwise_update_count; wconf->rsn_pairwise = conf->rsn_pairwise; wconf->rsn_preauth = conf->rsn_preauth; wconf->eapol_version = conf->eapol_version; - wconf->peerkey = conf->peerkey; wconf->wmm_enabled = conf->wmm_enabled; wconf->wmm_uapsd = conf->wmm_uapsd; wconf->disable_pmksa_caching = conf->disable_pmksa_caching; @@ -52,8 +59,9 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, #ifdef CONFIG_IEEE80211W wconf->ieee80211w = conf->ieee80211w; wconf->group_mgmt_cipher = conf->group_mgmt_cipher; + wconf->sae_require_mfp = conf->sae_require_mfp; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP wconf->ssid_len = conf->ssid.ssid_len; if (wconf->ssid_len > SSID_MAX_LEN) wconf->ssid_len = SSID_MAX_LEN; @@ -68,12 +76,18 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, } os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN); wconf->r0_key_lifetime = conf->r0_key_lifetime; + wconf->r1_max_key_lifetime = conf->r1_max_key_lifetime; wconf->reassociation_deadline = conf->reassociation_deadline; - wconf->r0kh_list = conf->r0kh_list; - wconf->r1kh_list = conf->r1kh_list; + wconf->rkh_pos_timeout = conf->rkh_pos_timeout; + wconf->rkh_neg_timeout = conf->rkh_neg_timeout; + wconf->rkh_pull_timeout = conf->rkh_pull_timeout; + wconf->rkh_pull_retries = conf->rkh_pull_retries; + wconf->r0kh_list = &conf->r0kh_list; + wconf->r1kh_list = &conf->r1kh_list; wconf->pmk_r1_push = conf->pmk_r1_push; wconf->ft_over_ds = conf->ft_over_ds; -#endif /* CONFIG_IEEE80211R */ + wconf->ft_psk_generate_local = conf->ft_psk_generate_local; +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_HS20 wconf->disable_gtk = conf->disable_dgaf; if (conf->osen) { @@ -107,6 +121,11 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4); os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4); #endif /* CONFIG_P2P */ +#ifdef CONFIG_FILS + wconf->fils_cache_id_set = conf->fils_cache_id_set; + os_memcpy(wconf->fils_cache_id, conf->fils_cache_id, + FILS_CACHE_ID_LEN); +#endif /* CONFIG_FILS */ } @@ -223,20 +242,47 @@ static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr, static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, const u8 *p2p_dev_addr, - const u8 *prev_psk) + const u8 *prev_psk, size_t *psk_len) { struct hostapd_data *hapd = ctx; struct sta_info *sta = ap_get_sta(hapd, addr); const u8 *psk; + if (psk_len) + *psk_len = PMK_LEN; + #ifdef CONFIG_SAE if (sta && sta->auth_alg == WLAN_AUTH_SAE) { if (!sta->sae || prev_psk) return NULL; return sta->sae->pmk; } + if (sta && wpa_auth_uses_sae(sta->wpa_sm)) { + wpa_printf(MSG_DEBUG, + "No PSK for STA trying to use SAE with PMKSA caching"); + return NULL; + } #endif /* CONFIG_SAE */ +#ifdef CONFIG_OWE + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && + sta && sta->owe_pmk) { + if (psk_len) + *psk_len = sta->owe_pmk_len; + return sta->owe_pmk; + } + if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) && sta) { + struct rsn_pmksa_cache_entry *sa; + + sa = wpa_auth_sta_get_pmksa(sta->wpa_sm); + if (sa && sa->akmp == WPA_KEY_MGMT_OWE) { + if (psk_len) + *psk_len = sa->pmk_len; + return sa->pmk; + } + } +#endif /* CONFIG_OWE */ + psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk); /* * This is about to iterate over all psks, prev_psk gives the last @@ -307,6 +353,37 @@ static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, return -1; } +#ifdef CONFIG_TESTING_OPTIONS + if (addr && !is_broadcast_ether_addr(addr)) { + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + if (sta) { + sta->last_tk_alg = alg; + sta->last_tk_key_idx = idx; + if (key) + os_memcpy(sta->last_tk, key, key_len); + sta->last_tk_len = key_len; + } +#ifdef CONFIG_IEEE80211W + } else if (alg == WPA_ALG_IGTK || + alg == WPA_ALG_BIP_GMAC_128 || + alg == WPA_ALG_BIP_GMAC_256 || + alg == WPA_ALG_BIP_CMAC_256) { + hapd->last_igtk_alg = alg; + hapd->last_igtk_key_idx = idx; + if (key) + os_memcpy(hapd->last_igtk, key, key_len); + hapd->last_igtk_len = key_len; +#endif /* CONFIG_IEEE80211W */ + } else { + hapd->last_gtk_alg = alg; + hapd->last_gtk_key_idx = idx; + if (key) + os_memcpy(hapd->last_gtk, key, key_len); + hapd->last_gtk_len = key_len; + } +#endif /* CONFIG_TESTING_OPTIONS */ return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0, key, key_len); } @@ -401,7 +478,32 @@ static int hostapd_wpa_auth_for_each_auth( } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP + +struct wpa_ft_rrb_rx_later_data { + struct dl_list list; + u8 addr[ETH_ALEN]; + size_t data_len; + /* followed by data_len octets of data */ +}; + +static void hostapd_wpa_ft_rrb_rx_later(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct wpa_ft_rrb_rx_later_data *data, *n; + + dl_list_for_each_safe(data, n, &hapd->l2_queue, + struct wpa_ft_rrb_rx_later_data, list) { + if (hapd->wpa_auth) { + wpa_ft_rrb_rx(hapd->wpa_auth, data->addr, + (const u8 *) (data + 1), + data->data_len); + } + dl_list_del(&data->list); + os_free(data); + } +} + struct wpa_auth_ft_iface_iter_data { struct hostapd_data *src_hapd; @@ -414,33 +516,54 @@ struct wpa_auth_ft_iface_iter_data { static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx) { struct wpa_auth_ft_iface_iter_data *idata = ctx; + struct wpa_ft_rrb_rx_later_data *data; struct hostapd_data *hapd; size_t j; for (j = 0; j < iface->num_bss; j++) { hapd = iface->bss[j]; - if (hapd == idata->src_hapd) - continue; - if (!hapd->wpa_auth) + if (hapd == idata->src_hapd || + !hapd->wpa_auth || + os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) != 0) continue; - if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) == 0) { - wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to " - "locally managed BSS " MACSTR "@%s -> " - MACSTR "@%s", - MAC2STR(idata->src_hapd->own_addr), - idata->src_hapd->conf->iface, - MAC2STR(hapd->own_addr), hapd->conf->iface); - wpa_ft_rrb_rx(hapd->wpa_auth, - idata->src_hapd->own_addr, - idata->data, idata->data_len); + + wpa_printf(MSG_DEBUG, + "FT: Send RRB data directly to locally managed BSS " + MACSTR "@%s -> " MACSTR "@%s", + MAC2STR(idata->src_hapd->own_addr), + idata->src_hapd->conf->iface, + MAC2STR(hapd->own_addr), hapd->conf->iface); + + /* Defer wpa_ft_rrb_rx() until next eloop step as this is + * when it would be triggered when reading from a socket. + * This avoids + * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv, + * that is calling hapd0:recv handler from within + * hapd0:send directly. + */ + data = os_zalloc(sizeof(*data) + idata->data_len); + if (!data) return 1; - } + + os_memcpy(data->addr, idata->src_hapd->own_addr, ETH_ALEN); + os_memcpy(data + 1, idata->data, idata->data_len); + data->data_len = idata->data_len; + + dl_list_add(&hapd->l2_queue, &data->list); + + if (!eloop_is_timeout_registered(hostapd_wpa_ft_rrb_rx_later, + hapd, NULL)) + eloop_register_timeout(0, 0, + hostapd_wpa_ft_rrb_rx_later, + hapd, NULL); + + return 1; } return 0; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, @@ -465,7 +588,7 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, } #endif /* CONFIG_TESTING_OPTIONS */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (proto == ETH_P_RRB && hapd->iface->interfaces && hapd->iface->interfaces->for_each_interface) { int res; @@ -480,7 +603,7 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, if (res == 1) return data_len; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if (hapd->driver && hapd->driver->send_ether) return hapd->driver->send_ether(hapd->drv_priv, dst, @@ -503,7 +626,157 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_ETH_P_OUI +static struct eth_p_oui_ctx * hostapd_wpa_get_oui(struct hostapd_data *hapd, + u8 oui_suffix) +{ + switch (oui_suffix) { +#ifdef CONFIG_IEEE80211R_AP + case FT_PACKET_R0KH_R1KH_PULL: + return hapd->oui_pull; + case FT_PACKET_R0KH_R1KH_RESP: + return hapd->oui_resp; + case FT_PACKET_R0KH_R1KH_PUSH: + return hapd->oui_push; + case FT_PACKET_R0KH_R1KH_SEQ_REQ: + return hapd->oui_sreq; + case FT_PACKET_R0KH_R1KH_SEQ_RESP: + return hapd->oui_sresp; +#endif /* CONFIG_IEEE80211R_AP */ + default: + return NULL; + } +} +#endif /* CONFIG_ETH_P_OUI */ + + +#ifdef CONFIG_IEEE80211R_AP + +struct oui_deliver_later_data { + struct dl_list list; + u8 src_addr[ETH_ALEN]; + u8 dst_addr[ETH_ALEN]; + size_t data_len; + u8 oui_suffix; + /* followed by data_len octets of data */ +}; + +static void hostapd_oui_deliver_later(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct oui_deliver_later_data *data, *n; + struct eth_p_oui_ctx *oui_ctx; + + dl_list_for_each_safe(data, n, &hapd->l2_oui_queue, + struct oui_deliver_later_data, list) { + oui_ctx = hostapd_wpa_get_oui(hapd, data->oui_suffix); + if (hapd->wpa_auth && oui_ctx) { + eth_p_oui_deliver(oui_ctx, data->src_addr, + data->dst_addr, + (const u8 *) (data + 1), + data->data_len); + } + dl_list_del(&data->list); + os_free(data); + } +} + + +struct wpa_auth_oui_iface_iter_data { + struct hostapd_data *src_hapd; + const u8 *dst_addr; + const u8 *data; + size_t data_len; + u8 oui_suffix; +}; + +static int hostapd_wpa_auth_oui_iter(struct hostapd_iface *iface, void *ctx) +{ + struct wpa_auth_oui_iface_iter_data *idata = ctx; + struct oui_deliver_later_data *data; + struct hostapd_data *hapd; + size_t j; + + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + if (hapd == idata->src_hapd) + continue; + if (!is_multicast_ether_addr(idata->dst_addr) && + os_memcmp(hapd->own_addr, idata->dst_addr, ETH_ALEN) != 0) + continue; + + /* defer eth_p_oui_deliver until next eloop step as this is + * when it would be triggerd from reading from sock + * This avoids + * hapd0:send -> hapd1:recv -> hapd1:send -> hapd0:recv, + * that is calling hapd0:recv handler from within + * hapd0:send directly. + */ + data = os_zalloc(sizeof(*data) + idata->data_len); + if (!data) + return 1; + + os_memcpy(data->src_addr, idata->src_hapd->own_addr, ETH_ALEN); + os_memcpy(data->dst_addr, idata->dst_addr, ETH_ALEN); + os_memcpy(data + 1, idata->data, idata->data_len); + data->data_len = idata->data_len; + data->oui_suffix = idata->oui_suffix; + + dl_list_add(&hapd->l2_oui_queue, &data->list); + + if (!eloop_is_timeout_registered(hostapd_oui_deliver_later, + hapd, NULL)) + eloop_register_timeout(0, 0, + hostapd_oui_deliver_later, + hapd, NULL); + + return 1; + } + + return 0; +} + +#endif /* CONFIG_IEEE80211R_AP */ + + +static int hostapd_wpa_auth_send_oui(void *ctx, const u8 *dst, u8 oui_suffix, + const u8 *data, size_t data_len) +{ +#ifdef CONFIG_ETH_P_OUI + struct hostapd_data *hapd = ctx; + struct eth_p_oui_ctx *oui_ctx; + +#ifdef CONFIG_IEEE80211R_AP + if (hapd->iface->interfaces && + hapd->iface->interfaces->for_each_interface) { + struct wpa_auth_oui_iface_iter_data idata; + int res; + + idata.src_hapd = hapd; + idata.dst_addr = dst; + idata.data = data; + idata.data_len = data_len; + idata.oui_suffix = oui_suffix; + res = hapd->iface->interfaces->for_each_interface( + hapd->iface->interfaces, hostapd_wpa_auth_oui_iter, + &idata); + if (res == 1) + return data_len; + } +#endif /* CONFIG_IEEE80211R_AP */ + + oui_ctx = hostapd_wpa_get_oui(hapd, oui_suffix); + if (!oui_ctx) + return -1; + + return eth_p_oui_send(oui_ctx, hapd->own_addr, dst, data, data_len); +#else /* CONFIG_ETH_P_OUI */ + return -1; +#endif /* CONFIG_ETH_P_OUI */ +} + + +#ifdef CONFIG_IEEE80211R_AP static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst, const u8 *data, size_t data_len) @@ -563,6 +836,244 @@ hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr) } +static int hostapd_wpa_auth_set_vlan(void *ctx, const u8 *sta_addr, + struct vlan_description *vlan) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, sta_addr); + if (!sta || !sta->wpa_sm) + return -1; + + if (vlan->notempty && + !hostapd_vlan_valid(hapd->conf->vlan, vlan)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Invalid VLAN %d%s received from FT", + vlan->untagged, vlan->tagged[0] ? "+" : ""); + return -1; + } + + if (ap_sta_set_vlan(hapd, sta, vlan) < 0) + return -1; + /* Configure wpa_group for GTK but ignore error due to driver not + * knowing this STA. */ + ap_sta_bind_vlan(hapd, sta); + + if (sta->vlan_id) + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); + + return 0; +} + + +static int hostapd_wpa_auth_get_vlan(void *ctx, const u8 *sta_addr, + struct vlan_description *vlan) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, sta_addr); + if (!sta) + return -1; + + if (sta->vlan_desc) + *vlan = *sta->vlan_desc; + else + os_memset(vlan, 0, sizeof(*vlan)); + + return 0; +} + + +static int +hostapd_wpa_auth_set_identity(void *ctx, const u8 *sta_addr, + const u8 *identity, size_t identity_len) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, sta_addr); + if (!sta) + return -1; + + os_free(sta->identity); + sta->identity = NULL; + + if (sta->eapol_sm) { + os_free(sta->eapol_sm->identity); + sta->eapol_sm->identity = NULL; + sta->eapol_sm->identity_len = 0; + } + + if (!identity_len) + return 0; + + /* sta->identity is NULL terminated */ + sta->identity = os_zalloc(identity_len + 1); + if (!sta->identity) + return -1; + os_memcpy(sta->identity, identity, identity_len); + + if (sta->eapol_sm) { + sta->eapol_sm->identity = os_zalloc(identity_len); + if (!sta->eapol_sm->identity) + return -1; + os_memcpy(sta->eapol_sm->identity, identity, identity_len); + sta->eapol_sm->identity_len = identity_len; + } + + return 0; +} + + +static size_t +hostapd_wpa_auth_get_identity(void *ctx, const u8 *sta_addr, const u8 **buf) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + size_t len; + char *identity; + + sta = ap_get_sta(hapd, sta_addr); + if (!sta) + return 0; + + *buf = ieee802_1x_get_identity(sta->eapol_sm, &len); + if (*buf && len) + return len; + + if (!sta->identity) { + *buf = NULL; + return 0; + } + + identity = sta->identity; + len = os_strlen(identity); + *buf = (u8 *) identity; + + return len; +} + + +static int +hostapd_wpa_auth_set_radius_cui(void *ctx, const u8 *sta_addr, + const u8 *radius_cui, size_t radius_cui_len) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, sta_addr); + if (!sta) + return -1; + + os_free(sta->radius_cui); + sta->radius_cui = NULL; + + if (sta->eapol_sm) { + wpabuf_free(sta->eapol_sm->radius_cui); + sta->eapol_sm->radius_cui = NULL; + } + + if (!radius_cui) + return 0; + + /* sta->radius_cui is NULL terminated */ + sta->radius_cui = os_zalloc(radius_cui_len + 1); + if (!sta->radius_cui) + return -1; + os_memcpy(sta->radius_cui, radius_cui, radius_cui_len); + + if (sta->eapol_sm) { + sta->eapol_sm->radius_cui = wpabuf_alloc_copy(radius_cui, + radius_cui_len); + if (!sta->eapol_sm->radius_cui) + return -1; + } + + return 0; +} + + +static size_t +hostapd_wpa_auth_get_radius_cui(void *ctx, const u8 *sta_addr, const u8 **buf) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + struct wpabuf *b; + size_t len; + char *radius_cui; + + sta = ap_get_sta(hapd, sta_addr); + if (!sta) + return 0; + + b = ieee802_1x_get_radius_cui(sta->eapol_sm); + if (b) { + len = wpabuf_len(b); + *buf = wpabuf_head(b); + return len; + } + + if (!sta->radius_cui) { + *buf = NULL; + return 0; + } + + radius_cui = sta->radius_cui; + len = os_strlen(radius_cui); + *buf = (u8 *) radius_cui; + + return len; +} + + +static void hostapd_wpa_auth_set_session_timeout(void *ctx, const u8 *sta_addr, + int session_timeout) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_get_sta(hapd, sta_addr); + if (!sta) + return; + + if (session_timeout) { + os_get_reltime(&sta->session_timeout); + sta->session_timeout.sec += session_timeout; + sta->session_timeout_set = 1; + ap_sta_session_timeout(hapd, sta, session_timeout); + } else { + sta->session_timeout_set = 0; + ap_sta_no_session_timeout(hapd, sta); + } +} + + +static int hostapd_wpa_auth_get_session_timeout(void *ctx, const u8 *sta_addr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + struct os_reltime now, remaining; + + sta = ap_get_sta(hapd, sta_addr); + if (!sta || !sta->session_timeout_set) + return 0; + + os_get_reltime(&now); + if (os_reltime_before(&sta->session_timeout, &now)) { + /* already expired, return >0 as timeout was set */ + return 1; + } + + os_reltime_sub(&sta->session_timeout, &now, &remaining); + + return (remaining.sec > 0) ? remaining.sec : 1; +} + + static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) { @@ -581,6 +1092,22 @@ static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, } +static void hostapd_rrb_oui_receive(void *ctx, const u8 *src_addr, + const u8 *dst_addr, u8 oui_suffix, + const u8 *buf, size_t len) +{ + struct hostapd_data *hapd = ctx; + + wpa_printf(MSG_DEBUG, "FT: RRB received packet " MACSTR " -> " + MACSTR, MAC2STR(src_addr), MAC2STR(dst_addr)); + if (!is_multicast_ether_addr(dst_addr) && + os_memcmp(hapd->own_addr, dst_addr, ETH_ALEN) != 0) + return; + wpa_ft_rrb_oui_rx(hapd->wpa_auth, src_addr, dst_addr, oui_suffix, buf, + len); +} + + static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr, u8 *tspec_ie, size_t tspec_ielen) { @@ -588,13 +1115,94 @@ static int hostapd_wpa_auth_add_tspec(void *ctx, const u8 *sta_addr, return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen); } -#endif /* CONFIG_IEEE80211R */ + + +static int hostapd_wpa_register_ft_oui(struct hostapd_data *hapd, + const char *ft_iface) +{ + hapd->oui_pull = eth_p_oui_register(hapd, ft_iface, + FT_PACKET_R0KH_R1KH_PULL, + hostapd_rrb_oui_receive, hapd); + if (!hapd->oui_pull) + return -1; + + hapd->oui_resp = eth_p_oui_register(hapd, ft_iface, + FT_PACKET_R0KH_R1KH_RESP, + hostapd_rrb_oui_receive, hapd); + if (!hapd->oui_resp) + return -1; + + hapd->oui_push = eth_p_oui_register(hapd, ft_iface, + FT_PACKET_R0KH_R1KH_PUSH, + hostapd_rrb_oui_receive, hapd); + if (!hapd->oui_push) + return -1; + + hapd->oui_sreq = eth_p_oui_register(hapd, ft_iface, + FT_PACKET_R0KH_R1KH_SEQ_REQ, + hostapd_rrb_oui_receive, hapd); + if (!hapd->oui_sreq) + return -1; + + hapd->oui_sresp = eth_p_oui_register(hapd, ft_iface, + FT_PACKET_R0KH_R1KH_SEQ_RESP, + hostapd_rrb_oui_receive, hapd); + if (!hapd->oui_sresp) + return -1; + + return 0; +} + + +static void hostapd_wpa_unregister_ft_oui(struct hostapd_data *hapd) +{ + eth_p_oui_unregister(hapd->oui_pull); + hapd->oui_pull = NULL; + eth_p_oui_unregister(hapd->oui_resp); + hapd->oui_resp = NULL; + eth_p_oui_unregister(hapd->oui_push); + hapd->oui_push = NULL; + eth_p_oui_unregister(hapd->oui_sreq); + hapd->oui_sreq = NULL; + eth_p_oui_unregister(hapd->oui_sresp); + hapd->oui_sresp = NULL; +} +#endif /* CONFIG_IEEE80211R_AP */ int hostapd_setup_wpa(struct hostapd_data *hapd) { struct wpa_auth_config _conf; - struct wpa_auth_callbacks cb; + static const struct wpa_auth_callbacks cb = { + .logger = hostapd_wpa_auth_logger, + .disconnect = hostapd_wpa_auth_disconnect, + .mic_failure_report = hostapd_wpa_auth_mic_failure_report, + .psk_failure_report = hostapd_wpa_auth_psk_failure_report, + .set_eapol = hostapd_wpa_auth_set_eapol, + .get_eapol = hostapd_wpa_auth_get_eapol, + .get_psk = hostapd_wpa_auth_get_psk, + .get_msk = hostapd_wpa_auth_get_msk, + .set_key = hostapd_wpa_auth_set_key, + .get_seqnum = hostapd_wpa_auth_get_seqnum, + .send_eapol = hostapd_wpa_auth_send_eapol, + .for_each_sta = hostapd_wpa_auth_for_each_sta, + .for_each_auth = hostapd_wpa_auth_for_each_auth, + .send_ether = hostapd_wpa_auth_send_ether, + .send_oui = hostapd_wpa_auth_send_oui, +#ifdef CONFIG_IEEE80211R_AP + .send_ft_action = hostapd_wpa_auth_send_ft_action, + .add_sta = hostapd_wpa_auth_add_sta, + .add_tspec = hostapd_wpa_auth_add_tspec, + .set_vlan = hostapd_wpa_auth_set_vlan, + .get_vlan = hostapd_wpa_auth_get_vlan, + .set_identity = hostapd_wpa_auth_set_identity, + .get_identity = hostapd_wpa_auth_get_identity, + .set_radius_cui = hostapd_wpa_auth_set_radius_cui, + .get_radius_cui = hostapd_wpa_auth_get_radius_cui, + .set_session_timeout = hostapd_wpa_auth_set_session_timeout, + .get_session_timeout = hostapd_wpa_auth_get_session_timeout, +#endif /* CONFIG_IEEE80211R_AP */ + }; const u8 *wpa_ie; size_t wpa_ie_len; @@ -603,28 +1211,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) _conf.tx_status = 1; if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME) _conf.ap_mlme = 1; - os_memset(&cb, 0, sizeof(cb)); - cb.ctx = hapd; - cb.logger = hostapd_wpa_auth_logger; - cb.disconnect = hostapd_wpa_auth_disconnect; - cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report; - cb.psk_failure_report = hostapd_wpa_auth_psk_failure_report; - cb.set_eapol = hostapd_wpa_auth_set_eapol; - cb.get_eapol = hostapd_wpa_auth_get_eapol; - cb.get_psk = hostapd_wpa_auth_get_psk; - cb.get_msk = hostapd_wpa_auth_get_msk; - cb.set_key = hostapd_wpa_auth_set_key; - cb.get_seqnum = hostapd_wpa_auth_get_seqnum; - cb.send_eapol = hostapd_wpa_auth_send_eapol; - cb.for_each_sta = hostapd_wpa_auth_for_each_sta; - cb.for_each_auth = hostapd_wpa_auth_for_each_auth; - cb.send_ether = hostapd_wpa_auth_send_ether; -#ifdef CONFIG_IEEE80211R - cb.send_ft_action = hostapd_wpa_auth_send_ft_action; - cb.add_sta = hostapd_wpa_auth_add_sta; - cb.add_tspec = hostapd_wpa_auth_add_tspec; -#endif /* CONFIG_IEEE80211R */ - hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb); + hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd); if (hapd->wpa_auth == NULL) { wpa_printf(MSG_ERROR, "WPA initialization failed."); return -1; @@ -649,12 +1236,14 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) return -1; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (!hostapd_drv_none(hapd) && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) { - hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ? - hapd->conf->bridge : - hapd->conf->iface, NULL, ETH_P_RRB, + const char *ft_iface; + + ft_iface = hapd->conf->bridge[0] ? hapd->conf->bridge : + hapd->conf->iface; + hapd->l2 = l2_packet_init(ft_iface, NULL, ETH_P_RRB, hostapd_rrb_receive, hapd, 1); if (hapd->l2 == NULL && (hapd->driver == NULL || @@ -663,8 +1252,14 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) "interface"); return -1; } + + if (hostapd_wpa_register_ft_oui(hapd, ft_iface)) { + wpa_printf(MSG_ERROR, + "Failed to open ETH_P_OUI interface"); + return -1; + } } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ return 0; @@ -702,8 +1297,13 @@ void hostapd_deinit_wpa(struct hostapd_data *hapd) } ieee802_1x_deinit(hapd); -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP + eloop_cancel_timeout(hostapd_wpa_ft_rrb_rx_later, hapd, ELOOP_ALL_CTX); + hostapd_wpa_ft_rrb_rx_later(hapd, NULL); /* flush without delivering */ + eloop_cancel_timeout(hostapd_oui_deliver_later, hapd, ELOOP_ALL_CTX); + hostapd_oui_deliver_later(hapd, NULL); /* flush without delivering */ l2_packet_deinit(hapd->l2); hapd->l2 = NULL; -#endif /* CONFIG_IEEE80211R */ + hostapd_wpa_unregister_ft_oui(hapd); +#endif /* CONFIG_IEEE80211R_AP */ } diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index 7fd8f05fa8ff8..b1cea1b49b14c 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -9,18 +9,13 @@ #ifndef WPA_AUTH_I_H #define WPA_AUTH_I_H +#include "utils/list.h" + /* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */ #define RSNA_MAX_EAPOL_RETRIES 4 struct wpa_group; -struct wpa_stsl_negotiation { - struct wpa_stsl_negotiation *next; - u8 initiator[ETH_ALEN]; - u8 peer[ETH_ALEN]; -}; - - struct wpa_state_machine { struct wpa_authenticator *wpa_auth; struct wpa_group *group; @@ -48,8 +43,9 @@ struct wpa_state_machine { Boolean AuthenticationRequest; Boolean ReAuthenticationRequest; Boolean Disconnect; - int TimeoutCtr; - int GTimeoutCtr; + u16 disconnect_reason; /* specific reason code to use with Disconnect */ + u32 TimeoutCtr; + u32 GTimeoutCtr; Boolean TimeoutEvt; Boolean EAPOLKeyReceived; Boolean EAPOLKeyPairwise; @@ -62,6 +58,7 @@ struct wpa_state_machine { u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN]; u8 PMK[PMK_LEN_MAX]; unsigned int pmk_len; + u8 pmkid[PMKID_LEN]; /* valid if pmkid_set == 1 */ struct wpa_ptk PTK; Boolean PTK_valid; Boolean pairwise_set; @@ -89,11 +86,12 @@ struct wpa_state_machine { unsigned int rx_eapol_key_secure:1; unsigned int update_snonce:1; unsigned int alt_snonce_valid:1; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP unsigned int ft_completed:1; unsigned int pmk_r1_name_valid:1; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ unsigned int is_wnmsleep:1; + unsigned int pmkid_set:1; u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; int req_replay_counter_used; @@ -113,8 +111,9 @@ struct wpa_state_machine { u32 dot11RSNAStatsTKIPLocalMICFailures; u32 dot11RSNAStatsTKIPRemoteMICFailures; -#ifdef CONFIG_IEEE80211R - u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */ +#ifdef CONFIG_IEEE80211R_AP + u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the + * first 384 bits of MSK */ size_t xxkey_len; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth * Request */ @@ -129,16 +128,30 @@ struct wpa_state_machine { const u8 *ies, size_t ies_len); void *ft_pending_cb_ctx; struct wpabuf *ft_pending_req_ies; - u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; + u8 ft_pending_pull_nonce[FT_RRB_NONCE_LEN]; u8 ft_pending_auth_transaction; u8 ft_pending_current_ap[ETH_ALEN]; -#endif /* CONFIG_IEEE80211R */ + int ft_pending_pull_left_retries; +#endif /* CONFIG_IEEE80211R_AP */ int pending_1_of_4_timeout; #ifdef CONFIG_P2P u8 ip_addr[4]; #endif /* CONFIG_P2P */ + +#ifdef CONFIG_FILS + u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN]; + u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN]; + size_t fils_key_auth_len; + unsigned int fils_completed:1; +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_TESTING_OPTIONS + void (*eapol_status_cb)(void *ctx1, void *ctx2); + void *eapol_status_cb_ctx1; + void *eapol_status_cb_ctx2; +#endif /* CONFIG_TESTING_OPTIONS */ }; @@ -194,10 +207,9 @@ struct wpa_authenticator { unsigned int dot11RSNATKIPCounterMeasuresInvoked; unsigned int dot11RSNA4WayHandshakeFailures; - struct wpa_stsl_negotiation *stsl_negotiations; - struct wpa_auth_config conf; - struct wpa_auth_callbacks cb; + const struct wpa_auth_callbacks *cb; + void *cb_ctx; u8 *wpa_ie; size_t wpa_ie_len; @@ -213,6 +225,38 @@ struct wpa_authenticator { }; +#ifdef CONFIG_IEEE80211R_AP + +#define FT_REMOTE_SEQ_BACKLOG 16 +struct ft_remote_seq_rx { + u32 dom; + struct os_reltime time_offset; /* local time - offset = remote time */ + + /* accepted sequence numbers: (offset ... offset + 0x40000000] + * (except those in last) + * dropped sequence numbers: (offset - 0x40000000 ... offset] + * all others trigger SEQ_REQ message (except first message) + */ + u32 last[FT_REMOTE_SEQ_BACKLOG]; + unsigned int num_last; + u32 offsetidx; + + struct dl_list queue; /* send nonces + rrb msgs awaiting seq resp */ +}; + +struct ft_remote_seq_tx { + u32 dom; /* non zero if initialized */ + u32 seq; +}; + +struct ft_remote_seq { + struct ft_remote_seq_rx rx; + struct ft_remote_seq_tx tx; +}; + +#endif /* CONFIG_IEEE80211R_AP */ + + int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, const u8 *pmkid); void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, @@ -231,24 +275,10 @@ int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, int (*cb)(struct wpa_authenticator *a, void *ctx), void *cb_ctx); -#ifdef CONFIG_PEERKEY -int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, - struct wpa_stsl_negotiation *neg); -void wpa_smk_error(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, - const u8 *key_data, size_t key_data_len); -void wpa_smk_m1(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key, - const u8 *key_data, size_t key_data_len); -void wpa_smk_m3(struct wpa_authenticator *wpa_auth, - struct wpa_state_machine *sm, struct wpa_eapol_key *key, - const u8 *key_data, size_t key_data_len); -#endif /* CONFIG_PEERKEY */ - -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len); -int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, - size_t r0kh_id_len, +int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384, + const u8 *r0kh_id, size_t r0kh_id_len, const u8 *anonce, const u8 *snonce, u8 *buf, size_t len, const u8 *subelem, size_t subelem_len); @@ -257,6 +287,8 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void); void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache); void wpa_ft_install_ptk(struct wpa_state_machine *sm); -#endif /* CONFIG_IEEE80211R */ +int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm, const u8 *pmk_r0, + const u8 *pmk_r0_name); +#endif /* CONFIG_IEEE80211R_AP */ #endif /* WPA_AUTH_I_H */ diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index f79783b91929b..cdcc5de39d457 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -1,6 +1,6 @@ /* * hostapd - WPA/RSN IE and KDE definitions - * Copyright (c) 2004-2015, 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. @@ -164,18 +164,25 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, pos += RSN_SELECTOR_LEN; num_suites++; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); pos += RSN_SELECTOR_LEN; num_suites++; } +#ifdef CONFIG_SHA384 + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_SHA384 */ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); pos += RSN_SELECTOR_LEN; num_suites++; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); @@ -210,6 +217,51 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, pos += RSN_SELECTOR_LEN; num_suites++; } +#ifdef CONFIG_FILS + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#ifdef CONFIG_IEEE80211R_AP + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_IEEE80211R_AP */ +#endif /* CONFIG_FILS */ +#ifdef CONFIG_OWE + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_DPP */ +#ifdef CONFIG_HS20 + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_OSEN) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_HS20 */ #ifdef CONFIG_RSN_TESTING if (rsn_testing) { @@ -230,8 +282,6 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, capab = 0; if (conf->rsn_preauth) capab |= WPA_CAPABILITY_PREAUTH; - if (conf->peerkey) - capab |= WPA_CAPABILITY_PEERKEY_ENABLED; if (conf->wmm_enabled) { /* 4 PTKSA replay counters when using WMM */ capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); @@ -407,7 +457,7 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) return res; pos += res; } -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) { res = wpa_write_mdie(&wpa_auth->conf, pos, buf + sizeof(buf) - pos); @@ -415,7 +465,7 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) return res; pos += res; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ if (wpa_auth->conf.wpa & WPA_PROTO_WPA) { res = wpa_write_wpa_ie(&wpa_auth->conf, pos, buf + sizeof(buf) - pos); @@ -474,7 +524,8 @@ static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx) int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, const u8 *wpa_ie, size_t wpa_ie_len, - const u8 *mdie, size_t mdie_len) + const u8 *mdie, size_t mdie_len, + const u8 *owe_dh, size_t owe_dh_len) { struct wpa_ie_data data; int ciphers, key_mgmt, res, version; @@ -509,12 +560,28 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_FILS +#ifdef CONFIG_IEEE80211R_AP + else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) + selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384; + else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) + selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256; +#endif /* CONFIG_IEEE80211R_AP */ + else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA384) + selector = RSN_AUTH_KEY_MGMT_FILS_SHA384; + else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA256) + selector = RSN_AUTH_KEY_MGMT_FILS_SHA256; +#endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R_AP +#ifdef CONFIG_SHA384 + else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) + selector = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384; +#endif /* CONFIG_SHA384 */ else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) selector = RSN_AUTH_KEY_MGMT_FT_802_1X; else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) selector = RSN_AUTH_KEY_MGMT_FT_PSK; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256; @@ -531,6 +598,18 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; else if (data.key_mgmt & WPA_KEY_MGMT_PSK) selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; +#ifdef CONFIG_OWE + else if (data.key_mgmt & WPA_KEY_MGMT_OWE) + selector = RSN_AUTH_KEY_MGMT_OWE; +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + else if (data.key_mgmt & WPA_KEY_MGMT_DPP) + selector = RSN_AUTH_KEY_MGMT_DPP; +#endif /* CONFIG_DPP */ +#ifdef CONFIG_HS20 + else if (data.key_mgmt & WPA_KEY_MGMT_OSEN) + selector = RSN_AUTH_KEY_MGMT_OSEN; +#endif /* CONFIG_HS20 */ wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector; selector = wpa_cipher_to_suite(WPA_PROTO_RSN, @@ -591,12 +670,28 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_FILS +#ifdef CONFIG_IEEE80211R_AP + else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384; + else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256; +#endif /* CONFIG_IEEE80211R_AP */ + else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA384; + else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA256; +#endif /* CONFIG_FILS */ +#ifdef CONFIG_IEEE80211R_AP +#ifdef CONFIG_SHA384 + else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X_SHA384; +#endif /* CONFIG_SHA384 */ else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; else if (key_mgmt & WPA_KEY_MGMT_FT_PSK) sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_IEEE80211W else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256; @@ -611,6 +706,18 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, #endif /* CONFIG_SAE */ else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; +#ifdef CONFIG_OWE + else if (key_mgmt & WPA_KEY_MGMT_OWE) + sm->wpa_key_mgmt = WPA_KEY_MGMT_OWE; +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + else if (key_mgmt & WPA_KEY_MGMT_DPP) + sm->wpa_key_mgmt = WPA_KEY_MGMT_DPP; +#endif /* CONFIG_DPP */ +#ifdef CONFIG_HS20 + else if (key_mgmt & WPA_KEY_MGMT_OSEN) + sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN; +#endif /* CONFIG_HS20 */ else sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK; @@ -634,12 +741,6 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, return WPA_MGMT_FRAME_PROTECTION_VIOLATION; } - if (ciphers & WPA_CIPHER_TKIP) { - wpa_printf(MSG_DEBUG, "Management frame protection " - "cannot use TKIP"); - return WPA_MGMT_FRAME_PROTECTION_VIOLATION; - } - if (data.mgmt_group_cipher != wpa_auth->conf.group_mgmt_cipher) { wpa_printf(MSG_DEBUG, "Unsupported management group " @@ -648,14 +749,31 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, } } +#ifdef CONFIG_SAE + if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_OPTIONAL && + wpa_auth->conf.sae_require_mfp && + wpa_key_mgmt_sae(sm->wpa_key_mgmt) && + !(data.capabilities & WPA_CAPABILITY_MFPC)) { + wpa_printf(MSG_DEBUG, + "Management frame protection required with SAE, but client did not enable it"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } +#endif /* CONFIG_SAE */ + if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION || !(data.capabilities & WPA_CAPABILITY_MFPC)) sm->mgmt_frame_prot = 0; else sm->mgmt_frame_prot = 1; + + if (sm->mgmt_frame_prot && (ciphers & WPA_CIPHER_TKIP)) { + wpa_printf(MSG_DEBUG, + "Management frame protection cannot use TKIP"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) { wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but " @@ -668,8 +786,25 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, "MDIE", mdie, MOBILITY_DOMAIN_ID_LEN); return WPA_INVALID_MDIE; } + } else if (mdie != NULL) { + wpa_printf(MSG_DEBUG, + "RSN: Trying to use non-FT AKM suite, but MDIE included"); + return WPA_INVALID_AKMP; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ + +#ifdef CONFIG_OWE + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OWE && !owe_dh) { + wpa_printf(MSG_DEBUG, + "OWE: No Diffie-Hellman Parameter element"); + return WPA_INVALID_AKMP; + } + if (sm->wpa_key_mgmt != WPA_KEY_MGMT_OWE && owe_dh) { + wpa_printf(MSG_DEBUG, + "OWE: Unexpected Diffie-Hellman Parameter element with non-OWE AKM"); + return WPA_INVALID_AKMP; + } +#endif /* CONFIG_OWE */ sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0); if (sm->pairwise < 0) @@ -723,6 +858,23 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN); } +#ifdef CONFIG_SAE + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE && data.num_pmkid && + !sm->pmksa) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "No PMKSA cache entry found for SAE"); + return WPA_INVALID_PMKID; + } +#endif /* CONFIG_SAE */ + +#ifdef CONFIG_DPP + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && !sm->pmksa) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "No PMKSA cache entry found for DPP"); + return WPA_INVALID_PMKID; + } +#endif /* CONFIG_DPP */ + if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) { os_free(sm->wpa_ie); sm->wpa_ie = os_malloc(wpa_ie_len); @@ -815,36 +967,6 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, return 0; } -#ifdef CONFIG_PEERKEY - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) { - ie->smk = pos + 2 + RSN_SELECTOR_LEN; - ie->smk_len = pos[1] - RSN_SELECTOR_LEN; - return 0; - } - - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) { - ie->nonce = pos + 2 + RSN_SELECTOR_LEN; - ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; - return 0; - } - - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) { - ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; - ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; - return 0; - } - - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) { - ie->error = pos + 2 + RSN_SELECTOR_LEN; - ie->error_len = pos[1] - RSN_SELECTOR_LEN; - return 0; - } -#endif /* CONFIG_PEERKEY */ - #ifdef CONFIG_IEEE80211W if (pos[1] > RSN_SELECTOR_LEN + 2 && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { @@ -908,14 +1030,14 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) if (*pos == WLAN_EID_RSN) { ie->rsn_ie = pos; ie->rsn_ie_len = pos[1] + 2; -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { ie->mdie = pos; ie->mdie_len = pos[1] + 2; } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) { ie->ftie = pos; ie->ftie_len = pos[1] + 2; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { ret = wpa_parse_generic(pos, end, ie); if (ret < 0) @@ -938,3 +1060,36 @@ int wpa_auth_uses_mfp(struct wpa_state_machine *sm) { return sm ? sm->mgmt_frame_prot : 0; } + + +#ifdef CONFIG_OWE +u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, + u8 *pos, size_t max_len, + const u8 *req_ies, size_t req_ies_len) +{ + int res; + struct wpa_auth_config *conf; + + if (!sm) + return pos; + conf = &sm->wpa_auth->conf; + +#ifdef CONFIG_TESTING_OPTIONS + if (conf->own_ie_override_len) { + if (max_len < conf->own_ie_override_len) + return NULL; + wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing", + conf->own_ie_override, conf->own_ie_override_len); + os_memcpy(pos, conf->own_ie_override, + conf->own_ie_override_len); + return pos + conf->own_ie_override_len; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + res = wpa_write_rsn_ie(conf, pos, max_len, + sm->pmksa ? sm->pmksa->pmkid : NULL); + if (res < 0) + return pos; + return pos + res; +} +#endif /* CONFIG_OWE */ diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h index d2067ba3112cf..73e433349049b 100644 --- a/src/ap/wpa_auth_ie.h +++ b/src/ap/wpa_auth_ie.h @@ -19,26 +19,16 @@ struct wpa_eapol_ie_parse { size_t gtk_len; const u8 *mac_addr; size_t mac_addr_len; -#ifdef CONFIG_PEERKEY - const u8 *smk; - size_t smk_len; - const u8 *nonce; - size_t nonce_len; - const u8 *lifetime; - size_t lifetime_len; - const u8 *error; - size_t error_len; -#endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211W const u8 *igtk; size_t igtk_len; #endif /* CONFIG_IEEE80211W */ -#ifdef CONFIG_IEEE80211R +#ifdef CONFIG_IEEE80211R_AP const u8 *mdie; size_t mdie_len; const u8 *ftie; size_t ftie_len; -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R_AP */ #ifdef CONFIG_P2P const u8 *ip_addr_req; const u8 *ip_addr_alloc; diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index 95b40da0f6bb4..5ec019971f372 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -1064,7 +1064,9 @@ int hostapd_init_wps(struct hostapd_data *hapd, if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) wps->auth_types |= WPS_AUTH_WPA2; - if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) { + if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | + WPA_CIPHER_CCMP_256 | + WPA_CIPHER_GCMP_256)) { wps->encr_types |= WPS_ENCR_AES; wps->encr_types_rsn |= WPS_ENCR_AES; } diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c index e0769c08e764a..0b596bbcc6313 100644 --- a/src/common/common_module_tests.c +++ b/src/common/common_module_tests.c @@ -53,12 +53,38 @@ static const struct ieee802_11_parse_test_data parse_tests[] = { 18, ParseOK, 9 }, { (u8 *) "\x8b\x00", 2, ParseOK, 1 }, { (u8 *) "\xdd\x04\x00\x90\x4c\x04", 6, ParseUnknown, 1 }, + { (u8 *) "\xed\x00", 2, ParseOK, 1 }, + { (u8 *) "\xef\x00", 2, ParseOK, 1 }, + { (u8 *) "\xef\x01\x11", 3, ParseOK, 1 }, + { (u8 *) "\xf0\x00", 2, ParseOK, 1 }, + { (u8 *) "\xf1\x00", 2, ParseOK, 1 }, + { (u8 *) "\xf1\x02\x11\x22", 4, ParseOK, 1 }, + { (u8 *) "\xf2\x00", 2, ParseOK, 1 }, + { (u8 *) "\xff\x00", 2, ParseUnknown, 1 }, + { (u8 *) "\xff\x01\x00", 3, ParseUnknown, 1 }, + { (u8 *) "\xff\x01\x01", 3, ParseOK, 1 }, + { (u8 *) "\xff\x02\x01\x00", 4, ParseOK, 1 }, + { (u8 *) "\xff\x01\x02", 3, ParseOK, 1 }, + { (u8 *) "\xff\x04\x02\x11\x22\x33", 6, ParseOK, 1 }, + { (u8 *) "\xff\x01\x04", 3, ParseOK, 1 }, + { (u8 *) "\xff\x01\x05", 3, ParseOK, 1 }, + { (u8 *) "\xff\x0d\x05\x11\x22\x33\x44\x55\x55\x11\x22\x33\x44\x55\x55", + 15, ParseOK, 1 }, + { (u8 *) "\xff\x01\x06", 3, ParseOK, 1 }, + { (u8 *) "\xff\x02\x06\x00", 4, ParseOK, 1 }, + { (u8 *) "\xff\x01\x07", 3, ParseOK, 1 }, + { (u8 *) "\xff\x09\x07\x11\x22\x33\x44\x55\x66\x77\x88", 11, + ParseOK, 1 }, + { (u8 *) "\xff\x01\x0c", 3, ParseOK, 1 }, + { (u8 *) "\xff\x02\x0c\x00", 4, ParseOK, 1 }, + { (u8 *) "\xff\x01\x0d", 3, ParseOK, 1 }, { NULL, 0, ParseOK, 0 } }; static int ieee802_11_parse_tests(void) { int i, ret = 0; + struct wpabuf *buf; wpa_printf(MSG_INFO, "ieee802_11_parse tests"); @@ -84,6 +110,35 @@ static int ieee802_11_parse_tests(void) ret = -1; } + buf = ieee802_11_vendor_ie_concat((const u8 *) "\xdd\x05\x11\x22\x33\x44\x01\xdd\x05\x11\x22\x33\x44\x02\x00\x01", + 16, 0x11223344); + do { + const u8 *pos; + + if (!buf) { + wpa_printf(MSG_ERROR, + "ieee802_11_vendor_ie_concat test 2 failed"); + ret = -1; + break; + } + + if (wpabuf_len(buf) != 2) { + wpa_printf(MSG_ERROR, + "ieee802_11_vendor_ie_concat test 3 failed"); + ret = -1; + break; + } + + pos = wpabuf_head(buf); + if (pos[0] != 0x01 || pos[1] != 0x02) { + wpa_printf(MSG_ERROR, + "ieee802_11_vendor_ie_concat test 3 failed"); + ret = -1; + break; + } + } while (0); + wpabuf_free(buf); + return ret; } diff --git a/src/common/ctrl_iface_common.c b/src/common/ctrl_iface_common.c index ebbe6ffdb385b..e26407dab9020 100644 --- a/src/common/ctrl_iface_common.c +++ b/src/common/ctrl_iface_common.c @@ -113,17 +113,53 @@ void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock, } +static int ctrl_set_events(struct wpa_ctrl_dst *dst, const char *input) +{ + const char *value; + int val; + + if (!input) + return 0; + + value = os_strchr(input, '='); + if (!value) + return -1; + value++; + val = atoi(value); + if (val < 0 || val > 1) + return -1; + + if (str_starts(input, "probe_rx_events=")) { + if (val) + dst->events |= WPA_EVENT_RX_PROBE_REQUEST; + else + dst->events &= ~WPA_EVENT_RX_PROBE_REQUEST; + } + + return 0; +} + + int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, - socklen_t fromlen) + socklen_t fromlen, const char *input) { struct wpa_ctrl_dst *dst; + /* Update event registration if already attached */ + dl_list_for_each(dst, ctrl_dst, struct wpa_ctrl_dst, list) { + if (!sockaddr_compare(from, fromlen, + &dst->addr, dst->addrlen)) + return ctrl_set_events(dst, input); + } + + /* New attachment */ dst = os_zalloc(sizeof(*dst)); if (dst == NULL) return -1; os_memcpy(&dst->addr, from, fromlen); dst->addrlen = fromlen; dst->debug_level = MSG_INFO; + ctrl_set_events(dst, input); dl_list_add(ctrl_dst, &dst->list); sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor attached", from, fromlen); diff --git a/src/common/ctrl_iface_common.h b/src/common/ctrl_iface_common.h index 0b6e3e740291d..85e258e938b63 100644 --- a/src/common/ctrl_iface_common.h +++ b/src/common/ctrl_iface_common.h @@ -11,6 +11,9 @@ #include "utils/list.h" +/* Events enable bits (wpa_ctrl_dst::events) */ +#define WPA_EVENT_RX_PROBE_REQUEST BIT(0) + /** * struct wpa_ctrl_dst - Data structure of control interface monitors * @@ -23,13 +26,14 @@ struct wpa_ctrl_dst { socklen_t addrlen; int debug_level; int errors; + u32 events; /* WPA_EVENT_* bitmap */ }; void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock, socklen_t socklen); int ctrl_iface_attach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, - socklen_t fromlen); + socklen_t fromlen, const char *input); int ctrl_iface_detach(struct dl_list *ctrl_dst, struct sockaddr_storage *from, socklen_t fromlen); int ctrl_iface_level(struct dl_list *ctrl_dst, struct sockaddr_storage *from, diff --git a/src/common/defs.h b/src/common/defs.h index 4f567945942e5..c968cd6cb82a5 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -1,6 +1,6 @@ /* * WPA Supplicant - Common definitions - * Copyright (c) 2004-2015, 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. @@ -51,16 +51,28 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean; #define WPA_KEY_MGMT_OSEN BIT(15) #define WPA_KEY_MGMT_IEEE8021X_SUITE_B BIT(16) #define WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 BIT(17) +#define WPA_KEY_MGMT_FILS_SHA256 BIT(18) +#define WPA_KEY_MGMT_FILS_SHA384 BIT(19) +#define WPA_KEY_MGMT_FT_FILS_SHA256 BIT(20) +#define WPA_KEY_MGMT_FT_FILS_SHA384 BIT(21) +#define WPA_KEY_MGMT_OWE BIT(22) +#define WPA_KEY_MGMT_DPP BIT(23) +#define WPA_KEY_MGMT_FT_IEEE8021X_SHA384 BIT(24) static inline int wpa_key_mgmt_wpa_ieee8021x(int akm) { return !!(akm & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_FT_IEEE8021X | + WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | WPA_KEY_MGMT_CCKM | WPA_KEY_MGMT_OSEN | WPA_KEY_MGMT_IEEE8021X_SHA256 | WPA_KEY_MGMT_IEEE8021X_SUITE_B | - WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)); + WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 | + WPA_KEY_MGMT_FILS_SHA256 | + WPA_KEY_MGMT_FILS_SHA384 | + WPA_KEY_MGMT_FT_FILS_SHA256 | + WPA_KEY_MGMT_FT_FILS_SHA384)); } static inline int wpa_key_mgmt_wpa_psk(int akm) @@ -76,7 +88,15 @@ static inline int wpa_key_mgmt_ft(int akm) { return !!(akm & (WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_FT_IEEE8021X | - WPA_KEY_MGMT_FT_SAE)); + WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | + WPA_KEY_MGMT_FT_SAE | + WPA_KEY_MGMT_FT_FILS_SHA256 | + WPA_KEY_MGMT_FT_FILS_SHA384)); +} + +static inline int wpa_key_mgmt_ft_psk(int akm) +{ + return !!(akm & WPA_KEY_MGMT_FT_PSK); } static inline int wpa_key_mgmt_sae(int akm) @@ -85,17 +105,32 @@ static inline int wpa_key_mgmt_sae(int akm) WPA_KEY_MGMT_FT_SAE)); } +static inline int wpa_key_mgmt_fils(int akm) +{ + return !!(akm & (WPA_KEY_MGMT_FILS_SHA256 | + WPA_KEY_MGMT_FILS_SHA384 | + WPA_KEY_MGMT_FT_FILS_SHA256 | + WPA_KEY_MGMT_FT_FILS_SHA384)); +} + static inline int wpa_key_mgmt_sha256(int akm) { return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 | WPA_KEY_MGMT_IEEE8021X_SHA256 | + WPA_KEY_MGMT_SAE | + WPA_KEY_MGMT_FT_SAE | WPA_KEY_MGMT_OSEN | - WPA_KEY_MGMT_IEEE8021X_SUITE_B)); + WPA_KEY_MGMT_IEEE8021X_SUITE_B | + WPA_KEY_MGMT_FILS_SHA256 | + WPA_KEY_MGMT_FT_FILS_SHA256)); } static inline int wpa_key_mgmt_sha384(int akm) { - return !!(akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192); + return !!(akm & (WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 | + WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | + WPA_KEY_MGMT_FILS_SHA384 | + WPA_KEY_MGMT_FT_FILS_SHA384)); } static inline int wpa_key_mgmt_suite_b(int akm) @@ -108,7 +143,10 @@ static inline int wpa_key_mgmt_wpa(int akm) { return wpa_key_mgmt_wpa_ieee8021x(akm) || wpa_key_mgmt_wpa_psk(akm) || - wpa_key_mgmt_sae(akm); + wpa_key_mgmt_fils(akm) || + wpa_key_mgmt_sae(akm) || + akm == WPA_KEY_MGMT_OWE || + akm == WPA_KEY_MGMT_DPP; } static inline int wpa_key_mgmt_wpa_any(int akm) @@ -132,7 +170,13 @@ static inline int wpa_key_mgmt_cckm(int akm) #define WPA_AUTH_ALG_LEAP BIT(2) #define WPA_AUTH_ALG_FT BIT(3) #define WPA_AUTH_ALG_SAE BIT(4) +#define WPA_AUTH_ALG_FILS BIT(5) +#define WPA_AUTH_ALG_FILS_SK_PFS BIT(6) +static inline int wpa_auth_alg_fils(int alg) +{ + return !!(alg & (WPA_AUTH_ALG_FILS | WPA_AUTH_ALG_FILS_SK_PFS)); +} enum wpa_alg { WPA_ALG_NONE, @@ -341,4 +385,18 @@ enum wpa_radio_work_band { BAND_60_GHZ = BIT(2), }; +enum beacon_rate_type { + BEACON_RATE_LEGACY, + BEACON_RATE_HT, + BEACON_RATE_VHT +}; + +enum eap_proxy_sim_state { + SIM_STATE_ERROR, +}; + +#define OCE_STA BIT(0) +#define OCE_STA_CFON BIT(1) +#define OCE_AP BIT(2) + #endif /* DEFS_H */ diff --git a/src/common/dhcp.h b/src/common/dhcp.h new file mode 100644 index 0000000000000..e38512c24d9f8 --- /dev/null +++ b/src/common/dhcp.h @@ -0,0 +1,263 @@ +/* + * DHCP definitions + * Copyright (c) 2014-2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DHCP_H +#define DHCP_H + +#include <netinet/ip.h> +#if __FAVOR_BSD +#include <netinet/udp.h> +#else +#define __FAVOR_BSD 1 +#include <netinet/udp.h> +#undef __FAVOR_BSD +#endif + +#define DHCP_SERVER_PORT 67 +#define DHCP_CLIENT_PORT 68 + +struct dhcp_data { + u8 op; + u8 htype; + u8 hlen; + u8 hops; + be32 xid; + be16 secs; + be16 flags; + be32 client_ip; + be32 your_ip; + be32 server_ip; + be32 relay_ip; + u8 hw_addr[16]; + u8 serv_name[64]; + u8 boot_file[128]; +} STRUCT_PACKED; + +struct bootp_pkt { + struct iphdr iph; + struct udphdr udph; + u8 op; + u8 htype; + u8 hlen; + u8 hops; + be32 xid; + be16 secs; + be16 flags; + be32 client_ip; + be32 your_ip; + be32 server_ip; + be32 relay_ip; + u8 hw_addr[16]; + u8 serv_name[64]; + u8 boot_file[128]; + u8 exten[312]; +} STRUCT_PACKED; + +#define DHCP_MAGIC 0x63825363 + +/* + * IANA DHCP/BOOTP registry + * http://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml +*/ +enum dhcp_options { + DHCP_OPT_PAD = 0, + DHCP_OPT_SUBNET_MASK = 1, + DHCP_OPT_TIME_OFFSET = 2, + DHCP_OPT_ROUTER = 3, + DHCP_OPT_TIME_SERVER = 4, + DHCP_OPT_NAME_SERVER = 5, + DHCP_OPT_DOMAIN_NAME_SERVER = 6, + DHCP_OPT_LOG_SERVER = 7, + DHCP_OPT_QUOTES_SERVER = 8, + DHCP_OPT_LPR_SERVER = 9, + DHCP_OPT_IMPRESS_SERVER = 10, + DHCP_OPT_RLP_SERVER = 11, + DHCP_OPT_HOSTNAME = 12, + DHCP_OPT_BOOT_FILE_SIZE = 13, + DHCP_OPT_MERIT_DUMP_FILE = 14, + DHCP_OPT_DOMAIN_NAME = 15, + DHCP_OPT_SWAP_SERVER = 16, + DHCP_OPT_ROOT_PATH = 17, + DHCP_OPT_EXTENSION_PATH = 18, + DHCP_OPT_FORWARD = 19, + DHCP_OPT_SRC_RTE = 20, + DHCP_OPT_POLICY_FILTER = 21, + DHCP_OPT_MAX_DG_ASSEMBLY = 22, + DHCP_OPT_DEFAULT_IP_TTL = 23, + DHCP_OPT_MTU_TIMEOUT = 24, + DHCP_OPT_MTU_PLATEAU = 25, + DHCP_OPT_MTU_INTERFACE = 26, + DHCP_OPT_ALL_SUBNETS_LOCAL = 27, + DHCP_OPT_BROADCAST_ADDRESS = 28, + DHCP_OPT_MASK_DISCOVERY = 29, + DHCP_OPT_MASK_SUPPLIER = 30, + DHCP_OPT_ROUTER_DISCOVERY = 31, + DHCP_OPT_ROUTER_SOLICITATION_ADDRESS = 32, + DHCP_OPT_STATIC_ROUTE = 33, + DHCP_OPT_TRAILERS = 34, + DHCP_OPT_ARP_TIMEOUT = 35, + DHCP_OPT_ETHERNET = 36, + DHCP_OPT_TCP_DEFAULT_TTL = 37, + DHCP_OPT_TCP_KEEPALIVE_INTERVAL = 38, + DHCP_OPT_TCP_KEEPALIVE_GARBAGE = 39, + DHCP_OPT_NIS_DOMAIN = 40, + DHCP_OPT_NIS_SERVERS = 41, + DHCP_OPT_NTP_SERVERS = 42, + DHCP_OPT_VENDOR_SPECIFIC = 43, + DHCP_OPT_NETBIOS_NAME_SERVER = 44, + DHCP_OPT_NETBIOS_DISTRIBUTION_SERVER = 45, + DHCP_OPT_NETBIOS_NODE_TYPE = 46, + DHCP_OPT_NETBIOS_SCOPE = 47, + DHCP_OPT_FONT_SERVER = 48, + DHCP_OPT_DISPLAY_MANAGER = 49, + DHCP_OPT_REQUESTED_IP_ADDRESS = 50, + DHCP_OPT_IP_ADDRESS_LEASE_TIME = 51, + DHCP_OPT_OVERLOAD = 52, + DHCP_OPT_MSG_TYPE = 53, + DHCP_OPT_SERVER_ID = 54, + DHCP_OPT_PARAMETER_REQ_LIST = 55, + DHCP_OPT_MESSAGE = 56, + DHCP_OPT_MAX_MESSAGE_SIZE = 57, + DHCP_OPT_RENEWAL_TIME = 58, + DHCP_OPT_REBINDING_TIME = 59, + DHCP_OPT_VENDOR_CLASS_ID = 60, + DHCP_OPT_CLIENT_ID = 61, + DHCP_OPT_NETWARE_IP_DOMAIN = 62, + DHCP_OPT_NETWARE_IP_OPTION = 63, + DHCP_OPT_NIS_V3_DOMAIN = 64, + DHCP_OPT_NIS_V3_SERVERS = 65, + DHCP_OPT_TFTP_SERVER_NAME = 66, + DHCP_OPT_BOOT_FILE_NAME = 67, + DHCP_OPT_HOME_AGENT_ADDRESSES = 68, + DHCP_OPT_SMTP_SERVER = 69, + DHCP_OPT_POP3_SERVER = 70, + DHCP_OPT_NNTP_SERVER = 71, + DHCP_OPT_WWW_SERVER = 72, + DHCP_OPT_FINGER_SERVER = 73, + DHCP_OPT_IRC_SERVER = 74, + DHCP_OPT_STREETTALK_SERVER = 75, + DHCP_OPT_STDA_SERVER = 76, + DHCP_OPT_USER_CLASS = 77, + DHCP_OPT_DIRECTORY_AGENT = 78, + DHCP_OPT_SERVICE_SCOPE = 79, + DHCP_OPT_RAPID_COMMIT = 80, + DHCP_OPT_CLIENT_FQDN = 81, + DHCP_OPT_RELAY_AGENT_INFO = 82, + DHCP_OPT_ISNS = 83, + DHCP_OPT_NDS_SERVERS = 85, + DHCP_OPT_NDS_TREE_NAME = 86, + DHCP_OPT_NDS_CONTEXT = 87, + DHCP_OPT_BCMCS_CONTROLLER_DOMAIN_NAME_LIST = 88, + DHCP_OPT_BCMCS_CONTROLLER_IPV4_ADDRESS = 89, + DHCP_OPT_AUTHENTICATION = 90, + DHCP_OPT_CLIENT_LAST_TRANSACTION_TIME = 91, + DHCP_OPT_ASSOCIATED_IP = 92, + DHCP_OPT_CLIENT_SYSYEM = 93, + DHCP_OPT_CLIENT_NDI = 94, + DHCP_OPT_LDAP = 95, + DHCP_OPT_UUID_GUID = 97, + DHCP_OPT_USER_AUTH = 98, + DHCP_OPT_GEOCONF_CIVIC = 99, + DHCP_OPT_PCODE = 100, + DHCP_OPT_TCODE = 101, + DHCP_OPT_NETINFO_ADDRESS = 112, + DHCP_OPT_NETINFO_TAG = 113, + DHCP_OPT_URL = 114, + DHCP_OPT_AUTO_CONFIG = 116, + DHCP_OPT_NAME_SERVICE_SEARCH = 117, + DHCP_OPT_SUBNET_SELECTION = 118, + DHCP_OPT_DOMAIN_SEARCH = 119, + DHCP_OPT_SIP_SERVERS_DCP = 120, + DHCP_OPT_CLASSLESS_STATIC_ROUTE = 121, + DHCP_OPT_CCC = 122, + DHCP_OPT_GEOCONF = 123, + DHCP_OPT_V_I_VENDOR_CLASS = 124, + DHCP_OPT_V_I_VENDOR_SPECIFIC_INFO = 125, + DHCP_OPT_PANA_AGENT = 136, + DHCP_OPT_V4_LOST = 137, + DHCP_OPT_CAPWAP_AC_V4 = 138, + DHCP_OPT_IPV4_ADDRESS_MOS = 139, + DHCP_OPT_IPV4_FQDN_MOS = 140, + DHCP_OPT_SIP_UA_CONF = 141, + DHCP_OPT_IPV4_ADDRESS_ANDSF = 142, + DHCP_OPT_GEOLOC = 144, + DHCP_OPT_FORCERENEW_NONCE_CAPABLE = 145, + DHCP_OPT_RDNSS_SELECTION = 146, + DHCP_OPT_TFTP_SERVER_ADDRESS = 150, + DHCP_OPT_STATUS_CODE = 151, + DHCP_OPT_BASE_TIME = 152, + DHCP_OPT_START_TIME_OF_STATE = 153, + DHCP_OPT_QUERY_START_TIME = 154, + DHCP_OPT_QUERY_END_TIME = 155, + DHCP_OPT_STATE = 156, + DHCP_OPT_DATA_SOURCE = 157, + DHCP_OPT_V4_PCP_SERVER = 158, + DHCP_OPT_V4_PORTPARAMS = 159, + DHCP_OPT_CAPTIVE_PORTAL = 160, + DHCP_OPT_CONF_FILE = 209, + DHCP_OPT_PATH_PREFIX = 210, + DHCP_OPT_REBOOT_TIME = 211, + DHCP_OPT_6RD = 212, + DHCP_OPT_V4_ACCESS_DOMAIN = 213, + DHCP_OPT_SUBNET_ALLOCATION = 220, + DHCP_OPT_VSS = 221, + DHCP_OPT_END = 255 +}; + +enum dhcp_message_types { + DHCPDISCOVER = 1, + DHCPOFFER = 2, + DHCPREQUEST = 3, + DHCPDECLINE = 4, + DHCPACK = 5, + DHCPNAK = 6, + DHCPRELEASE = 7, + DHCPINFORM = 8, + DHCPFORCERENEW = 9, + DHCPLEASEQUERY = 10, + DHCPLEASEUNASSIGNED = 11, + DHCPLEASEUNKNOWN = 12, + DHCPLEASEACTIVE = 13, + DHCPBULKLEASEQUERY = 14, + DHCPLEASEQUERYDONE = 15, + DHCPACTIVELEASEQUERY = 16, + DHCPLEASEQUERYSTATUS = 17, + DHCPTLS = 18, +}; + +enum dhcp_relay_agent_suboptions { + DHCP_RELAY_OPT_AGENT_CIRCUIT_ID = 1, + DHCP_RELAY_OPT_AGENT_REMOTE_ID = 2, + DHCP_RELAY_OPT_DOCSIS_DEVICE_CLASS = 4, + DHCP_RELAY_OPT_LINK_SELECTION = 5, + DHCP_RELAY_OPT_SUBSCRIBE_ID = 6, + DHCP_RELAY_OPT_RADIUS_ATTRIBUTES = 7, + DHCP_RELAY_OPT_AUTHENTICATION = 8, + DHCP_RELAY_OPT_VEDOR_SPECIFIC = 9, + DHCP_RELAY_OPT_RELAY_AGENT_FLAGS = 10, + DHCP_RELAY_OPT_SERVER_ID_OVERRIDE = 11, + DHCP_RELAY_OPT_RELAY_AGENT_ID = 12, + DHCP_RELAY_OPT_ACCESS_TECHNOLOGY_TYPE = 13, + DHCP_RELAY_OPT_ACCESS_NETWORK_NAME = 14, + DHCP_RELAY_OPT_ACCESS_POINT_NAME = 15, + DHCP_RELAY_OPT_ACCESS_POINT_BSSID = 16, + DHCP_RELAY_OPT_OPERATOR_ID = 17, + DHCP_RELAY_OPT_OPERATOR_REALM = 18, + DHCP_RELAY_OPT_DHCPV4_VIRTUAL_SUBNET_SELECTION = 151, + DHCP_RELAY_OPT_DHCPV4_VIRTUAL_SUBNET_SELECTION_CONTROL = 152, +}; + +enum access_technology_types { + ACCESS_TECHNOLOGY_VIRTUAL = 1, + ACCESS_TECHNOLOGY_PPP = 2, + ACCESS_TECHNOLOGY_ETHERNET = 3, + ACCESS_TECHNOLOGY_WLAN = 4, + ACCESS_TECHNOLOGY_WIMAX = 5, +}; + +#endif /* DHCP_H */ diff --git a/src/common/dpp.c b/src/common/dpp.c new file mode 100644 index 0000000000000..e715e0454d725 --- /dev/null +++ b/src/common/dpp.c @@ -0,0 +1,7691 @@ +/* + * DPP functionality shared between hostapd and wpa_supplicant + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#include <openssl/opensslv.h> +#include <openssl/err.h> +#include <openssl/asn1.h> +#include <openssl/asn1t.h> + +#include "utils/common.h" +#include "utils/base64.h" +#include "utils/json.h" +#include "common/ieee802_11_common.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" +#include "crypto/crypto.h" +#include "crypto/random.h" +#include "crypto/aes.h" +#include "crypto/aes_siv.h" +#include "crypto/sha384.h" +#include "crypto/sha512.h" +#include "drivers/driver.h" +#include "dpp.h" + + +#ifdef CONFIG_TESTING_OPTIONS +enum dpp_test_behavior dpp_test = DPP_TEST_DISABLED; +u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; +u8 dpp_pkex_peer_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; +u8 dpp_pkex_ephemeral_key_override[600]; +size_t dpp_pkex_ephemeral_key_override_len = 0; +u8 dpp_protocol_key_override[600]; +size_t dpp_protocol_key_override_len = 0; +u8 dpp_nonce_override[DPP_MAX_NONCE_LEN]; +size_t dpp_nonce_override_len = 0; + +static int dpp_test_gen_invalid_key(struct wpabuf *msg, + const struct dpp_curve_params *curve); +#endif /* CONFIG_TESTING_OPTIONS */ + +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) +/* Compatibility wrappers for older versions. */ + +static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) +{ + sig->r = r; + sig->s = s; + return 1; +} + + +static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, + const BIGNUM **ps) +{ + if (pr) + *pr = sig->r; + if (ps) + *ps = sig->s; +} + +#endif + + +static const struct dpp_curve_params dpp_curves[] = { + /* The mandatory to support and the default NIST P-256 curve needs to + * be the first entry on this list. */ + { "prime256v1", 32, 32, 16, 32, "P-256", 19, "ES256" }, + { "secp384r1", 48, 48, 24, 48, "P-384", 20, "ES384" }, + { "secp521r1", 64, 64, 32, 66, "P-521", 21, "ES512" }, + { "brainpoolP256r1", 32, 32, 16, 32, "BP-256", 28, "BS256" }, + { "brainpoolP384r1", 48, 48, 24, 48, "BP-384", 29, "BS384" }, + { "brainpoolP512r1", 64, 64, 32, 64, "BP-512", 30, "BS512" }, + { NULL, 0, 0, 0, 0, NULL, 0, NULL } +}; + + +/* Role-specific elements for PKEX */ + +/* NIST P-256 */ +static const u8 pkex_init_x_p256[32] = { + 0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b, + 0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54, + 0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07, + 0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25 + }; +static const u8 pkex_init_y_p256[32] = { + 0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b, + 0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc, + 0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45, + 0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4 + }; +static const u8 pkex_resp_x_p256[32] = { + 0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39, + 0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f, + 0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f, + 0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76 +}; +static const u8 pkex_resp_y_p256[32] = { + 0xd9, 0xfb, 0xf6, 0xb9, 0xf5, 0xfa, 0xdf, 0x19, + 0x58, 0xd8, 0x3e, 0xc9, 0x89, 0x7a, 0x35, 0xc1, + 0xbd, 0xe9, 0x0b, 0x77, 0x7a, 0xcb, 0x91, 0x2a, + 0xe8, 0x21, 0x3f, 0x47, 0x52, 0x02, 0x4d, 0x67 +}; + +/* NIST P-384 */ +static const u8 pkex_init_x_p384[48] = { + 0x95, 0x3f, 0x42, 0x9e, 0x50, 0x7f, 0xf9, 0xaa, + 0xac, 0x1a, 0xf2, 0x85, 0x2e, 0x64, 0x91, 0x68, + 0x64, 0xc4, 0x3c, 0xb7, 0x5c, 0xf8, 0xc9, 0x53, + 0x6e, 0x58, 0x4c, 0x7f, 0xc4, 0x64, 0x61, 0xac, + 0x51, 0x8a, 0x6f, 0xfe, 0xab, 0x74, 0xe6, 0x12, + 0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3 +}; +static const u8 pkex_init_y_p384[48] = { + 0x76, 0x2f, 0x68, 0x84, 0xa6, 0xb0, 0x59, 0x29, + 0x83, 0xa2, 0x6c, 0xa4, 0x6c, 0x3b, 0xf8, 0x56, + 0x76, 0x11, 0x2a, 0x32, 0x90, 0xbd, 0x07, 0xc7, + 0x37, 0x39, 0x9d, 0xdb, 0x96, 0xf3, 0x2b, 0xb6, + 0x27, 0xbb, 0x29, 0x3c, 0x17, 0x33, 0x9d, 0x94, + 0xc3, 0xda, 0xac, 0x46, 0xb0, 0x8e, 0x07, 0x18 +}; +static const u8 pkex_resp_x_p384[48] = { + 0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98, + 0x5f, 0xb4, 0xd6, 0x4b, 0x50, 0xd0, 0x84, 0x97, + 0x4b, 0x7e, 0x57, 0x70, 0xd2, 0xd9, 0xf4, 0x92, + 0x2a, 0x3f, 0xce, 0x99, 0xc5, 0x77, 0x33, 0x44, + 0x14, 0x56, 0x92, 0xcb, 0xae, 0x46, 0x64, 0xdf, + 0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf +}; +static const u8 pkex_resp_y_p384[48] = { + 0xab, 0xa7, 0xdf, 0x52, 0xaa, 0xe2, 0x35, 0x0c, + 0xe3, 0x75, 0x32, 0xe6, 0xbf, 0x06, 0xc8, 0x7c, + 0x38, 0x29, 0x4c, 0xec, 0x82, 0xac, 0xd7, 0xa3, + 0x09, 0xd2, 0x0e, 0x22, 0x5a, 0x74, 0x52, 0xa1, + 0x7e, 0x54, 0x4e, 0xfe, 0xc6, 0x29, 0x33, 0x63, + 0x15, 0xe1, 0x7b, 0xe3, 0x40, 0x1c, 0xca, 0x06 +}; + +/* NIST P-521 */ +static const u8 pkex_init_x_p521[66] = { + 0x00, 0x16, 0x20, 0x45, 0x19, 0x50, 0x95, 0x23, + 0x0d, 0x24, 0xbe, 0x00, 0x87, 0xdc, 0xfa, 0xf0, + 0x58, 0x9a, 0x01, 0x60, 0x07, 0x7a, 0xca, 0x76, + 0x01, 0xab, 0x2d, 0x5a, 0x46, 0xcd, 0x2c, 0xb5, + 0x11, 0x9a, 0xff, 0xaa, 0x48, 0x04, 0x91, 0x38, + 0xcf, 0x86, 0xfc, 0xa4, 0xa5, 0x0f, 0x47, 0x01, + 0x80, 0x1b, 0x30, 0xa3, 0xae, 0xe8, 0x1c, 0x2e, + 0xea, 0xcc, 0xf0, 0x03, 0x9f, 0x77, 0x4c, 0x8d, + 0x97, 0x76 +}; +static const u8 pkex_init_y_p521[66] = { + 0x00, 0xb3, 0x8e, 0x02, 0xe4, 0x2a, 0x63, 0x59, + 0x12, 0xc6, 0x10, 0xba, 0x3a, 0xf9, 0x02, 0x99, + 0x3f, 0x14, 0xf0, 0x40, 0xde, 0x5c, 0xc9, 0x8b, + 0x02, 0x55, 0xfa, 0x91, 0xb1, 0xcc, 0x6a, 0xbd, + 0xe5, 0x62, 0xc0, 0xc5, 0xe3, 0xa1, 0x57, 0x9f, + 0x08, 0x1a, 0xa6, 0xe2, 0xf8, 0x55, 0x90, 0xbf, + 0xf5, 0xa6, 0xc3, 0xd8, 0x52, 0x1f, 0xb7, 0x02, + 0x2e, 0x7c, 0xc8, 0xb3, 0x20, 0x1e, 0x79, 0x8d, + 0x03, 0xa8 +}; +static const u8 pkex_resp_x_p521[66] = { + 0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a, + 0x18, 0x2c, 0xb3, 0x05, 0x77, 0x0f, 0xc3, 0x44, + 0x1a, 0xcd, 0x78, 0x46, 0x14, 0xee, 0x46, 0x3f, + 0xab, 0xc9, 0x59, 0x7c, 0x85, 0xa0, 0xc2, 0xfb, + 0x02, 0x32, 0x99, 0xde, 0x5d, 0xe1, 0x0d, 0x48, + 0x2d, 0x71, 0x7d, 0x8d, 0x3f, 0x61, 0x67, 0x9e, + 0x2b, 0x8b, 0x12, 0xde, 0x10, 0x21, 0x55, 0x0a, + 0x5b, 0x2d, 0xe8, 0x05, 0x09, 0xf6, 0x20, 0x97, + 0x84, 0xb4 +}; +static const u8 pkex_resp_y_p521[66] = { + 0x00, 0x46, 0x63, 0x39, 0xbe, 0xcd, 0xa4, 0x2d, + 0xca, 0x27, 0x74, 0xd4, 0x1b, 0x91, 0x33, 0x20, + 0x83, 0xc7, 0x3b, 0xa4, 0x09, 0x8b, 0x8e, 0xa3, + 0x88, 0xe9, 0x75, 0x7f, 0x56, 0x7b, 0x38, 0x84, + 0x62, 0x02, 0x7c, 0x90, 0x51, 0x07, 0xdb, 0xe9, + 0xd0, 0xde, 0xda, 0x9a, 0x5d, 0xe5, 0x94, 0xd2, + 0xcf, 0x9d, 0x4c, 0x33, 0x91, 0xa6, 0xc3, 0x80, + 0xa7, 0x6e, 0x7e, 0x8d, 0xf8, 0x73, 0x6e, 0x53, + 0xce, 0xe1 +}; + +/* Brainpool P-256r1 */ +static const u8 pkex_init_x_bp_p256r1[32] = { + 0x46, 0x98, 0x18, 0x6c, 0x27, 0xcd, 0x4b, 0x10, + 0x7d, 0x55, 0xa3, 0xdd, 0x89, 0x1f, 0x9f, 0xca, + 0xc7, 0x42, 0x5b, 0x8a, 0x23, 0xed, 0xf8, 0x75, + 0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8 +}; +static const u8 pkex_init_y_bp_p256r1[32] = { + 0x93, 0xca, 0xef, 0xa9, 0x66, 0x3e, 0x87, 0xcd, + 0x52, 0x6e, 0x54, 0x13, 0xef, 0x31, 0x67, 0x30, + 0x15, 0x13, 0x9d, 0x6d, 0xc0, 0x95, 0x32, 0xbe, + 0x4f, 0xab, 0x5d, 0xf7, 0xbf, 0x5e, 0xaa, 0x0b +}; +static const u8 pkex_resp_x_bp_p256r1[32] = { + 0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f, + 0x4a, 0x3f, 0x4f, 0x18, 0x0a, 0x22, 0x56, 0x6a, + 0xa9, 0xef, 0xd4, 0xe6, 0xc3, 0x53, 0xc2, 0x1a, + 0x23, 0x54, 0xdd, 0x08, 0x7e, 0x10, 0xd8, 0xe3 +}; +static const u8 pkex_resp_y_bp_p256r1[32] = { + 0x2a, 0xfa, 0x98, 0x9b, 0xe3, 0xda, 0x30, 0xfd, + 0x32, 0x28, 0xcb, 0x66, 0xfb, 0x40, 0x7f, 0xf2, + 0xb2, 0x25, 0x80, 0x82, 0x44, 0x85, 0x13, 0x7e, + 0x4b, 0xb5, 0x06, 0xc0, 0x03, 0x69, 0x23, 0x64 +}; + +/* Brainpool P-384r1 */ +static const u8 pkex_init_x_bp_p384r1[48] = { + 0x0a, 0x2c, 0xeb, 0x49, 0x5e, 0xb7, 0x23, 0xbd, + 0x20, 0x5b, 0xe0, 0x49, 0xdf, 0xcf, 0xcf, 0x19, + 0x37, 0x36, 0xe1, 0x2f, 0x59, 0xdb, 0x07, 0x06, + 0xb5, 0xeb, 0x2d, 0xae, 0xc2, 0xb2, 0x38, 0x62, + 0xa6, 0x73, 0x09, 0xa0, 0x6c, 0x0a, 0xa2, 0x30, + 0x99, 0xeb, 0xf7, 0x1e, 0x47, 0xb9, 0x5e, 0xbe +}; +static const u8 pkex_init_y_bp_p384r1[48] = { + 0x54, 0x76, 0x61, 0x65, 0x75, 0x5a, 0x2f, 0x99, + 0x39, 0x73, 0xca, 0x6c, 0xf9, 0xf7, 0x12, 0x86, + 0x54, 0xd5, 0xd4, 0xad, 0x45, 0x7b, 0xbf, 0x32, + 0xee, 0x62, 0x8b, 0x9f, 0x52, 0xe8, 0xa0, 0xc9, + 0xb7, 0x9d, 0xd1, 0x09, 0xb4, 0x79, 0x1c, 0x3e, + 0x1a, 0xbf, 0x21, 0x45, 0x66, 0x6b, 0x02, 0x52 +}; +static const u8 pkex_resp_x_bp_p384r1[48] = { + 0x03, 0xa2, 0x57, 0xef, 0xe8, 0x51, 0x21, 0xa0, + 0xc8, 0x9e, 0x21, 0x02, 0xb5, 0x9a, 0x36, 0x25, + 0x74, 0x22, 0xd1, 0xf2, 0x1b, 0xa8, 0x9a, 0x9b, + 0x97, 0xbc, 0x5a, 0xeb, 0x26, 0x15, 0x09, 0x71, + 0x77, 0x59, 0xec, 0x8b, 0xb7, 0xe1, 0xe8, 0xce, + 0x65, 0xb8, 0xaf, 0xf8, 0x80, 0xae, 0x74, 0x6c +}; +static const u8 pkex_resp_y_bp_p384r1[48] = { + 0x2f, 0xd9, 0x6a, 0xc7, 0x3e, 0xec, 0x76, 0x65, + 0x2d, 0x38, 0x7f, 0xec, 0x63, 0x26, 0x3f, 0x04, + 0xd8, 0x4e, 0xff, 0xe1, 0x0a, 0x51, 0x74, 0x70, + 0xe5, 0x46, 0x63, 0x7f, 0x5c, 0xc0, 0xd1, 0x7c, + 0xfb, 0x2f, 0xea, 0xe2, 0xd8, 0x0f, 0x84, 0xcb, + 0xe9, 0x39, 0x5c, 0x64, 0xfe, 0xcb, 0x2f, 0xf1 +}; + +/* Brainpool P-512r1 */ +static const u8 pkex_init_x_bp_p512r1[64] = { + 0x4c, 0xe9, 0xb6, 0x1c, 0xe2, 0x00, 0x3c, 0x9c, + 0xa9, 0xc8, 0x56, 0x52, 0xaf, 0x87, 0x3e, 0x51, + 0x9c, 0xbb, 0x15, 0x31, 0x1e, 0xc1, 0x05, 0xfc, + 0x7c, 0x77, 0xd7, 0x37, 0x61, 0x27, 0xd0, 0x95, + 0x98, 0xee, 0x5d, 0xa4, 0x3d, 0x09, 0xdb, 0x3d, + 0xfa, 0x89, 0x9e, 0x7f, 0xa6, 0xa6, 0x9c, 0xff, + 0x83, 0x5c, 0x21, 0x6c, 0x3e, 0xf2, 0xfe, 0xdc, + 0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f +}; +static const u8 pkex_init_y_bp_p512r1[64] = { + 0x50, 0xb5, 0x9b, 0xfa, 0x45, 0x67, 0x75, 0x94, + 0x44, 0xe7, 0x68, 0xb0, 0xeb, 0x3e, 0xb3, 0xb8, + 0xf9, 0x99, 0x05, 0xef, 0xae, 0x6c, 0xbc, 0xe3, + 0xe1, 0xd2, 0x51, 0x54, 0xdf, 0x59, 0xd4, 0x45, + 0x41, 0x3a, 0xa8, 0x0b, 0x76, 0x32, 0x44, 0x0e, + 0x07, 0x60, 0x3a, 0x6e, 0xbe, 0xfe, 0xe0, 0x58, + 0x52, 0xa0, 0xaa, 0x8b, 0xd8, 0x5b, 0xf2, 0x71, + 0x11, 0x9a, 0x9e, 0x8f, 0x1a, 0xd1, 0xc9, 0x99 +}; +static const u8 pkex_resp_x_bp_p512r1[64] = { + 0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72, + 0x1c, 0x48, 0xbe, 0xc5, 0x77, 0x14, 0x30, 0x76, + 0xe4, 0xbf, 0xf7, 0x7b, 0xc5, 0xfd, 0xdf, 0x19, + 0x1e, 0x0f, 0xdf, 0x1c, 0x40, 0xfa, 0x34, 0x9e, + 0x1f, 0x42, 0x24, 0xa3, 0x2c, 0xd5, 0xc7, 0xc9, + 0x7b, 0x47, 0x78, 0x96, 0xf1, 0x37, 0x0e, 0x88, + 0xcb, 0xa6, 0x52, 0x29, 0xd7, 0xa8, 0x38, 0x29, + 0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e +}; +static const u8 pkex_resp_y_bp_p512r1[64] = { + 0x80, 0x1f, 0x43, 0xd2, 0x17, 0x35, 0xec, 0x81, + 0xd9, 0x4b, 0xdc, 0x81, 0x19, 0xd9, 0x5f, 0x68, + 0x16, 0x84, 0xfe, 0x63, 0x4b, 0x8d, 0x5d, 0xaa, + 0x88, 0x4a, 0x47, 0x48, 0xd4, 0xea, 0xab, 0x7d, + 0x6a, 0xbf, 0xe1, 0x28, 0x99, 0x6a, 0x87, 0x1c, + 0x30, 0xb4, 0x44, 0x2d, 0x75, 0xac, 0x35, 0x09, + 0x73, 0x24, 0x3d, 0xb4, 0x43, 0xb1, 0xc1, 0x56, + 0x56, 0xad, 0x30, 0x87, 0xf4, 0xc3, 0x00, 0xc7 +}; + + +static void dpp_debug_print_point(const char *title, const EC_GROUP *group, + const EC_POINT *point) +{ + BIGNUM *x, *y; + BN_CTX *ctx; + char *x_str = NULL, *y_str = NULL; + + if (!wpa_debug_show_keys) + return; + + ctx = BN_CTX_new(); + x = BN_new(); + y = BN_new(); + if (!ctx || !x || !y || + EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx) != 1) + goto fail; + + x_str = BN_bn2hex(x); + y_str = BN_bn2hex(y); + if (!x_str || !y_str) + goto fail; + + wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str); + +fail: + OPENSSL_free(x_str); + OPENSSL_free(y_str); + BN_free(x); + BN_free(y); + BN_CTX_free(ctx); +} + + +static int dpp_hash_vector(const struct dpp_curve_params *curve, + size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + if (curve->hash_len == 32) + return sha256_vector(num_elem, addr, len, mac); + if (curve->hash_len == 48) + return sha384_vector(num_elem, addr, len, mac); + if (curve->hash_len == 64) + return sha512_vector(num_elem, addr, len, mac); + return -1; +} + + +static int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len, + const char *label, u8 *out, size_t outlen) +{ + if (hash_len == 32) + return hmac_sha256_kdf(secret, secret_len, NULL, + (const u8 *) label, os_strlen(label), + out, outlen); + if (hash_len == 48) + return hmac_sha384_kdf(secret, secret_len, NULL, + (const u8 *) label, os_strlen(label), + out, outlen); + if (hash_len == 64) + return hmac_sha512_kdf(secret, secret_len, NULL, + (const u8 *) label, os_strlen(label), + out, outlen); + return -1; +} + + +static int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len, + size_t num_elem, const u8 *addr[], + const size_t *len, u8 *mac) +{ + if (hash_len == 32) + return hmac_sha256_vector(key, key_len, num_elem, addr, len, + mac); + if (hash_len == 48) + return hmac_sha384_vector(key, key_len, num_elem, addr, len, + mac); + if (hash_len == 64) + return hmac_sha512_vector(key, key_len, num_elem, addr, len, + mac); + return -1; +} + + +static int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len, + const u8 *data, size_t data_len, u8 *mac) +{ + if (hash_len == 32) + return hmac_sha256(key, key_len, data, data_len, mac); + if (hash_len == 48) + return hmac_sha384(key, key_len, data, data_len, mac); + if (hash_len == 64) + return hmac_sha512(key, key_len, data, data_len, mac); + return -1; +} + + +static int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len) +{ + int num_bytes, offset; + + num_bytes = BN_num_bytes(bn); + if ((size_t) num_bytes > len) + return -1; + offset = len - num_bytes; + os_memset(pos, 0, offset); + BN_bn2bin(bn, pos + offset); + return 0; +} + + +static struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix) +{ + int len, res; + EC_KEY *eckey; + struct wpabuf *buf; + unsigned char *pos; + + eckey = EVP_PKEY_get1_EC_KEY(pkey); + if (!eckey) + return NULL; + EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED); + len = i2o_ECPublicKey(eckey, NULL); + if (len <= 0) { + wpa_printf(MSG_ERROR, + "DDP: Failed to determine public key encoding length"); + EC_KEY_free(eckey); + return NULL; + } + + buf = wpabuf_alloc(len); + if (!buf) { + EC_KEY_free(eckey); + return NULL; + } + + pos = wpabuf_put(buf, len); + res = i2o_ECPublicKey(eckey, &pos); + EC_KEY_free(eckey); + if (res != len) { + wpa_printf(MSG_ERROR, + "DDP: Failed to encode public key (res=%d/%d)", + res, len); + wpabuf_free(buf); + return NULL; + } + + if (!prefix) { + /* Remove 0x04 prefix to match DPP definition */ + pos = wpabuf_mhead(buf); + os_memmove(pos, pos + 1, len - 1); + buf->used--; + } + + return buf; +} + + +static EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group, + const u8 *buf_x, const u8 *buf_y, + size_t len) +{ + EC_KEY *eckey = NULL; + BN_CTX *ctx; + EC_POINT *point = NULL; + BIGNUM *x = NULL, *y = NULL; + EVP_PKEY *pkey = NULL; + + ctx = BN_CTX_new(); + if (!ctx) { + wpa_printf(MSG_ERROR, "DPP: Out of memory"); + return NULL; + } + + point = EC_POINT_new(group); + x = BN_bin2bn(buf_x, len, NULL); + y = BN_bin2bn(buf_y, len, NULL); + if (!point || !x || !y) { + wpa_printf(MSG_ERROR, "DPP: Out of memory"); + goto fail; + } + + if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) { + wpa_printf(MSG_ERROR, + "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (!EC_POINT_is_on_curve(group, point, ctx) || + EC_POINT_is_at_infinity(group, point)) { + wpa_printf(MSG_ERROR, "DPP: Invalid point"); + goto fail; + } + dpp_debug_print_point("DPP: dpp_set_pubkey_point_group", group, point); + + eckey = EC_KEY_new(); + if (!eckey || + EC_KEY_set_group(eckey, group) != 1 || + EC_KEY_set_public_key(eckey, point) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to set EC_KEY: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); + + pkey = EVP_PKEY_new(); + if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) { + wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY"); + goto fail; + } + +out: + BN_free(x); + BN_free(y); + EC_KEY_free(eckey); + EC_POINT_free(point); + BN_CTX_free(ctx); + return pkey; +fail: + EVP_PKEY_free(pkey); + pkey = NULL; + goto out; +} + + +static EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key, + const u8 *buf, size_t len) +{ + EC_KEY *eckey; + const EC_GROUP *group; + EVP_PKEY *pkey = NULL; + + if (len & 1) + return NULL; + + eckey = EVP_PKEY_get1_EC_KEY(group_key); + if (!eckey) { + wpa_printf(MSG_ERROR, + "DPP: Could not get EC_KEY from group_key"); + return NULL; + } + + group = EC_KEY_get0_group(eckey); + if (group) + pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2, + len / 2); + else + wpa_printf(MSG_ERROR, "DPP: Could not get EC group"); + + EC_KEY_free(eckey); + return pkey; +} + + +static void dpp_auth_fail(struct dpp_authentication *auth, const char *txt) +{ + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt); +} + + +struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type, + size_t len) +{ + struct wpabuf *msg; + + msg = wpabuf_alloc(8 + len); + if (!msg) + return NULL; + wpabuf_put_u8(msg, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC); + wpabuf_put_be24(msg, OUI_WFA); + wpabuf_put_u8(msg, DPP_OUI_TYPE); + wpabuf_put_u8(msg, 1); /* Crypto Suite */ + wpabuf_put_u8(msg, type); + return msg; +} + + +const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len) +{ + u16 id, alen; + const u8 *pos = buf, *end = buf + len; + + while (end - pos >= 4) { + id = WPA_GET_LE16(pos); + pos += 2; + alen = WPA_GET_LE16(pos); + pos += 2; + if (alen > end - pos) + return NULL; + if (id == req_id) { + *ret_len = alen; + return pos; + } + pos += alen; + } + + return NULL; +} + + +int dpp_check_attrs(const u8 *buf, size_t len) +{ + const u8 *pos, *end; + int wrapped_data = 0; + + pos = buf; + end = buf + len; + while (end - pos >= 4) { + u16 id, alen; + + id = WPA_GET_LE16(pos); + pos += 2; + alen = WPA_GET_LE16(pos); + pos += 2; + wpa_printf(MSG_MSGDUMP, "DPP: Attribute ID %04x len %u", + id, alen); + if (alen > end - pos) { + wpa_printf(MSG_DEBUG, + "DPP: Truncated message - not enough room for the attribute - dropped"); + return -1; + } + if (wrapped_data) { + wpa_printf(MSG_DEBUG, + "DPP: An unexpected attribute included after the Wrapped Data attribute"); + return -1; + } + if (id == DPP_ATTR_WRAPPED_DATA) + wrapped_data = 1; + pos += alen; + } + + if (end != pos) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected octets (%d) after the last attribute", + (int) (end - pos)); + return -1; + } + + return 0; +} + + +void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info) +{ + if (!info) + return; + os_free(info->uri); + os_free(info->info); + EVP_PKEY_free(info->pubkey); + os_free(info); +} + + +const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type) +{ + switch (type) { + case DPP_BOOTSTRAP_QR_CODE: + return "QRCODE"; + case DPP_BOOTSTRAP_PKEX: + return "PKEX"; + } + return "??"; +} + + +static int dpp_uri_valid_info(const char *info) +{ + while (*info) { + unsigned char val = *info++; + + if (val < 0x20 || val > 0x7e || val == 0x3b) + return 0; + } + + return 1; +} + + +static int dpp_clone_uri(struct dpp_bootstrap_info *bi, const char *uri) +{ + bi->uri = os_strdup(uri); + return bi->uri ? 0 : -1; +} + + +int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi, + const char *chan_list) +{ + const char *pos = chan_list; + int opclass, channel, freq; + + while (pos && *pos && *pos != ';') { + opclass = atoi(pos); + if (opclass <= 0) + goto fail; + pos = os_strchr(pos, '/'); + if (!pos) + goto fail; + pos++; + channel = atoi(pos); + if (channel <= 0) + goto fail; + while (*pos >= '0' && *pos <= '9') + pos++; + freq = ieee80211_chan_to_freq(NULL, opclass, channel); + wpa_printf(MSG_DEBUG, + "DPP: URI channel-list: opclass=%d channel=%d ==> freq=%d", + opclass, channel, freq); + if (freq < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Ignore unknown URI channel-list channel (opclass=%d channel=%d)", + opclass, channel); + } else if (bi->num_freq == DPP_BOOTSTRAP_MAX_FREQ) { + wpa_printf(MSG_DEBUG, + "DPP: Too many channels in URI channel-list - ignore list"); + bi->num_freq = 0; + break; + } else { + bi->freq[bi->num_freq++] = freq; + } + + if (*pos == ';' || *pos == '\0') + break; + if (*pos != ',') + goto fail; + pos++; + } + + return 0; +fail: + wpa_printf(MSG_DEBUG, "DPP: Invalid URI channel-list"); + return -1; +} + + +int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac) +{ + if (!mac) + return 0; + + if (hwaddr_aton2(mac, bi->mac_addr) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Invalid URI mac"); + return -1; + } + + wpa_printf(MSG_DEBUG, "DPP: URI mac: " MACSTR, MAC2STR(bi->mac_addr)); + + return 0; +} + + +int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info) +{ + const char *end; + + if (!info) + return 0; + + end = os_strchr(info, ';'); + if (!end) + end = info + os_strlen(info); + bi->info = os_malloc(end - info + 1); + if (!bi->info) + return -1; + os_memcpy(bi->info, info, end - info); + bi->info[end - info] = '\0'; + wpa_printf(MSG_DEBUG, "DPP: URI(information): %s", bi->info); + if (!dpp_uri_valid_info(bi->info)) { + wpa_printf(MSG_DEBUG, "DPP: Invalid URI information payload"); + return -1; + } + + return 0; +} + + +static const struct dpp_curve_params * +dpp_get_curve_oid(const ASN1_OBJECT *poid) +{ + ASN1_OBJECT *oid; + int i; + + for (i = 0; dpp_curves[i].name; i++) { + oid = OBJ_txt2obj(dpp_curves[i].name, 0); + if (oid && OBJ_cmp(poid, oid) == 0) + return &dpp_curves[i]; + } + return NULL; +} + + +static const struct dpp_curve_params * dpp_get_curve_nid(int nid) +{ + int i, tmp; + + if (!nid) + return NULL; + for (i = 0; dpp_curves[i].name; i++) { + tmp = OBJ_txt2nid(dpp_curves[i].name); + if (tmp == nid) + return &dpp_curves[i]; + } + return NULL; +} + + +static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info) +{ + const char *end; + u8 *data; + size_t data_len; + EVP_PKEY *pkey; + const unsigned char *p; + int res; + X509_PUBKEY *pub = NULL; + ASN1_OBJECT *ppkalg; + const unsigned char *pk; + int ppklen; + X509_ALGOR *pa; +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + ASN1_OBJECT *pa_oid; +#else + const ASN1_OBJECT *pa_oid; +#endif + const void *pval; + int ptype; + const ASN1_OBJECT *poid; + char buf[100]; + + end = os_strchr(info, ';'); + if (!end) + return -1; + + data = base64_decode((const unsigned char *) info, end - info, + &data_len); + if (!data) { + wpa_printf(MSG_DEBUG, + "DPP: Invalid base64 encoding on URI public-key"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "DPP: Base64 decoded URI public-key", + data, data_len); + + if (sha256_vector(1, (const u8 **) &data, &data_len, + bi->pubkey_hash) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key"); + os_free(data); + return -1; + } + wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", + bi->pubkey_hash, SHA256_MAC_LEN); + + /* DER encoded ASN.1 SubjectPublicKeyInfo + * + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + * + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL } + * + * subjectPublicKey = compressed format public key per ANSI X9.63 + * algorithm = ecPublicKey (1.2.840.10045.2.1) + * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g., + * prime256v1 (1.2.840.10045.3.1.7) + */ + + p = data; + pkey = d2i_PUBKEY(NULL, &p, data_len); + os_free(data); + + if (!pkey) { + wpa_printf(MSG_DEBUG, + "DPP: Could not parse URI public-key SubjectPublicKeyInfo"); + return -1; + } + + if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) { + wpa_printf(MSG_DEBUG, + "DPP: SubjectPublicKeyInfo does not describe an EC key"); + EVP_PKEY_free(pkey); + return -1; + } + + res = X509_PUBKEY_set(&pub, pkey); + if (res != 1) { + wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey"); + goto fail; + } + + res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub); + if (res != 1) { + wpa_printf(MSG_DEBUG, + "DPP: Could not extract SubjectPublicKeyInfo parameters"); + goto fail; + } + res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0); + if (res < 0 || (size_t) res >= sizeof(buf)) { + wpa_printf(MSG_DEBUG, + "DPP: Could not extract SubjectPublicKeyInfo algorithm"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf); + if (os_strcmp(buf, "id-ecPublicKey") != 0) { + wpa_printf(MSG_DEBUG, + "DPP: Unsupported SubjectPublicKeyInfo algorithm"); + goto fail; + } + + X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa); + if (ptype != V_ASN1_OBJECT) { + wpa_printf(MSG_DEBUG, + "DPP: SubjectPublicKeyInfo parameters did not contain an OID"); + goto fail; + } + poid = pval; + res = OBJ_obj2txt(buf, sizeof(buf), poid, 0); + if (res < 0 || (size_t) res >= sizeof(buf)) { + wpa_printf(MSG_DEBUG, + "DPP: Could not extract SubjectPublicKeyInfo parameters OID"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf); + bi->curve = dpp_get_curve_oid(poid); + if (!bi->curve) { + wpa_printf(MSG_DEBUG, + "DPP: Unsupported SubjectPublicKeyInfo curve: %s", + buf); + goto fail; + } + + wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen); + + X509_PUBKEY_free(pub); + bi->pubkey = pkey; + return 0; +fail: + X509_PUBKEY_free(pub); + EVP_PKEY_free(pkey); + return -1; +} + + +static struct dpp_bootstrap_info * dpp_parse_uri(const char *uri) +{ + const char *pos = uri; + const char *end; + const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL; + struct dpp_bootstrap_info *bi; + + wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri)); + + if (os_strncmp(pos, "DPP:", 4) != 0) { + wpa_printf(MSG_INFO, "DPP: Not a DPP URI"); + return NULL; + } + pos += 4; + + for (;;) { + end = os_strchr(pos, ';'); + if (!end) + break; + + if (end == pos) { + /* Handle terminating ";;" and ignore unexpected ";" + * for parsing robustness. */ + pos++; + continue; + } + + if (pos[0] == 'C' && pos[1] == ':' && !chan_list) + chan_list = pos + 2; + else if (pos[0] == 'M' && pos[1] == ':' && !mac) + mac = pos + 2; + else if (pos[0] == 'I' && pos[1] == ':' && !info) + info = pos + 2; + else if (pos[0] == 'K' && pos[1] == ':' && !pk) + pk = pos + 2; + else + wpa_hexdump_ascii(MSG_DEBUG, + "DPP: Ignore unrecognized URI parameter", + pos, end - pos); + pos = end + 1; + } + + if (!pk) { + wpa_printf(MSG_INFO, "DPP: URI missing public-key"); + return NULL; + } + + bi = os_zalloc(sizeof(*bi)); + if (!bi) + return NULL; + + if (dpp_clone_uri(bi, uri) < 0 || + dpp_parse_uri_chan_list(bi, chan_list) < 0 || + dpp_parse_uri_mac(bi, mac) < 0 || + dpp_parse_uri_info(bi, info) < 0 || + dpp_parse_uri_pk(bi, pk) < 0) { + dpp_bootstrap_info_free(bi); + bi = NULL; + } + + return bi; +} + + +struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri) +{ + struct dpp_bootstrap_info *bi; + + bi = dpp_parse_uri(uri); + if (bi) + bi->type = DPP_BOOTSTRAP_QR_CODE; + return bi; +} + + +static void dpp_debug_print_key(const char *title, EVP_PKEY *key) +{ + EC_KEY *eckey; + BIO *out; + size_t rlen; + char *txt; + int res; + unsigned char *der = NULL; + int der_len; + const EC_GROUP *group; + const EC_POINT *point; + + out = BIO_new(BIO_s_mem()); + if (!out) + return; + + EVP_PKEY_print_private(out, key, 0, NULL); + rlen = BIO_ctrl_pending(out); + txt = os_malloc(rlen + 1); + if (txt) { + res = BIO_read(out, txt, rlen); + if (res > 0) { + txt[res] = '\0'; + wpa_printf(MSG_DEBUG, "%s: %s", title, txt); + } + os_free(txt); + } + BIO_free(out); + + eckey = EVP_PKEY_get1_EC_KEY(key); + if (!eckey) + return; + + group = EC_KEY_get0_group(eckey); + point = EC_KEY_get0_public_key(eckey); + if (group && point) + dpp_debug_print_point(title, group, point); + + der_len = i2d_ECPrivateKey(eckey, &der); + if (der_len > 0) + wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len); + OPENSSL_free(der); + if (der_len <= 0) { + der = NULL; + der_len = i2d_EC_PUBKEY(eckey, &der); + if (der_len > 0) + wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len); + OPENSSL_free(der); + } + + EC_KEY_free(eckey); +} + + +static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve) +{ + EVP_PKEY_CTX *kctx = NULL; + EC_KEY *ec_params; + EVP_PKEY *params = NULL, *key = NULL; + int nid; + + wpa_printf(MSG_DEBUG, "DPP: Generating a keypair"); + + nid = OBJ_txt2nid(curve->name); + if (nid == NID_undef) { + wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name); + return NULL; + } + + ec_params = EC_KEY_new_by_curve_name(nid); + if (!ec_params) { + wpa_printf(MSG_ERROR, + "DPP: Failed to generate EC_KEY parameters"); + goto fail; + } + EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE); + params = EVP_PKEY_new(); + if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to generate EVP_PKEY parameters"); + goto fail; + } + + kctx = EVP_PKEY_CTX_new(params, NULL); + if (!kctx || + EVP_PKEY_keygen_init(kctx) != 1 || + EVP_PKEY_keygen(kctx, &key) != 1) { + wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key"); + goto fail; + } + + if (wpa_debug_show_keys) + dpp_debug_print_key("Own generated key", key); + + EVP_PKEY_free(params); + EVP_PKEY_CTX_free(kctx); + return key; +fail: + EVP_PKEY_CTX_free(kctx); + EVP_PKEY_free(params); + return NULL; +} + + +static const struct dpp_curve_params * +dpp_get_curve_name(const char *name) +{ + int i; + + for (i = 0; dpp_curves[i].name; i++) { + if (os_strcmp(name, dpp_curves[i].name) == 0 || + (dpp_curves[i].jwk_crv && + os_strcmp(name, dpp_curves[i].jwk_crv) == 0)) + return &dpp_curves[i]; + } + return NULL; +} + + +static const struct dpp_curve_params * +dpp_get_curve_jwk_crv(const char *name) +{ + int i; + + for (i = 0; dpp_curves[i].name; i++) { + if (dpp_curves[i].jwk_crv && + os_strcmp(name, dpp_curves[i].jwk_crv) == 0) + return &dpp_curves[i]; + } + return NULL; +} + + +static EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve, + const u8 *privkey, size_t privkey_len) +{ + EVP_PKEY *pkey; + EC_KEY *eckey; + const EC_GROUP *group; + int nid; + + pkey = EVP_PKEY_new(); + if (!pkey) + return NULL; + eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len); + if (!eckey) { + wpa_printf(MSG_INFO, + "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + EVP_PKEY_free(pkey); + return NULL; + } + group = EC_KEY_get0_group(eckey); + if (!group) { + EC_KEY_free(eckey); + EVP_PKEY_free(pkey); + return NULL; + } + nid = EC_GROUP_get_curve_name(group); + *curve = dpp_get_curve_nid(nid); + if (!*curve) { + wpa_printf(MSG_INFO, + "DPP: Unsupported curve (nid=%d) in pre-assigned key", + nid); + EC_KEY_free(eckey); + EVP_PKEY_free(pkey); + return NULL; + } + + if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) { + EC_KEY_free(eckey); + EVP_PKEY_free(pkey); + return NULL; + } + return pkey; +} + + +typedef struct { + /* AlgorithmIdentifier ecPublicKey with optional parameters present + * as an OID identifying the curve */ + X509_ALGOR *alg; + /* Compressed format public key per ANSI X9.63 */ + ASN1_BIT_STRING *pub_key; +} DPP_BOOTSTRAPPING_KEY; + +ASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = { + ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR), + ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING) +} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY); + +IMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY); + + +static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key) +{ + unsigned char *der = NULL; + int der_len; + EC_KEY *eckey; + struct wpabuf *ret = NULL; + size_t len; + const EC_GROUP *group; + const EC_POINT *point; + BN_CTX *ctx; + DPP_BOOTSTRAPPING_KEY *bootstrap = NULL; + int nid; + + ctx = BN_CTX_new(); + eckey = EVP_PKEY_get1_EC_KEY(key); + if (!ctx || !eckey) + goto fail; + + group = EC_KEY_get0_group(eckey); + point = EC_KEY_get0_public_key(eckey); + if (!group || !point) + goto fail; + dpp_debug_print_point("DPP: bootstrap public key", group, point); + nid = EC_GROUP_get_curve_name(group); + + bootstrap = DPP_BOOTSTRAPPING_KEY_new(); + if (!bootstrap || + X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC), + V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1) + goto fail; + + len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, + NULL, 0, ctx); + if (len == 0) + goto fail; + + der = OPENSSL_malloc(len); + if (!der) + goto fail; + len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, + der, len, ctx); + + OPENSSL_free(bootstrap->pub_key->data); + bootstrap->pub_key->data = der; + der = NULL; + bootstrap->pub_key->length = len; + /* No unused bits */ + bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07); + bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT; + + der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der); + if (der_len <= 0) { + wpa_printf(MSG_ERROR, + "DDP: Failed to build DER encoded public key"); + goto fail; + } + + ret = wpabuf_alloc_copy(der, der_len); +fail: + DPP_BOOTSTRAPPING_KEY_free(bootstrap); + OPENSSL_free(der); + EC_KEY_free(eckey); + BN_CTX_free(ctx); + return ret; +} + + +int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi) +{ + struct wpabuf *der; + int res; + const u8 *addr[1]; + size_t len[1]; + + der = dpp_bootstrap_key_der(bi->pubkey); + if (!der) + return -1; + wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)", + der); + + addr[0] = wpabuf_head(der); + len[0] = wpabuf_len(der); + res = sha256_vector(1, addr, len, bi->pubkey_hash); + if (res < 0) + wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key"); + else + wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash, + SHA256_MAC_LEN); + wpabuf_free(der); + return res; +} + + +char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve, + const u8 *privkey, size_t privkey_len) +{ + unsigned char *base64 = NULL; + char *pos, *end; + size_t len; + struct wpabuf *der = NULL; + const u8 *addr[1]; + int res; + + if (!curve) { + bi->curve = &dpp_curves[0]; + } else { + bi->curve = dpp_get_curve_name(curve); + if (!bi->curve) { + wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", + curve); + return NULL; + } + } + if (privkey) + bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len); + else + bi->pubkey = dpp_gen_keypair(bi->curve); + if (!bi->pubkey) + goto fail; + bi->own = 1; + + der = dpp_bootstrap_key_der(bi->pubkey); + if (!der) + goto fail; + wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)", + der); + + addr[0] = wpabuf_head(der); + len = wpabuf_len(der); + res = sha256_vector(1, addr, &len, bi->pubkey_hash); + if (res < 0) { + wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash, + SHA256_MAC_LEN); + + base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len); + wpabuf_free(der); + der = NULL; + if (!base64) + goto fail; + pos = (char *) base64; + end = pos + len; + for (;;) { + pos = os_strchr(pos, '\n'); + if (!pos) + break; + os_memmove(pos, pos + 1, end - pos); + } + return (char *) base64; +fail: + os_free(base64); + wpabuf_free(der); + return NULL; +} + + +static int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1, + unsigned int hash_len) +{ + u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN]; + const char *info = "first intermediate key"; + int res; + + /* k1 = HKDF(<>, "first intermediate key", M.x) */ + + /* HKDF-Extract(<>, M.x) */ + os_memset(salt, 0, hash_len); + if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)", + prk, hash_len); + + /* HKDF-Expand(PRK, info, L) */ + res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len); + os_memset(prk, 0, hash_len); + if (res < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)", + k1, hash_len); + return 0; +} + + +static int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2, + unsigned int hash_len) +{ + u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN]; + const char *info = "second intermediate key"; + int res; + + /* k2 = HKDF(<>, "second intermediate key", N.x) */ + + /* HKDF-Extract(<>, N.x) */ + os_memset(salt, 0, hash_len); + res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk); + if (res < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)", + prk, hash_len); + + /* HKDF-Expand(PRK, info, L) */ + res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len); + os_memset(prk, 0, hash_len); + if (res < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)", + k2, hash_len); + return 0; +} + + +static int dpp_derive_ke(struct dpp_authentication *auth, u8 *ke, + unsigned int hash_len) +{ + size_t nonce_len; + u8 nonces[2 * DPP_MAX_NONCE_LEN]; + const char *info_ke = "DPP Key"; + u8 prk[DPP_MAX_HASH_LEN]; + int res; + const u8 *addr[3]; + size_t len[3]; + size_t num_elem = 0; + + if (!auth->Mx_len || !auth->Nx_len) { + wpa_printf(MSG_DEBUG, + "DPP: Mx/Nx not available - cannot derive ke"); + return -1; + } + + /* ke = HKDF(I-nonce | R-nonce, "DPP Key", M.x | N.x [| L.x]) */ + + /* HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */ + nonce_len = auth->curve->nonce_len; + os_memcpy(nonces, auth->i_nonce, nonce_len); + os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len); + addr[num_elem] = auth->Mx; + len[num_elem] = auth->Mx_len; + num_elem++; + addr[num_elem] = auth->Nx; + len[num_elem] = auth->Nx_len; + num_elem++; + if (auth->peer_bi && auth->own_bi) { + if (!auth->Lx_len) { + wpa_printf(MSG_DEBUG, + "DPP: Lx not available - cannot derive ke"); + return -1; + } + addr[num_elem] = auth->Lx; + len[num_elem] = auth->secret_len; + num_elem++; + } + res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len, + num_elem, addr, len, prk); + if (res < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)", + prk, hash_len); + + /* HKDF-Expand(PRK, info, L) */ + res = dpp_hkdf_expand(hash_len, prk, hash_len, info_ke, ke, hash_len); + os_memset(prk, 0, hash_len); + if (res < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ke = HKDF-Expand(PRK, info, L)", + ke, hash_len); + return 0; +} + + +static void dpp_build_attr_status(struct wpabuf *msg, + enum dpp_status_error status) +{ + wpa_printf(MSG_DEBUG, "DPP: Status %d", status); + wpabuf_put_le16(msg, DPP_ATTR_STATUS); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, status); +} + + +static void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg, + const u8 *hash) +{ + if (hash) { + wpa_printf(MSG_DEBUG, "DPP: R-Bootstrap Key Hash"); + wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH); + wpabuf_put_le16(msg, SHA256_MAC_LEN); + wpabuf_put_data(msg, hash, SHA256_MAC_LEN); + } +} + + +static void dpp_build_attr_i_bootstrap_key_hash(struct wpabuf *msg, + const u8 *hash) +{ + if (hash) { + wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash"); + wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH); + wpabuf_put_le16(msg, SHA256_MAC_LEN); + wpabuf_put_data(msg, hash, SHA256_MAC_LEN); + } +} + + +static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth, + const struct wpabuf *pi, + size_t nonce_len, + const u8 *r_pubkey_hash, + const u8 *i_pubkey_hash, + unsigned int neg_freq) +{ + struct wpabuf *msg; + u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1]; + u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE]; + u8 *pos; + const u8 *addr[2]; + size_t len[2], siv_len, attr_len; + u8 *attr_start, *attr_end; + + /* Build DPP Authentication Request frame attributes */ + attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) + + 4 + sizeof(wrapped_data); + if (neg_freq > 0) + attr_len += 4 + 2; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) + attr_len += 5; +#endif /* CONFIG_TESTING_OPTIONS */ + msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len); + if (!msg) + return NULL; + + attr_start = wpabuf_put(msg, 0); + + /* Responder Bootstrapping Key Hash */ + dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash); + + /* Initiator Bootstrapping Key Hash */ + dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash); + + /* Initiator Protocol Key */ + if (pi) { + wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY); + wpabuf_put_le16(msg, wpabuf_len(pi)); + wpabuf_put_buf(msg, pi); + } + + /* Channel */ + if (neg_freq > 0) { + u8 op_class, channel; + + if (ieee80211_freq_to_channel_ext(neg_freq, 0, 0, &op_class, + &channel) == + NUM_HOSTAPD_MODES) { + wpa_printf(MSG_INFO, + "DPP: Unsupported negotiation frequency request: %d", + neg_freq); + wpabuf_free(msg); + return NULL; + } + wpabuf_put_le16(msg, DPP_ATTR_CHANNEL); + wpabuf_put_le16(msg, 2); + wpabuf_put_u8(msg, op_class); + wpabuf_put_u8(msg, channel); + } + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); + goto skip_wrapped_data; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* Wrapped data ({I-nonce, I-capabilities}k1) */ + pos = clear; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce"); + goto skip_i_nonce; + } + if (dpp_test == DPP_TEST_INVALID_I_NONCE_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-nonce"); + WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE); + pos += 2; + WPA_PUT_LE16(pos, nonce_len - 1); + pos += 2; + os_memcpy(pos, auth->i_nonce, nonce_len - 1); + pos += nonce_len - 1; + goto skip_i_nonce; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* I-nonce */ + WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE); + pos += 2; + WPA_PUT_LE16(pos, nonce_len); + pos += 2; + os_memcpy(pos, auth->i_nonce, nonce_len); + pos += nonce_len; + +#ifdef CONFIG_TESTING_OPTIONS +skip_i_nonce: + if (dpp_test == DPP_TEST_NO_I_CAPAB_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-capab"); + goto skip_i_capab; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* I-capabilities */ + WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES); + pos += 2; + WPA_PUT_LE16(pos, 1); + pos += 2; + auth->i_capab = auth->allowed_roles; + *pos++ = auth->i_capab; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_ZERO_I_CAPAB) { + wpa_printf(MSG_INFO, "DPP: TESTING - zero I-capabilities"); + pos[-1] = 0; + } +skip_i_capab: +#endif /* CONFIG_TESTING_OPTIONS */ + + attr_end = wpabuf_put(msg, 0); + + /* OUI, OUI type, Crypto Suite, DPP frame type */ + addr[0] = wpabuf_head_u8(msg) + 2; + len[0] = 3 + 1 + 1 + 1; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + + /* Attributes before Wrapped Data */ + addr[1] = attr_start; + len[1] = attr_end - attr_start; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + siv_len = pos - clear; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len); + if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len, + 2, addr, len, wrapped_data) < 0) { + wpabuf_free(msg); + return NULL; + } + siv_len += AES_BLOCK_SIZE; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, siv_len); + + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, siv_len); + wpabuf_put_data(msg, wrapped_data, siv_len); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); + dpp_build_attr_status(msg, DPP_STATUS_OK); + } +skip_wrapped_data: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Authentication Request frame attributes", msg); + + return msg; +} + + +static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth, + enum dpp_status_error status, + const struct wpabuf *pr, + size_t nonce_len, + const u8 *r_pubkey_hash, + const u8 *i_pubkey_hash, + const u8 *r_nonce, const u8 *i_nonce, + const u8 *wrapped_r_auth, + size_t wrapped_r_auth_len, + const u8 *siv_key) +{ + struct wpabuf *msg; +#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \ + 4 + 4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE + u8 clear[DPP_AUTH_RESP_CLEAR_LEN]; + u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE]; + const u8 *addr[2]; + size_t len[2], siv_len, attr_len; + u8 *attr_start, *attr_end, *pos; + + auth->waiting_auth_conf = 1; + auth->auth_resp_tries = 0; + + /* Build DPP Authentication Response frame attributes */ + attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) + + 4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data); +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) + attr_len += 5; +#endif /* CONFIG_TESTING_OPTIONS */ + msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len); + if (!msg) + return NULL; + + attr_start = wpabuf_put(msg, 0); + + /* DPP Status */ + if (status != 255) + dpp_build_attr_status(msg, status); + + /* Responder Bootstrapping Key Hash */ + dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash); + + /* Initiator Bootstrapping Key Hash (mutual authentication) */ + dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash); + + /* Responder Protocol Key */ + if (pr) { + wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY); + wpabuf_put_le16(msg, wpabuf_len(pr)); + wpabuf_put_buf(msg, pr); + } + + attr_end = wpabuf_put(msg, 0); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); + goto skip_wrapped_data; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */ + pos = clear; + + if (r_nonce) { + /* R-nonce */ + WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE); + pos += 2; + WPA_PUT_LE16(pos, nonce_len); + pos += 2; + os_memcpy(pos, r_nonce, nonce_len); + pos += nonce_len; + } + + if (i_nonce) { + /* I-nonce */ + WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE); + pos += 2; + WPA_PUT_LE16(pos, nonce_len); + pos += 2; + os_memcpy(pos, i_nonce, nonce_len); +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - I-nonce mismatch"); + pos[nonce_len / 2] ^= 0x01; + } +#endif /* CONFIG_TESTING_OPTIONS */ + pos += nonce_len; + } + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_R_CAPAB_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-capab"); + goto skip_r_capab; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* R-capabilities */ + WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES); + pos += 2; + WPA_PUT_LE16(pos, 1); + pos += 2; + auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR : + DPP_CAPAB_ENROLLEE; + *pos++ = auth->r_capab; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_ZERO_R_CAPAB) { + wpa_printf(MSG_INFO, "DPP: TESTING - zero R-capabilities"); + pos[-1] = 0; + } else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - incompatible R-capabilities"); + if ((auth->i_capab & DPP_CAPAB_ROLE_MASK) == + (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) + pos[-1] = 0; + else + pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE : + DPP_CAPAB_CONFIGURATOR; + } +skip_r_capab: +#endif /* CONFIG_TESTING_OPTIONS */ + + if (wrapped_r_auth) { + /* {R-auth}ke */ + WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA); + pos += 2; + WPA_PUT_LE16(pos, wrapped_r_auth_len); + pos += 2; + os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len); + pos += wrapped_r_auth_len; + } + + /* OUI, OUI type, Crypto Suite, DPP frame type */ + addr[0] = wpabuf_head_u8(msg) + 2; + len[0] = 3 + 1 + 1 + 1; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + + /* Attributes before Wrapped Data */ + addr[1] = attr_start; + len[1] = attr_end - attr_start; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + siv_len = pos - clear; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len); + if (aes_siv_encrypt(siv_key, auth->curve->hash_len, clear, siv_len, + 2, addr, len, wrapped_data) < 0) { + wpabuf_free(msg); + return NULL; + } + siv_len += AES_BLOCK_SIZE; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, siv_len); + + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, siv_len); + wpabuf_put_data(msg, wrapped_data, siv_len); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); + dpp_build_attr_status(msg, DPP_STATUS_OK); + } +skip_wrapped_data: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Authentication Response frame attributes", msg); + return msg; +} + + +static int dpp_channel_ok_init(struct hostapd_hw_modes *own_modes, + u16 num_modes, unsigned int freq) +{ + u16 m; + int c, flag; + + if (!own_modes || !num_modes) + return 1; + + for (m = 0; m < num_modes; m++) { + for (c = 0; c < own_modes[m].num_channels; c++) { + if ((unsigned int) own_modes[m].channels[c].freq != + freq) + continue; + flag = own_modes[m].channels[c].flag; + if (!(flag & (HOSTAPD_CHAN_DISABLED | + HOSTAPD_CHAN_NO_IR | + HOSTAPD_CHAN_RADAR))) + return 1; + } + } + + wpa_printf(MSG_DEBUG, "DPP: Peer channel %u MHz not supported", freq); + return 0; +} + + +static int freq_included(const unsigned int freqs[], unsigned int num, + unsigned int freq) +{ + while (num > 0) { + if (freqs[--num] == freq) + return 1; + } + return 0; +} + + +static void freq_to_start(unsigned int freqs[], unsigned int num, + unsigned int freq) +{ + unsigned int i; + + for (i = 0; i < num; i++) { + if (freqs[i] == freq) + break; + } + if (i == 0 || i >= num) + return; + os_memmove(&freqs[1], &freqs[0], i * sizeof(freqs[0])); + freqs[0] = freq; +} + + +static int dpp_channel_intersect(struct dpp_authentication *auth, + struct hostapd_hw_modes *own_modes, + u16 num_modes) +{ + struct dpp_bootstrap_info *peer_bi = auth->peer_bi; + unsigned int i, freq; + + for (i = 0; i < peer_bi->num_freq; i++) { + freq = peer_bi->freq[i]; + if (freq_included(auth->freq, auth->num_freq, freq)) + continue; + if (dpp_channel_ok_init(own_modes, num_modes, freq)) + auth->freq[auth->num_freq++] = freq; + } + if (!auth->num_freq) { + wpa_printf(MSG_INFO, + "DPP: No available channels for initiating DPP Authentication"); + return -1; + } + auth->curr_freq = auth->freq[0]; + return 0; +} + + +static int dpp_channel_local_list(struct dpp_authentication *auth, + struct hostapd_hw_modes *own_modes, + u16 num_modes) +{ + u16 m; + int c, flag; + unsigned int freq; + + auth->num_freq = 0; + + if (!own_modes || !num_modes) { + auth->freq[0] = 2412; + auth->freq[1] = 2437; + auth->freq[2] = 2462; + auth->num_freq = 3; + return 0; + } + + for (m = 0; m < num_modes; m++) { + for (c = 0; c < own_modes[m].num_channels; c++) { + freq = own_modes[m].channels[c].freq; + flag = own_modes[m].channels[c].flag; + if (flag & (HOSTAPD_CHAN_DISABLED | + HOSTAPD_CHAN_NO_IR | + HOSTAPD_CHAN_RADAR)) + continue; + if (freq_included(auth->freq, auth->num_freq, freq)) + continue; + auth->freq[auth->num_freq++] = freq; + if (auth->num_freq == DPP_BOOTSTRAP_MAX_FREQ) { + m = num_modes; + break; + } + } + } + + return auth->num_freq == 0 ? -1 : 0; +} + + +static int dpp_prepare_channel_list(struct dpp_authentication *auth, + struct hostapd_hw_modes *own_modes, + u16 num_modes) +{ + int res; + char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end; + unsigned int i; + + if (auth->peer_bi->num_freq > 0) + res = dpp_channel_intersect(auth, own_modes, num_modes); + else + res = dpp_channel_local_list(auth, own_modes, num_modes); + if (res < 0) + return res; + + /* Prioritize 2.4 GHz channels 6, 1, 11 (in this order) to hit the most + * likely channels first. */ + freq_to_start(auth->freq, auth->num_freq, 2462); + freq_to_start(auth->freq, auth->num_freq, 2412); + freq_to_start(auth->freq, auth->num_freq, 2437); + + auth->freq_idx = 0; + auth->curr_freq = auth->freq[0]; + + pos = freqs; + end = pos + sizeof(freqs); + for (i = 0; i < auth->num_freq; i++) { + res = os_snprintf(pos, end - pos, " %u", auth->freq[i]); + if (os_snprintf_error(end - pos, res)) + break; + pos += res; + } + *pos = '\0'; + wpa_printf(MSG_DEBUG, "DPP: Possible frequencies for initiating:%s", + freqs); + + return 0; +} + + +static int dpp_autogen_bootstrap_key(struct dpp_authentication *auth) +{ + struct dpp_bootstrap_info *bi; + char *pk = NULL; + size_t len; + + if (auth->own_bi) + return 0; /* already generated */ + + bi = os_zalloc(sizeof(*bi)); + if (!bi) + return -1; + bi->type = DPP_BOOTSTRAP_QR_CODE; + pk = dpp_keygen(bi, auth->peer_bi->curve->name, NULL, 0); + if (!pk) + goto fail; + + len = 4; /* "DPP:" */ + len += 4 + os_strlen(pk); + bi->uri = os_malloc(len + 1); + if (!bi->uri) + goto fail; + os_snprintf(bi->uri, len + 1, "DPP:K:%s;;", pk); + wpa_printf(MSG_DEBUG, + "DPP: Auto-generated own bootstrapping key info: URI %s", + bi->uri); + + auth->tmp_own_bi = auth->own_bi = bi; + + os_free(pk); + + return 0; +fail: + os_free(pk); + dpp_bootstrap_info_free(bi); + return -1; +} + + +struct dpp_authentication * dpp_auth_init(void *msg_ctx, + struct dpp_bootstrap_info *peer_bi, + struct dpp_bootstrap_info *own_bi, + u8 dpp_allowed_roles, + unsigned int neg_freq, + struct hostapd_hw_modes *own_modes, + u16 num_modes) +{ + struct dpp_authentication *auth; + size_t nonce_len; + EVP_PKEY_CTX *ctx = NULL; + size_t secret_len; + struct wpabuf *pi = NULL; + const u8 *r_pubkey_hash, *i_pubkey_hash; +#ifdef CONFIG_TESTING_OPTIONS + u8 test_hash[SHA256_MAC_LEN]; +#endif /* CONFIG_TESTING_OPTIONS */ + + auth = os_zalloc(sizeof(*auth)); + if (!auth) + return NULL; + auth->msg_ctx = msg_ctx; + auth->initiator = 1; + auth->waiting_auth_resp = 1; + auth->allowed_roles = dpp_allowed_roles; + auth->configurator = !!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR); + auth->peer_bi = peer_bi; + auth->own_bi = own_bi; + auth->curve = peer_bi->curve; + + if (dpp_autogen_bootstrap_key(auth) < 0 || + dpp_prepare_channel_list(auth, own_modes, num_modes) < 0) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_nonce_override_len > 0) { + wpa_printf(MSG_INFO, "DPP: TESTING - override I-nonce"); + nonce_len = dpp_nonce_override_len; + os_memcpy(auth->i_nonce, dpp_nonce_override, nonce_len); + } else { + nonce_len = auth->curve->nonce_len; + if (random_get_bytes(auth->i_nonce, nonce_len)) { + wpa_printf(MSG_ERROR, + "DPP: Failed to generate I-nonce"); + goto fail; + } + } +#else /* CONFIG_TESTING_OPTIONS */ + nonce_len = auth->curve->nonce_len; + if (random_get_bytes(auth->i_nonce, nonce_len)) { + wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce"); + goto fail; + } +#endif /* CONFIG_TESTING_OPTIONS */ + wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_protocol_key_override_len) { + const struct dpp_curve_params *tmp_curve; + + wpa_printf(MSG_INFO, + "DPP: TESTING - override protocol key"); + auth->own_protocol_key = dpp_set_keypair( + &tmp_curve, dpp_protocol_key_override, + dpp_protocol_key_override_len); + } else { + auth->own_protocol_key = dpp_gen_keypair(auth->curve); + } +#else /* CONFIG_TESTING_OPTIONS */ + auth->own_protocol_key = dpp_gen_keypair(auth->curve); +#endif /* CONFIG_TESTING_OPTIONS */ + if (!auth->own_protocol_key) + goto fail; + + pi = dpp_get_pubkey_point(auth->own_protocol_key, 0); + if (!pi) + goto fail; + + /* ECDH: M = pI * BR */ + ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, auth->peer_bi->pubkey) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || + secret_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + auth->secret_len = secret_len; + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)", + auth->Mx, auth->secret_len); + auth->Mx_len = auth->secret_len; + + if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1, + auth->curve->hash_len) < 0) + goto fail; + + r_pubkey_hash = auth->peer_bi->pubkey_hash; + i_pubkey_hash = auth->own_bi->pubkey_hash; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); + r_pubkey_hash = NULL; + } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid R-Bootstrap Key Hash"); + os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + r_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); + i_pubkey_hash = NULL; + } else if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid I-Bootstrap Key Hash"); + os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + i_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-Proto Key"); + wpabuf_free(pi); + pi = NULL; + } else if (dpp_test == DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-Proto Key"); + wpabuf_free(pi); + pi = wpabuf_alloc(2 * auth->curve->prime_len); + if (!pi || dpp_test_gen_invalid_key(pi, auth->curve) < 0) + goto fail; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + auth->req_msg = dpp_auth_build_req(auth, pi, nonce_len, r_pubkey_hash, + i_pubkey_hash, neg_freq); + if (!auth->req_msg) + goto fail; + +out: + wpabuf_free(pi); + EVP_PKEY_CTX_free(ctx); + return auth; +fail: + dpp_auth_deinit(auth); + auth = NULL; + goto out; +} + + +struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth, + const char *json) +{ + size_t nonce_len; + size_t json_len, clear_len; + struct wpabuf *clear = NULL, *msg = NULL; + u8 *wrapped; + size_t attr_len; + + wpa_printf(MSG_DEBUG, "DPP: Build configuration request"); + + nonce_len = auth->curve->nonce_len; + if (random_get_bytes(auth->e_nonce, nonce_len)) { + wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", auth->e_nonce, nonce_len); + json_len = os_strlen(json); + wpa_hexdump_ascii(MSG_DEBUG, "DPP: configAttr JSON", json, json_len); + + /* { E-nonce, configAttrib }ke */ + clear_len = 4 + nonce_len + 4 + json_len; + clear = wpabuf_alloc(clear_len); + attr_len = 4 + clear_len + AES_BLOCK_SIZE; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) + attr_len += 5; +#endif /* CONFIG_TESTING_OPTIONS */ + msg = wpabuf_alloc(attr_len); + if (!clear || !msg) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce"); + goto skip_e_nonce; + } + if (dpp_test == DPP_TEST_INVALID_E_NONCE_CONF_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid E-nonce"); + wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); + wpabuf_put_le16(clear, nonce_len - 1); + wpabuf_put_data(clear, auth->e_nonce, nonce_len - 1); + goto skip_e_nonce; + } + if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); + goto skip_wrapped_data; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* E-nonce */ + wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); + wpabuf_put_le16(clear, nonce_len); + wpabuf_put_data(clear, auth->e_nonce, nonce_len); + +#ifdef CONFIG_TESTING_OPTIONS +skip_e_nonce: + if (dpp_test == DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no configAttrib"); + goto skip_conf_attr_obj; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* configAttrib */ + wpabuf_put_le16(clear, DPP_ATTR_CONFIG_ATTR_OBJ); + wpabuf_put_le16(clear, json_len); + wpabuf_put_data(clear, json, json_len); + +#ifdef CONFIG_TESTING_OPTIONS +skip_conf_attr_obj: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + + /* No AES-SIV AD */ + wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); + if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, + wpabuf_head(clear), wpabuf_len(clear), + 0, NULL, NULL, wrapped) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); + dpp_build_attr_status(msg, DPP_STATUS_OK); + } +skip_wrapped_data: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Configuration Request frame attributes", msg); + wpabuf_free(clear); + return msg; + +fail: + wpabuf_free(clear); + wpabuf_free(msg); + return NULL; +} + + +static void dpp_auth_success(struct dpp_authentication *auth) +{ + wpa_printf(MSG_DEBUG, + "DPP: Authentication success - clear temporary keys"); + os_memset(auth->Mx, 0, sizeof(auth->Mx)); + auth->Mx_len = 0; + os_memset(auth->Nx, 0, sizeof(auth->Nx)); + auth->Nx_len = 0; + os_memset(auth->Lx, 0, sizeof(auth->Lx)); + auth->Lx_len = 0; + os_memset(auth->k1, 0, sizeof(auth->k1)); + os_memset(auth->k2, 0, sizeof(auth->k2)); + + auth->auth_success = 1; +} + + +static int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth) +{ + struct wpabuf *pix, *prx, *bix, *brx; + const u8 *addr[7]; + size_t len[7]; + size_t i, num_elem = 0; + size_t nonce_len; + u8 zero = 0; + int res = -1; + + /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */ + nonce_len = auth->curve->nonce_len; + + if (auth->initiator) { + pix = dpp_get_pubkey_point(auth->own_protocol_key, 0); + prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0); + if (auth->own_bi) + bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); + else + bix = NULL; + brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); + } else { + pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0); + prx = dpp_get_pubkey_point(auth->own_protocol_key, 0); + if (auth->peer_bi) + bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); + else + bix = NULL; + brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); + } + if (!pix || !prx || !brx) + goto fail; + + addr[num_elem] = auth->i_nonce; + len[num_elem] = nonce_len; + num_elem++; + + addr[num_elem] = auth->r_nonce; + len[num_elem] = nonce_len; + num_elem++; + + addr[num_elem] = wpabuf_head(pix); + len[num_elem] = wpabuf_len(pix) / 2; + num_elem++; + + addr[num_elem] = wpabuf_head(prx); + len[num_elem] = wpabuf_len(prx) / 2; + num_elem++; + + if (bix) { + addr[num_elem] = wpabuf_head(bix); + len[num_elem] = wpabuf_len(bix) / 2; + num_elem++; + } + + addr[num_elem] = wpabuf_head(brx); + len[num_elem] = wpabuf_len(brx) / 2; + num_elem++; + + addr[num_elem] = &zero; + len[num_elem] = 1; + num_elem++; + + wpa_printf(MSG_DEBUG, "DPP: R-auth hash components"); + for (i = 0; i < num_elem; i++) + wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]); + res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth); + if (res == 0) + wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth, + auth->curve->hash_len); +fail: + wpabuf_free(pix); + wpabuf_free(prx); + wpabuf_free(bix); + wpabuf_free(brx); + return res; +} + + +static int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth) +{ + struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL; + const u8 *addr[7]; + size_t len[7]; + size_t i, num_elem = 0; + size_t nonce_len; + u8 one = 1; + int res = -1; + + /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */ + nonce_len = auth->curve->nonce_len; + + if (auth->initiator) { + pix = dpp_get_pubkey_point(auth->own_protocol_key, 0); + prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0); + if (auth->own_bi) + bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); + else + bix = NULL; + if (!auth->peer_bi) + goto fail; + brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); + } else { + pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0); + prx = dpp_get_pubkey_point(auth->own_protocol_key, 0); + if (auth->peer_bi) + bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); + else + bix = NULL; + if (!auth->own_bi) + goto fail; + brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); + } + if (!pix || !prx || !brx) + goto fail; + + addr[num_elem] = auth->r_nonce; + len[num_elem] = nonce_len; + num_elem++; + + addr[num_elem] = auth->i_nonce; + len[num_elem] = nonce_len; + num_elem++; + + addr[num_elem] = wpabuf_head(prx); + len[num_elem] = wpabuf_len(prx) / 2; + num_elem++; + + addr[num_elem] = wpabuf_head(pix); + len[num_elem] = wpabuf_len(pix) / 2; + num_elem++; + + addr[num_elem] = wpabuf_head(brx); + len[num_elem] = wpabuf_len(brx) / 2; + num_elem++; + + if (bix) { + addr[num_elem] = wpabuf_head(bix); + len[num_elem] = wpabuf_len(bix) / 2; + num_elem++; + } + + addr[num_elem] = &one; + len[num_elem] = 1; + num_elem++; + + wpa_printf(MSG_DEBUG, "DPP: I-auth hash components"); + for (i = 0; i < num_elem; i++) + wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]); + res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth); + if (res == 0) + wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth, + auth->curve->hash_len); +fail: + wpabuf_free(pix); + wpabuf_free(prx); + wpabuf_free(bix); + wpabuf_free(brx); + return res; +} + + +static int dpp_auth_derive_l_responder(struct dpp_authentication *auth) +{ + const EC_GROUP *group; + EC_POINT *l = NULL; + EC_KEY *BI = NULL, *bR = NULL, *pR = NULL; + const EC_POINT *BI_point; + BN_CTX *bnctx; + BIGNUM *lx, *sum, *q; + const BIGNUM *bR_bn, *pR_bn; + int ret = -1; + + /* L = ((bR + pR) modulo q) * BI */ + + bnctx = BN_CTX_new(); + sum = BN_new(); + q = BN_new(); + lx = BN_new(); + if (!bnctx || !sum || !q || !lx) + goto fail; + BI = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey); + if (!BI) + goto fail; + BI_point = EC_KEY_get0_public_key(BI); + group = EC_KEY_get0_group(BI); + if (!group) + goto fail; + + bR = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey); + pR = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key); + if (!bR || !pR) + goto fail; + bR_bn = EC_KEY_get0_private_key(bR); + pR_bn = EC_KEY_get0_private_key(pR); + if (!bR_bn || !pR_bn) + goto fail; + if (EC_GROUP_get_order(group, q, bnctx) != 1 || + BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1) + goto fail; + l = EC_POINT_new(group); + if (!l || + EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 || + EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL, + bnctx) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0) + goto fail; + wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len); + auth->Lx_len = auth->secret_len; + ret = 0; +fail: + EC_POINT_clear_free(l); + EC_KEY_free(BI); + EC_KEY_free(bR); + EC_KEY_free(pR); + BN_clear_free(lx); + BN_clear_free(sum); + BN_free(q); + BN_CTX_free(bnctx); + return ret; +} + + +static int dpp_auth_derive_l_initiator(struct dpp_authentication *auth) +{ + const EC_GROUP *group; + EC_POINT *l = NULL, *sum = NULL; + EC_KEY *bI = NULL, *BR = NULL, *PR = NULL; + const EC_POINT *BR_point, *PR_point; + BN_CTX *bnctx; + BIGNUM *lx; + const BIGNUM *bI_bn; + int ret = -1; + + /* L = bI * (BR + PR) */ + + bnctx = BN_CTX_new(); + lx = BN_new(); + if (!bnctx || !lx) + goto fail; + BR = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey); + PR = EVP_PKEY_get1_EC_KEY(auth->peer_protocol_key); + if (!BR || !PR) + goto fail; + BR_point = EC_KEY_get0_public_key(BR); + PR_point = EC_KEY_get0_public_key(PR); + + bI = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey); + if (!bI) + goto fail; + group = EC_KEY_get0_group(bI); + bI_bn = EC_KEY_get0_private_key(bI); + if (!group || !bI_bn) + goto fail; + sum = EC_POINT_new(group); + l = EC_POINT_new(group); + if (!sum || !l || + EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 || + EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 || + EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL, + bnctx) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0) + goto fail; + wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len); + auth->Lx_len = auth->secret_len; + ret = 0; +fail: + EC_POINT_clear_free(l); + EC_POINT_clear_free(sum); + EC_KEY_free(bI); + EC_KEY_free(BR); + EC_KEY_free(PR); + BN_clear_free(lx); + BN_CTX_free(bnctx); + return ret; +} + + +static int dpp_auth_build_resp_ok(struct dpp_authentication *auth) +{ + size_t nonce_len; + EVP_PKEY_CTX *ctx = NULL; + size_t secret_len; + struct wpabuf *msg, *pr = NULL; + u8 r_auth[4 + DPP_MAX_HASH_LEN]; + u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth; + size_t wrapped_r_auth_len; + int ret = -1; + const u8 *r_pubkey_hash, *i_pubkey_hash, *r_nonce, *i_nonce; + enum dpp_status_error status = DPP_STATUS_OK; +#ifdef CONFIG_TESTING_OPTIONS + u8 test_hash[SHA256_MAC_LEN]; +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response"); + if (!auth->own_bi) + return -1; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_nonce_override_len > 0) { + wpa_printf(MSG_INFO, "DPP: TESTING - override R-nonce"); + nonce_len = dpp_nonce_override_len; + os_memcpy(auth->r_nonce, dpp_nonce_override, nonce_len); + } else { + nonce_len = auth->curve->nonce_len; + if (random_get_bytes(auth->r_nonce, nonce_len)) { + wpa_printf(MSG_ERROR, + "DPP: Failed to generate R-nonce"); + goto fail; + } + } +#else /* CONFIG_TESTING_OPTIONS */ + nonce_len = auth->curve->nonce_len; + if (random_get_bytes(auth->r_nonce, nonce_len)) { + wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce"); + goto fail; + } +#endif /* CONFIG_TESTING_OPTIONS */ + wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_protocol_key_override_len) { + const struct dpp_curve_params *tmp_curve; + + wpa_printf(MSG_INFO, + "DPP: TESTING - override protocol key"); + auth->own_protocol_key = dpp_set_keypair( + &tmp_curve, dpp_protocol_key_override, + dpp_protocol_key_override_len); + } else { + auth->own_protocol_key = dpp_gen_keypair(auth->curve); + } +#else /* CONFIG_TESTING_OPTIONS */ + auth->own_protocol_key = dpp_gen_keypair(auth->curve); +#endif /* CONFIG_TESTING_OPTIONS */ + if (!auth->own_protocol_key) + goto fail; + + pr = dpp_get_pubkey_point(auth->own_protocol_key, 0); + if (!pr) + goto fail; + + /* ECDH: N = pR * PI */ + ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, auth->peer_protocol_key) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || + secret_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", + auth->Nx, auth->secret_len); + auth->Nx_len = auth->secret_len; + + if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2, + auth->curve->hash_len) < 0) + goto fail; + + if (auth->own_bi && auth->peer_bi) { + /* Mutual authentication */ + if (dpp_auth_derive_l_responder(auth) < 0) + goto fail; + } + + if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0) + goto fail; + + /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */ + WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG); + WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len); + if (dpp_gen_r_auth(auth, r_auth + 4) < 0) + goto fail; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - R-auth mismatch"); + r_auth[4 + auth->curve->hash_len / 2] ^= 0x01; + } +#endif /* CONFIG_TESTING_OPTIONS */ + if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, + r_auth, 4 + auth->curve->hash_len, + 0, NULL, NULL, wrapped_r_auth) < 0) + goto fail; + wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE; + wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke", + wrapped_r_auth, wrapped_r_auth_len); + w_r_auth = wrapped_r_auth; + + r_pubkey_hash = auth->own_bi->pubkey_hash; + if (auth->peer_bi) + i_pubkey_hash = auth->peer_bi->pubkey_hash; + else + i_pubkey_hash = NULL; + + i_nonce = auth->i_nonce; + r_nonce = auth->r_nonce; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); + r_pubkey_hash = NULL; + } else if (dpp_test == + DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid R-Bootstrap Key Hash"); + os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + r_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); + i_pubkey_hash = NULL; + } else if (dpp_test == + DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid I-Bootstrap Key Hash"); + if (i_pubkey_hash) + os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); + else + os_memset(test_hash, 0, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + i_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Proto Key"); + wpabuf_free(pr); + pr = NULL; + } else if (dpp_test == DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid R-Proto Key"); + wpabuf_free(pr); + pr = wpabuf_alloc(2 * auth->curve->prime_len); + if (!pr || dpp_test_gen_invalid_key(pr, auth->curve) < 0) + goto fail; + } else if (dpp_test == DPP_TEST_NO_R_AUTH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth"); + w_r_auth = NULL; + wrapped_r_auth_len = 0; + } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); + status = 255; + } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); + status = 254; + } else if (dpp_test == DPP_TEST_NO_R_NONCE_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-nonce"); + r_nonce = NULL; + } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce"); + i_nonce = NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + msg = dpp_auth_build_resp(auth, status, pr, nonce_len, + r_pubkey_hash, i_pubkey_hash, + r_nonce, i_nonce, + w_r_auth, wrapped_r_auth_len, + auth->k2); + if (!msg) + goto fail; + wpabuf_free(auth->resp_msg); + auth->resp_msg = msg; + ret = 0; +fail: + wpabuf_free(pr); + return ret; +} + + +static int dpp_auth_build_resp_status(struct dpp_authentication *auth, + enum dpp_status_error status) +{ + struct wpabuf *msg; + const u8 *r_pubkey_hash, *i_pubkey_hash, *i_nonce; +#ifdef CONFIG_TESTING_OPTIONS + u8 test_hash[SHA256_MAC_LEN]; +#endif /* CONFIG_TESTING_OPTIONS */ + + if (!auth->own_bi) + return -1; + wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response"); + + r_pubkey_hash = auth->own_bi->pubkey_hash; + if (auth->peer_bi) + i_pubkey_hash = auth->peer_bi->pubkey_hash; + else + i_pubkey_hash = NULL; + + i_nonce = auth->i_nonce; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); + r_pubkey_hash = NULL; + } else if (dpp_test == + DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid R-Bootstrap Key Hash"); + os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + r_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); + i_pubkey_hash = NULL; + } else if (dpp_test == + DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid I-Bootstrap Key Hash"); + if (i_pubkey_hash) + os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); + else + os_memset(test_hash, 0, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + i_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); + status = 255; + } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce"); + i_nonce = NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + msg = dpp_auth_build_resp(auth, status, NULL, auth->curve->nonce_len, + r_pubkey_hash, i_pubkey_hash, + NULL, i_nonce, NULL, 0, auth->k1); + if (!msg) + return -1; + wpabuf_free(auth->resp_msg); + auth->resp_msg = msg; + return 0; +} + + +struct dpp_authentication * +dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual, + struct dpp_bootstrap_info *peer_bi, + struct dpp_bootstrap_info *own_bi, + unsigned int freq, const u8 *hdr, const u8 *attr_start, + size_t attr_len) +{ + EVP_PKEY *pi = NULL; + EVP_PKEY_CTX *ctx = NULL; + size_t secret_len; + const u8 *addr[2]; + size_t len[2]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap, + *channel; + u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len, + i_bootstrap_len, channel_len; + struct dpp_authentication *auth = NULL; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at Authentication Request"); + return NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Missing or invalid required Wrapped Data attribute"); + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data", + wrapped_data, wrapped_data_len); + attr_len = wrapped_data - 4 - attr_start; + + auth = os_zalloc(sizeof(*auth)); + if (!auth) + goto fail; + auth->msg_ctx = msg_ctx; + auth->peer_bi = peer_bi; + auth->own_bi = own_bi; + auth->curve = own_bi->curve; + auth->curr_freq = freq; + + channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL, + &channel_len); + if (channel) { + int neg_freq; + + if (channel_len < 2) { + dpp_auth_fail(auth, "Too short Channel attribute"); + goto fail; + } + + neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]); + wpa_printf(MSG_DEBUG, + "DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d", + channel[0], channel[1], neg_freq); + if (neg_freq < 0) { + dpp_auth_fail(auth, + "Unsupported Channel attribute value"); + goto fail; + } + + if (auth->curr_freq != (unsigned int) neg_freq) { + wpa_printf(MSG_DEBUG, + "DPP: Changing negotiation channel from %u MHz to %u MHz", + freq, neg_freq); + auth->curr_freq = neg_freq; + } + } + + i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY, + &i_proto_len); + if (!i_proto) { + dpp_auth_fail(auth, + "Missing required Initiator Protocol Key attribute"); + goto fail; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key", + i_proto, i_proto_len); + + /* M = bR * PI */ + pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len); + if (!pi) { + dpp_auth_fail(auth, "Invalid Initiator Protocol Key"); + goto fail; + } + dpp_debug_print_key("Peer (Initiator) Protocol Key", pi); + + ctx = EVP_PKEY_CTX_new(own_bi->pubkey, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pi) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || + secret_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + dpp_auth_fail(auth, "Failed to derive ECDH shared secret"); + goto fail; + } + auth->secret_len = secret_len; + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)", + auth->Mx, auth->secret_len); + auth->Mx_len = auth->secret_len; + + if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1, + auth->curve->hash_len) < 0) + goto fail; + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + addr[1] = attr_start; + len[1] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + goto fail; + if (aes_siv_decrypt(auth->k1, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE, + &i_nonce_len); + if (!i_nonce || i_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, "Missing or invalid I-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len); + os_memcpy(auth->i_nonce, i_nonce, i_nonce_len); + + i_capab = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_I_CAPABILITIES, + &i_capab_len); + if (!i_capab || i_capab_len < 1) { + dpp_auth_fail(auth, "Missing or invalid I-capabilities"); + goto fail; + } + auth->i_capab = i_capab[0]; + wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab); + + bin_clear_free(unwrapped, unwrapped_len); + unwrapped = NULL; + + switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) { + case DPP_CAPAB_ENROLLEE: + if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) { + wpa_printf(MSG_DEBUG, + "DPP: Local policy does not allow Configurator role"); + goto not_compatible; + } + wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator"); + auth->configurator = 1; + break; + case DPP_CAPAB_CONFIGURATOR: + if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) { + wpa_printf(MSG_DEBUG, + "DPP: Local policy does not allow Enrollee role"); + goto not_compatible; + } + wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee"); + auth->configurator = 0; + break; + case DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE: + if (dpp_allowed_roles & DPP_CAPAB_ENROLLEE) { + wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee"); + auth->configurator = 0; + } else if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) { + wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator"); + auth->configurator = 1; + } else { + wpa_printf(MSG_DEBUG, + "DPP: Local policy does not allow Configurator/Enrollee role"); + goto not_compatible; + } + break; + default: + wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities"); + wpa_msg(auth->msg_ctx, MSG_INFO, + DPP_EVENT_FAIL "Invalid role in I-capabilities 0x%02x", + auth->i_capab & DPP_CAPAB_ROLE_MASK); + goto fail; + } + + auth->peer_protocol_key = pi; + pi = NULL; + if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) { + char hex[SHA256_MAC_LEN * 2 + 1]; + + wpa_printf(MSG_DEBUG, + "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time"); + if (dpp_auth_build_resp_status(auth, + DPP_STATUS_RESPONSE_PENDING) < 0) + goto fail; + i_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_I_BOOTSTRAP_KEY_HASH, + &i_bootstrap_len); + if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) { + auth->response_pending = 1; + os_memcpy(auth->waiting_pubkey_hash, + i_bootstrap, i_bootstrap_len); + wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap, + i_bootstrap_len); + } else { + hex[0] = '\0'; + } + + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE + "%s", hex); + return auth; + } + if (dpp_auth_build_resp_ok(auth) < 0) + goto fail; + + return auth; + +not_compatible: + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE + "i-capab=0x%02x", auth->i_capab); + if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) + auth->configurator = 1; + else + auth->configurator = 0; + auth->peer_protocol_key = pi; + pi = NULL; + if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0) + goto fail; + + auth->remove_on_tx_status = 1; + return auth; +fail: + bin_clear_free(unwrapped, unwrapped_len); + EVP_PKEY_free(pi); + EVP_PKEY_CTX_free(ctx); + dpp_auth_deinit(auth); + return NULL; +} + + +int dpp_notify_new_qr_code(struct dpp_authentication *auth, + struct dpp_bootstrap_info *peer_bi) +{ + if (!auth || !auth->response_pending || + os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) + return 0; + + wpa_printf(MSG_DEBUG, + "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with " + MACSTR, MAC2STR(auth->peer_mac_addr)); + auth->peer_bi = peer_bi; + + if (dpp_auth_build_resp_ok(auth) < 0) + return -1; + + return 1; +} + + +static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth, + enum dpp_status_error status) +{ + struct wpabuf *msg; + u8 i_auth[4 + DPP_MAX_HASH_LEN]; + size_t i_auth_len; + u8 r_nonce[4 + DPP_MAX_NONCE_LEN]; + size_t r_nonce_len; + const u8 *addr[2]; + size_t len[2], attr_len; + u8 *wrapped_i_auth; + u8 *wrapped_r_nonce; + u8 *attr_start, *attr_end; + const u8 *r_pubkey_hash, *i_pubkey_hash; +#ifdef CONFIG_TESTING_OPTIONS + u8 test_hash[SHA256_MAC_LEN]; +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation"); + + i_auth_len = 4 + auth->curve->hash_len; + r_nonce_len = 4 + auth->curve->nonce_len; + /* Build DPP Authentication Confirmation frame attributes */ + attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) + + 4 + i_auth_len + r_nonce_len + AES_BLOCK_SIZE; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) + attr_len += 5; +#endif /* CONFIG_TESTING_OPTIONS */ + msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len); + if (!msg) + goto fail; + + attr_start = wpabuf_put(msg, 0); + + r_pubkey_hash = auth->peer_bi->pubkey_hash; + if (auth->own_bi) + i_pubkey_hash = auth->own_bi->pubkey_hash; + else + i_pubkey_hash = NULL; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); + goto skip_status; + } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_CONF) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); + status = 254; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* DPP Status */ + dpp_build_attr_status(msg, status); + +#ifdef CONFIG_TESTING_OPTIONS +skip_status: + if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); + r_pubkey_hash = NULL; + } else if (dpp_test == + DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid R-Bootstrap Key Hash"); + os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + r_pubkey_hash = test_hash; + } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); + i_pubkey_hash = NULL; + } else if (dpp_test == + DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) { + wpa_printf(MSG_INFO, + "DPP: TESTING - invalid I-Bootstrap Key Hash"); + if (i_pubkey_hash) + os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); + else + os_memset(test_hash, 0, SHA256_MAC_LEN); + test_hash[SHA256_MAC_LEN - 1] ^= 0x01; + i_pubkey_hash = test_hash; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* Responder Bootstrapping Key Hash */ + dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash); + + /* Initiator Bootstrapping Key Hash (mutual authentication) */ + dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF) + goto skip_wrapped_data; + if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF) + i_auth_len = 0; +#endif /* CONFIG_TESTING_OPTIONS */ + + attr_end = wpabuf_put(msg, 0); + + /* OUI, OUI type, Crypto Suite, DPP frame type */ + addr[0] = wpabuf_head_u8(msg) + 2; + len[0] = 3 + 1 + 1 + 1; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + + /* Attributes before Wrapped Data */ + addr[1] = attr_start; + len[1] = attr_end - attr_start; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + if (status == DPP_STATUS_OK) { + /* I-auth wrapped with ke */ + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE); + wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF) + goto skip_i_auth; +#endif /* CONFIG_TESTING_OPTIONS */ + + /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] + * 1) */ + WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG); + WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len); + if (dpp_gen_i_auth(auth, i_auth + 4) < 0) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF) { + wpa_printf(MSG_INFO, "DPP: TESTING - I-auth mismatch"); + i_auth[4 + auth->curve->hash_len / 2] ^= 0x01; + } +skip_i_auth: +#endif /* CONFIG_TESTING_OPTIONS */ + if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, + i_auth, i_auth_len, + 2, addr, len, wrapped_i_auth) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke", + wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE); + } else { + /* R-nonce wrapped with k2 */ + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, r_nonce_len + AES_BLOCK_SIZE); + wrapped_r_nonce = wpabuf_put(msg, r_nonce_len + AES_BLOCK_SIZE); + + WPA_PUT_LE16(r_nonce, DPP_ATTR_R_NONCE); + WPA_PUT_LE16(&r_nonce[2], auth->curve->nonce_len); + os_memcpy(r_nonce + 4, auth->r_nonce, auth->curve->nonce_len); + + if (aes_siv_encrypt(auth->k2, auth->curve->hash_len, + r_nonce, r_nonce_len, + 2, addr, len, wrapped_r_nonce) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: {R-nonce}k2", + wrapped_r_nonce, r_nonce_len + AES_BLOCK_SIZE); + } + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) { + wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); + dpp_build_attr_status(msg, DPP_STATUS_OK); + } +skip_wrapped_data: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Authentication Confirmation frame attributes", + msg); + if (status == DPP_STATUS_OK) + dpp_auth_success(auth); + + return msg; + +fail: + wpabuf_free(msg); + return NULL; +} + + +static void +dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr, + const u8 *attr_start, size_t attr_len, + const u8 *wrapped_data, u16 wrapped_data_len, + enum dpp_status_error status) +{ + const u8 *addr[2]; + size_t len[2]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + const u8 *i_nonce, *r_capab; + u16 i_nonce_len, r_capab_len; + + if (status == DPP_STATUS_NOT_COMPATIBLE) { + wpa_printf(MSG_DEBUG, + "DPP: Responder reported incompatible roles"); + } else if (status == DPP_STATUS_RESPONSE_PENDING) { + wpa_printf(MSG_DEBUG, + "DPP: Responder reported more time needed"); + } else { + wpa_printf(MSG_DEBUG, + "DPP: Responder reported failure (status %d)", + status); + dpp_auth_fail(auth, "Responder reported failure"); + return; + } + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + addr[1] = attr_start; + len[1] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + goto fail; + if (aes_siv_decrypt(auth->k1, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE, + &i_nonce_len); + if (!i_nonce || i_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, "Missing or invalid I-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len); + if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) { + dpp_auth_fail(auth, "I-nonce mismatch"); + goto fail; + } + + r_capab = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_R_CAPABILITIES, + &r_capab_len); + if (!r_capab || r_capab_len < 1) { + dpp_auth_fail(auth, "Missing or invalid R-capabilities"); + goto fail; + } + auth->r_capab = r_capab[0]; + wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab); + if (status == DPP_STATUS_NOT_COMPATIBLE) { + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE + "r-capab=0x%02x", auth->r_capab); + } else if (status == DPP_STATUS_RESPONSE_PENDING) { + u8 role = auth->r_capab & DPP_CAPAB_ROLE_MASK; + + if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) || + (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) { + wpa_msg(auth->msg_ctx, MSG_INFO, + DPP_EVENT_FAIL "Unexpected role in R-capabilities 0x%02x", + role); + } else { + wpa_printf(MSG_DEBUG, + "DPP: Continue waiting for full DPP Authentication Response"); + wpa_msg(auth->msg_ctx, MSG_INFO, + DPP_EVENT_RESPONSE_PENDING "%s", + auth->tmp_own_bi ? auth->tmp_own_bi->uri : ""); + } + } +fail: + bin_clear_free(unwrapped, unwrapped_len); +} + + +struct wpabuf * +dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr, + const u8 *attr_start, size_t attr_len) +{ + EVP_PKEY *pr; + EVP_PKEY_CTX *ctx = NULL; + size_t secret_len; + const u8 *addr[2]; + size_t len[2]; + u8 *unwrapped = NULL, *unwrapped2 = NULL; + size_t unwrapped_len = 0, unwrapped2_len = 0; + const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto, + *r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth; + u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len, + r_proto_len, r_nonce_len, i_nonce_len, r_capab_len, + wrapped2_len, r_auth_len; + u8 r_auth2[DPP_MAX_HASH_LEN]; + u8 role; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at Authentication Response"); + return NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (!auth->initiator || !auth->peer_bi) { + dpp_auth_fail(auth, "Unexpected Authentication Response"); + return NULL; + } + + auth->waiting_auth_resp = 0; + + wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + dpp_auth_fail(auth, + "Missing or invalid required Wrapped Data attribute"); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", + wrapped_data, wrapped_data_len); + + attr_len = wrapped_data - 4 - attr_start; + + r_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_R_BOOTSTRAP_KEY_HASH, + &r_bootstrap_len); + if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { + dpp_auth_fail(auth, + "Missing or invalid required Responder Bootstrapping Key Hash attribute"); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash", + r_bootstrap, r_bootstrap_len); + if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) { + dpp_auth_fail(auth, + "Unexpected Responder Bootstrapping Key Hash value"); + wpa_hexdump(MSG_DEBUG, + "DPP: Expected Responder Bootstrapping Key Hash", + auth->peer_bi->pubkey_hash, SHA256_MAC_LEN); + return NULL; + } + + i_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_I_BOOTSTRAP_KEY_HASH, + &i_bootstrap_len); + if (i_bootstrap) { + if (i_bootstrap_len != SHA256_MAC_LEN) { + dpp_auth_fail(auth, + "Invalid Initiator Bootstrapping Key Hash attribute"); + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, + "DPP: Initiator Bootstrapping Key Hash", + i_bootstrap, i_bootstrap_len); + if (!auth->own_bi || + os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) { + dpp_auth_fail(auth, + "Initiator Bootstrapping Key Hash attribute did not match"); + return NULL; + } + } else if (auth->own_bi && auth->own_bi->type == DPP_BOOTSTRAP_PKEX) { + /* PKEX bootstrapping mandates use of mutual authentication */ + dpp_auth_fail(auth, + "Missing Initiator Bootstrapping Key Hash attribute"); + return NULL; + } + + status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS, + &status_len); + if (!status || status_len < 1) { + dpp_auth_fail(auth, + "Missing or invalid required DPP Status attribute"); + return NULL; + } + wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); + auth->auth_resp_status = status[0]; + if (status[0] != DPP_STATUS_OK) { + dpp_auth_resp_rx_status(auth, hdr, attr_start, + attr_len, wrapped_data, + wrapped_data_len, status[0]); + return NULL; + } + + if (!i_bootstrap && auth->own_bi) { + wpa_printf(MSG_DEBUG, + "DPP: Responder decided not to use mutual authentication"); + auth->own_bi = NULL; + } + + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_DIRECTION "mutual=%d", + auth->own_bi != NULL); + + r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY, + &r_proto_len); + if (!r_proto) { + dpp_auth_fail(auth, + "Missing required Responder Protocol Key attribute"); + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key", + r_proto, r_proto_len); + + /* N = pI * PR */ + pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len); + if (!pr) { + dpp_auth_fail(auth, "Invalid Responder Protocol Key"); + return NULL; + } + dpp_debug_print_key("Peer (Responder) Protocol Key", pr); + + ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pr) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || + secret_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + dpp_auth_fail(auth, "Failed to derive ECDH shared secret"); + goto fail; + } + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + auth->peer_protocol_key = pr; + pr = NULL; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", + auth->Nx, auth->secret_len); + auth->Nx_len = auth->secret_len; + + if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2, + auth->curve->hash_len) < 0) + goto fail; + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + addr[1] = attr_start; + len[1] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + goto fail; + if (aes_siv_decrypt(auth->k2, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE, + &r_nonce_len); + if (!r_nonce || r_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len); + os_memcpy(auth->r_nonce, r_nonce, r_nonce_len); + + i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE, + &i_nonce_len); + if (!i_nonce || i_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, "Missing or invalid I-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len); + if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) { + dpp_auth_fail(auth, "I-nonce mismatch"); + goto fail; + } + + if (auth->own_bi) { + /* Mutual authentication */ + if (dpp_auth_derive_l_initiator(auth) < 0) + goto fail; + } + + r_capab = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_R_CAPABILITIES, + &r_capab_len); + if (!r_capab || r_capab_len < 1) { + dpp_auth_fail(auth, "Missing or invalid R-capabilities"); + goto fail; + } + auth->r_capab = r_capab[0]; + wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab); + role = auth->r_capab & DPP_CAPAB_ROLE_MASK; + if ((auth->allowed_roles == + (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) && + (role == DPP_CAPAB_CONFIGURATOR || role == DPP_CAPAB_ENROLLEE)) { + /* Peer selected its role, so move from "either role" to the + * role that is compatible with peer's selection. */ + auth->configurator = role == DPP_CAPAB_ENROLLEE; + wpa_printf(MSG_DEBUG, "DPP: Acting as %s", + auth->configurator ? "Configurator" : "Enrollee"); + } else if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) || + (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) { + wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection"); + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Unexpected role in R-capabilities 0x%02x", + role); + if (role != DPP_CAPAB_ENROLLEE && + role != DPP_CAPAB_CONFIGURATOR) + goto fail; + bin_clear_free(unwrapped, unwrapped_len); + auth->remove_on_tx_status = 1; + return dpp_auth_build_conf(auth, DPP_STATUS_NOT_COMPATIBLE); + } + + wrapped2 = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_WRAPPED_DATA, &wrapped2_len); + if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) { + dpp_auth_fail(auth, + "Missing or invalid Secondary Wrapped Data"); + goto fail; + } + + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped2, wrapped2_len); + + if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0) + goto fail; + + unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE; + unwrapped2 = os_malloc(unwrapped2_len); + if (!unwrapped2) + goto fail; + if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, + wrapped2, wrapped2_len, + 0, NULL, NULL, unwrapped2) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped2, unwrapped2_len); + + if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) { + dpp_auth_fail(auth, + "Invalid attribute in secondary unwrapped data"); + goto fail; + } + + r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG, + &r_auth_len); + if (!r_auth || r_auth_len != auth->curve->hash_len) { + dpp_auth_fail(auth, + "Missing or invalid Responder Authenticating Tag"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag", + r_auth, r_auth_len); + /* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */ + if (dpp_gen_r_auth(auth, r_auth2) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag", + r_auth2, r_auth_len); + if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) { + dpp_auth_fail(auth, "Mismatching Responder Authenticating Tag"); + bin_clear_free(unwrapped, unwrapped_len); + bin_clear_free(unwrapped2, unwrapped2_len); + auth->remove_on_tx_status = 1; + return dpp_auth_build_conf(auth, DPP_STATUS_AUTH_FAILURE); + } + + bin_clear_free(unwrapped, unwrapped_len); + bin_clear_free(unwrapped2, unwrapped2_len); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF) { + wpa_printf(MSG_INFO, + "DPP: TESTING - Authentication Response in place of Confirm"); + if (dpp_auth_build_resp_ok(auth) < 0) + return NULL; + return wpabuf_dup(auth->resp_msg); + } +#endif /* CONFIG_TESTING_OPTIONS */ + + return dpp_auth_build_conf(auth, DPP_STATUS_OK); + +fail: + bin_clear_free(unwrapped, unwrapped_len); + bin_clear_free(unwrapped2, unwrapped2_len); + EVP_PKEY_free(pr); + EVP_PKEY_CTX_free(ctx); + return NULL; +} + + +static int dpp_auth_conf_rx_failure(struct dpp_authentication *auth, + const u8 *hdr, + const u8 *attr_start, size_t attr_len, + const u8 *wrapped_data, + u16 wrapped_data_len, + enum dpp_status_error status) +{ + const u8 *addr[2]; + size_t len[2]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + const u8 *r_nonce; + u16 r_nonce_len; + + /* Authentication Confirm failure cases are expected to include + * {R-nonce}k2 in the Wrapped Data attribute. */ + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + addr[1] = attr_start; + len[1] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) { + dpp_auth_fail(auth, "Authentication failed"); + goto fail; + } + if (aes_siv_decrypt(auth->k2, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE, + &r_nonce_len); + if (!r_nonce || r_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce"); + goto fail; + } + if (os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) { + wpa_hexdump(MSG_DEBUG, "DPP: Received R-nonce", + r_nonce, r_nonce_len); + wpa_hexdump(MSG_DEBUG, "DPP: Expected R-nonce", + auth->r_nonce, r_nonce_len); + dpp_auth_fail(auth, "R-nonce mismatch"); + goto fail; + } + + if (status == DPP_STATUS_NOT_COMPATIBLE) + dpp_auth_fail(auth, "Peer reported incompatible R-capab role"); + else if (status == DPP_STATUS_AUTH_FAILURE) + dpp_auth_fail(auth, "Peer reported authentication failure)"); + +fail: + bin_clear_free(unwrapped, unwrapped_len); + return -1; +} + + +int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr, + const u8 *attr_start, size_t attr_len) +{ + const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth; + u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len, + i_auth_len; + const u8 *addr[2]; + size_t len[2]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + u8 i_auth2[DPP_MAX_HASH_LEN]; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at Authentication Confirm"); + return -1; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (auth->initiator || !auth->own_bi) { + dpp_auth_fail(auth, "Unexpected Authentication Confirm"); + return -1; + } + + auth->waiting_auth_conf = 0; + + wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + dpp_auth_fail(auth, + "Missing or invalid required Wrapped Data attribute"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", + wrapped_data, wrapped_data_len); + + attr_len = wrapped_data - 4 - attr_start; + + r_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_R_BOOTSTRAP_KEY_HASH, + &r_bootstrap_len); + if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { + dpp_auth_fail(auth, + "Missing or invalid required Responder Bootstrapping Key Hash attribute"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash", + r_bootstrap, r_bootstrap_len); + if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) { + wpa_hexdump(MSG_DEBUG, + "DPP: Expected Responder Bootstrapping Key Hash", + auth->peer_bi->pubkey_hash, SHA256_MAC_LEN); + dpp_auth_fail(auth, + "Responder Bootstrapping Key Hash mismatch"); + return -1; + } + + i_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_I_BOOTSTRAP_KEY_HASH, + &i_bootstrap_len); + if (i_bootstrap) { + if (i_bootstrap_len != SHA256_MAC_LEN) { + dpp_auth_fail(auth, + "Invalid Initiator Bootstrapping Key Hash attribute"); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, + "DPP: Initiator Bootstrapping Key Hash", + i_bootstrap, i_bootstrap_len); + if (!auth->peer_bi || + os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) { + dpp_auth_fail(auth, + "Initiator Bootstrapping Key Hash mismatch"); + return -1; + } + } else if (auth->peer_bi) { + /* Mutual authentication and peer did not include its + * Bootstrapping Key Hash attribute. */ + dpp_auth_fail(auth, + "Missing Initiator Bootstrapping Key Hash attribute"); + return -1; + } + + status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS, + &status_len); + if (!status || status_len < 1) { + dpp_auth_fail(auth, + "Missing or invalid required DPP Status attribute"); + return -1; + } + wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); + if (status[0] == DPP_STATUS_NOT_COMPATIBLE || + status[0] == DPP_STATUS_AUTH_FAILURE) + return dpp_auth_conf_rx_failure(auth, hdr, attr_start, + attr_len, wrapped_data, + wrapped_data_len, status[0]); + + if (status[0] != DPP_STATUS_OK) { + dpp_auth_fail(auth, "Authentication failed"); + return -1; + } + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + addr[1] = attr_start; + len[1] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + return -1; + if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG, + &i_auth_len); + if (!i_auth || i_auth_len != auth->curve->hash_len) { + dpp_auth_fail(auth, + "Missing or invalid Initiator Authenticating Tag"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag", + i_auth, i_auth_len); + /* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */ + if (dpp_gen_i_auth(auth, i_auth2) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag", + i_auth2, i_auth_len); + if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) { + dpp_auth_fail(auth, "Mismatching Initiator Authenticating Tag"); + goto fail; + } + + bin_clear_free(unwrapped, unwrapped_len); + dpp_auth_success(auth); + return 0; +fail: + bin_clear_free(unwrapped, unwrapped_len); + return -1; +} + + +void dpp_configuration_free(struct dpp_configuration *conf) +{ + if (!conf) + return; + str_clear_free(conf->passphrase); + os_free(conf->group_id); + bin_clear_free(conf, sizeof(*conf)); +} + + +void dpp_auth_deinit(struct dpp_authentication *auth) +{ + if (!auth) + return; + dpp_configuration_free(auth->conf_ap); + dpp_configuration_free(auth->conf_sta); + EVP_PKEY_free(auth->own_protocol_key); + EVP_PKEY_free(auth->peer_protocol_key); + wpabuf_free(auth->req_msg); + wpabuf_free(auth->resp_msg); + wpabuf_free(auth->conf_req); + os_free(auth->connector); + wpabuf_free(auth->net_access_key); + wpabuf_free(auth->c_sign_key); + dpp_bootstrap_info_free(auth->tmp_own_bi); +#ifdef CONFIG_TESTING_OPTIONS + os_free(auth->config_obj_override); + os_free(auth->discovery_override); + os_free(auth->groups_override); +#endif /* CONFIG_TESTING_OPTIONS */ + bin_clear_free(auth, sizeof(*auth)); +} + + +static struct wpabuf * +dpp_build_conf_start(struct dpp_authentication *auth, + struct dpp_configuration *conf, size_t tailroom) +{ + struct wpabuf *buf; + char ssid[6 * sizeof(conf->ssid) + 1]; + +#ifdef CONFIG_TESTING_OPTIONS + if (auth->discovery_override) + tailroom += os_strlen(auth->discovery_override); +#endif /* CONFIG_TESTING_OPTIONS */ + + buf = wpabuf_alloc(200 + tailroom); + if (!buf) + return NULL; + wpabuf_put_str(buf, "{\"wi-fi_tech\":\"infra\",\"discovery\":"); +#ifdef CONFIG_TESTING_OPTIONS + if (auth->discovery_override) { + wpa_printf(MSG_DEBUG, "DPP: TESTING - discovery override: '%s'", + auth->discovery_override); + wpabuf_put_str(buf, auth->discovery_override); + wpabuf_put_u8(buf, ','); + return buf; + } +#endif /* CONFIG_TESTING_OPTIONS */ + wpabuf_put_str(buf, "{\"ssid\":\""); + json_escape_string(ssid, sizeof(ssid), + (const char *) conf->ssid, conf->ssid_len); + wpabuf_put_str(buf, ssid); + wpabuf_put_str(buf, "\"},"); + + return buf; +} + + +static int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key, + const char *kid, const struct dpp_curve_params *curve) +{ + struct wpabuf *pub; + const u8 *pos; + char *x = NULL, *y = NULL; + int ret = -1; + + pub = dpp_get_pubkey_point(key, 0); + if (!pub) + goto fail; + pos = wpabuf_head(pub); + x = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0); + pos += curve->prime_len; + y = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0); + if (!x || !y) + goto fail; + + wpabuf_put_str(buf, "\""); + wpabuf_put_str(buf, name); + wpabuf_put_str(buf, "\":{\"kty\":\"EC\",\"crv\":\""); + wpabuf_put_str(buf, curve->jwk_crv); + wpabuf_put_str(buf, "\",\"x\":\""); + wpabuf_put_str(buf, x); + wpabuf_put_str(buf, "\",\"y\":\""); + wpabuf_put_str(buf, y); + if (kid) { + wpabuf_put_str(buf, "\",\"kid\":\""); + wpabuf_put_str(buf, kid); + } + wpabuf_put_str(buf, "\"}"); + ret = 0; +fail: + wpabuf_free(pub); + os_free(x); + os_free(y); + return ret; +} + + +static struct wpabuf * +dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap, + struct dpp_configuration *conf) +{ + struct wpabuf *buf = NULL; + char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL; + size_t tailroom; + const struct dpp_curve_params *curve; + char jws_prot_hdr[100]; + size_t signed1_len, signed2_len, signed3_len; + struct wpabuf *dppcon = NULL; + unsigned char *signature = NULL; + const unsigned char *p; + size_t signature_len; + EVP_MD_CTX *md_ctx = NULL; + ECDSA_SIG *sig = NULL; + char *dot = "."; + const EVP_MD *sign_md; + const BIGNUM *r, *s; + size_t extra_len = 1000; + + if (!auth->conf) { + wpa_printf(MSG_INFO, + "DPP: No configurator specified - cannot generate DPP config object"); + goto fail; + } + curve = auth->conf->curve; + if (curve->hash_len == SHA256_MAC_LEN) { + sign_md = EVP_sha256(); + } else if (curve->hash_len == SHA384_MAC_LEN) { + sign_md = EVP_sha384(); + } else if (curve->hash_len == SHA512_MAC_LEN) { + sign_md = EVP_sha512(); + } else { + wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm"); + goto fail; + } + +#ifdef CONFIG_TESTING_OPTIONS + if (auth->groups_override) + extra_len += os_strlen(auth->groups_override); +#endif /* CONFIG_TESTING_OPTIONS */ + + if (conf->group_id) + extra_len += os_strlen(conf->group_id); + + /* Connector (JSON dppCon object) */ + dppcon = wpabuf_alloc(extra_len + 2 * auth->curve->prime_len * 4 / 3); + if (!dppcon) + goto fail; +#ifdef CONFIG_TESTING_OPTIONS + if (auth->groups_override) { + wpabuf_put_u8(dppcon, '{'); + if (auth->groups_override) { + wpa_printf(MSG_DEBUG, + "DPP: TESTING - groups override: '%s'", + auth->groups_override); + wpabuf_put_str(dppcon, "\"groups\":"); + wpabuf_put_str(dppcon, auth->groups_override); + wpabuf_put_u8(dppcon, ','); + } + goto skip_groups; + } +#endif /* CONFIG_TESTING_OPTIONS */ + wpabuf_printf(dppcon, "{\"groups\":[{\"groupId\":\"%s\",", + conf->group_id ? conf->group_id : "*"); + wpabuf_printf(dppcon, "\"netRole\":\"%s\"}],", ap ? "ap" : "sta"); +#ifdef CONFIG_TESTING_OPTIONS +skip_groups: +#endif /* CONFIG_TESTING_OPTIONS */ + if (dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL, + auth->curve) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK"); + goto fail; + } + if (conf->netaccesskey_expiry) { + struct os_tm tm; + + if (os_gmtime(conf->netaccesskey_expiry, &tm) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to generate expiry string"); + goto fail; + } + wpabuf_printf(dppcon, + ",\"expiry\":\"%04u-%02u-%02uT%02u:%02u:%02uZ\"", + tm.year, tm.month, tm.day, + tm.hour, tm.min, tm.sec); + } + wpabuf_put_u8(dppcon, '}'); + wpa_printf(MSG_DEBUG, "DPP: dppCon: %s", + (const char *) wpabuf_head(dppcon)); + + os_snprintf(jws_prot_hdr, sizeof(jws_prot_hdr), + "{\"typ\":\"dppCon\",\"kid\":\"%s\",\"alg\":\"%s\"}", + auth->conf->kid, curve->jws_alg); + signed1 = (char *) base64_url_encode((unsigned char *) jws_prot_hdr, + os_strlen(jws_prot_hdr), + &signed1_len, 0); + signed2 = (char *) base64_url_encode(wpabuf_head(dppcon), + wpabuf_len(dppcon), + &signed2_len, 0); + if (!signed1 || !signed2) + goto fail; + + md_ctx = EVP_MD_CTX_create(); + if (!md_ctx) + goto fail; + + ERR_clear_error(); + if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL, + auth->conf->csign) != 1) { + wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 || + EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 || + EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) { + wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) { + wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + signature = os_malloc(signature_len); + if (!signature) + goto fail; + if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) { + wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)", + signature, signature_len); + /* Convert to raw coordinates r,s */ + p = signature; + sig = d2i_ECDSA_SIG(NULL, &p, signature_len); + if (!sig) + goto fail; + ECDSA_SIG_get0(sig, &r, &s); + if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 || + dpp_bn2bin_pad(s, signature + curve->prime_len, + curve->prime_len) < 0) + goto fail; + signature_len = 2 * curve->prime_len; + wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)", + signature, signature_len); + signed3 = (char *) base64_url_encode(signature, signature_len, + &signed3_len, 0); + if (!signed3) + goto fail; + + tailroom = 1000; + tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid); + tailroom += signed1_len + signed2_len + signed3_len; + buf = dpp_build_conf_start(auth, conf, tailroom); + if (!buf) + goto fail; + + wpabuf_put_str(buf, "\"cred\":{\"akm\":\"dpp\",\"signedConnector\":\""); + wpabuf_put_str(buf, signed1); + wpabuf_put_u8(buf, '.'); + wpabuf_put_str(buf, signed2); + wpabuf_put_u8(buf, '.'); + wpabuf_put_str(buf, signed3); + wpabuf_put_str(buf, "\","); + if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid, + curve) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK"); + goto fail; + } + + wpabuf_put_str(buf, "}}"); + + wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object", + wpabuf_head(buf), wpabuf_len(buf)); + +out: + EVP_MD_CTX_destroy(md_ctx); + ECDSA_SIG_free(sig); + os_free(signed1); + os_free(signed2); + os_free(signed3); + os_free(signature); + wpabuf_free(dppcon); + return buf; +fail: + wpa_printf(MSG_DEBUG, "DPP: Failed to build configuration object"); + wpabuf_free(buf); + buf = NULL; + goto out; +} + + +static struct wpabuf * +dpp_build_conf_obj_legacy(struct dpp_authentication *auth, int ap, + struct dpp_configuration *conf) +{ + struct wpabuf *buf; + + buf = dpp_build_conf_start(auth, conf, 1000); + if (!buf) + return NULL; + + wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(conf->akm)); + if (conf->passphrase) { + char pass[63 * 6 + 1]; + + if (os_strlen(conf->passphrase) > 63) { + wpabuf_free(buf); + return NULL; + } + + json_escape_string(pass, sizeof(pass), conf->passphrase, + os_strlen(conf->passphrase)); + wpabuf_put_str(buf, "\"pass\":\""); + wpabuf_put_str(buf, pass); + wpabuf_put_str(buf, "\""); + } else { + char psk[2 * sizeof(conf->psk) + 1]; + + wpa_snprintf_hex(psk, sizeof(psk), + conf->psk, sizeof(conf->psk)); + wpabuf_put_str(buf, "\"psk_hex\":\""); + wpabuf_put_str(buf, psk); + wpabuf_put_str(buf, "\""); + } + wpabuf_put_str(buf, "}}"); + + wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)", + wpabuf_head(buf), wpabuf_len(buf)); + + return buf; +} + + +static struct wpabuf * +dpp_build_conf_obj(struct dpp_authentication *auth, int ap) +{ + struct dpp_configuration *conf; + +#ifdef CONFIG_TESTING_OPTIONS + if (auth->config_obj_override) { + wpa_printf(MSG_DEBUG, "DPP: Testing - Config Object override"); + return wpabuf_alloc_copy(auth->config_obj_override, + os_strlen(auth->config_obj_override)); + } +#endif /* CONFIG_TESTING_OPTIONS */ + + conf = ap ? auth->conf_ap : auth->conf_sta; + if (!conf) { + wpa_printf(MSG_DEBUG, + "DPP: No configuration available for Enrollee(%s) - reject configuration request", + ap ? "ap" : "sta"); + return NULL; + } + + if (conf->akm == DPP_AKM_DPP) + return dpp_build_conf_obj_dpp(auth, ap, conf); + return dpp_build_conf_obj_legacy(auth, ap, conf); +} + + +static struct wpabuf * +dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce, + u16 e_nonce_len, int ap) +{ + struct wpabuf *conf; + size_t clear_len, attr_len; + struct wpabuf *clear = NULL, *msg = NULL; + u8 *wrapped; + const u8 *addr[1]; + size_t len[1]; + enum dpp_status_error status; + + conf = dpp_build_conf_obj(auth, ap); + if (conf) { + wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON", + wpabuf_head(conf), wpabuf_len(conf)); + } + status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE; + + /* { E-nonce, configurationObject}ke */ + clear_len = 4 + e_nonce_len; + if (conf) + clear_len += 4 + wpabuf_len(conf); + clear = wpabuf_alloc(clear_len); + attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) + attr_len += 5; +#endif /* CONFIG_TESTING_OPTIONS */ + msg = wpabuf_alloc(attr_len); + if (!clear || !msg) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce"); + goto skip_e_nonce; + } + if (dpp_test == DPP_TEST_E_NONCE_MISMATCH_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - E-nonce mismatch"); + wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); + wpabuf_put_le16(clear, e_nonce_len); + wpabuf_put_data(clear, e_nonce, e_nonce_len - 1); + wpabuf_put_u8(clear, e_nonce[e_nonce_len - 1] ^ 0x01); + goto skip_e_nonce; + } + if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); + goto skip_wrapped_data; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* E-nonce */ + wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); + wpabuf_put_le16(clear, e_nonce_len); + wpabuf_put_data(clear, e_nonce, e_nonce_len); + +#ifdef CONFIG_TESTING_OPTIONS +skip_e_nonce: + if (dpp_test == DPP_TEST_NO_CONFIG_OBJ_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - Config Object"); + goto skip_config_obj; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (conf) { + wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ); + wpabuf_put_le16(clear, wpabuf_len(conf)); + wpabuf_put_buf(clear, conf); + } + +#ifdef CONFIG_TESTING_OPTIONS +skip_config_obj: + if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - Status"); + goto skip_status; + } + if (dpp_test == DPP_TEST_INVALID_STATUS_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); + status = 255; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* DPP Status */ + dpp_build_attr_status(msg, status); + +#ifdef CONFIG_TESTING_OPTIONS +skip_status: +#endif /* CONFIG_TESTING_OPTIONS */ + + addr[0] = wpabuf_head(msg); + len[0] = wpabuf_len(msg); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]); + + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + + wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); + if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, + wpabuf_head(clear), wpabuf_len(clear), + 1, addr, len, wrapped) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); + dpp_build_attr_status(msg, DPP_STATUS_OK); + } +skip_wrapped_data: +#endif /* CONFIG_TESTING_OPTIONS */ + + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Configuration Response attributes", msg); +out: + wpabuf_free(conf); + wpabuf_free(clear); + + return msg; +fail: + wpabuf_free(msg); + msg = NULL; + goto out; +} + + +struct wpabuf * +dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, + size_t attr_len) +{ + const u8 *wrapped_data, *e_nonce, *config_attr; + u16 wrapped_data_len, e_nonce_len, config_attr_len; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + struct wpabuf *resp = NULL; + struct json_token *root = NULL, *token; + int ap; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at Config Request"); + return NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (dpp_check_attrs(attr_start, attr_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in config request"); + return NULL; + } + + wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + dpp_auth_fail(auth, + "Missing or invalid required Wrapped Data attribute"); + return NULL; + } + + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + return NULL; + if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 0, NULL, NULL, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + e_nonce = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_ENROLLEE_NONCE, + &e_nonce_len); + if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, + "Missing or invalid Enrollee Nonce attribute"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); + + config_attr = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_CONFIG_ATTR_OBJ, + &config_attr_len); + if (!config_attr) { + dpp_auth_fail(auth, + "Missing or invalid Config Attributes attribute"); + goto fail; + } + wpa_hexdump_ascii(MSG_DEBUG, "DPP: Config Attributes", + config_attr, config_attr_len); + + root = json_parse((const char *) config_attr, config_attr_len); + if (!root) { + dpp_auth_fail(auth, "Could not parse Config Attributes"); + goto fail; + } + + token = json_get_member(root, "name"); + if (!token || token->type != JSON_STRING) { + dpp_auth_fail(auth, "No Config Attributes - name"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: Enrollee name = '%s'", token->string); + + token = json_get_member(root, "wi-fi_tech"); + if (!token || token->type != JSON_STRING) { + dpp_auth_fail(auth, "No Config Attributes - wi-fi_tech"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: wi-fi_tech = '%s'", token->string); + if (os_strcmp(token->string, "infra") != 0) { + wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech '%s'", + token->string); + dpp_auth_fail(auth, "Unsupported wi-fi_tech"); + goto fail; + } + + token = json_get_member(root, "netRole"); + if (!token || token->type != JSON_STRING) { + dpp_auth_fail(auth, "No Config Attributes - netRole"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: netRole = '%s'", token->string); + if (os_strcmp(token->string, "sta") == 0) { + ap = 0; + } else if (os_strcmp(token->string, "ap") == 0) { + ap = 1; + } else { + wpa_printf(MSG_DEBUG, "DPP: Unsupported netRole '%s'", + token->string); + dpp_auth_fail(auth, "Unsupported netRole"); + goto fail; + } + + resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, ap); + +fail: + json_free(root); + os_free(unwrapped); + return resp; +} + + +static struct wpabuf * +dpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve, + const u8 *prot_hdr, u16 prot_hdr_len, + const EVP_MD **ret_md) +{ + struct json_token *root, *token; + struct wpabuf *kid = NULL; + + root = json_parse((const char *) prot_hdr, prot_hdr_len); + if (!root) { + wpa_printf(MSG_DEBUG, + "DPP: JSON parsing failed for JWS Protected Header"); + goto fail; + } + + if (root->type != JSON_OBJECT) { + wpa_printf(MSG_DEBUG, + "DPP: JWS Protected Header root is not an object"); + goto fail; + } + + token = json_get_member(root, "typ"); + if (!token || token->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, "DPP: No typ string value found"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s", + token->string); + if (os_strcmp(token->string, "dppCon") != 0) { + wpa_printf(MSG_DEBUG, + "DPP: Unsupported JWS Protected Header typ=%s", + token->string); + goto fail; + } + + token = json_get_member(root, "alg"); + if (!token || token->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, "DPP: No alg string value found"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s", + token->string); + if (os_strcmp(token->string, curve->jws_alg) != 0) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)", + token->string, curve->jws_alg); + goto fail; + } + if (os_strcmp(token->string, "ES256") == 0 || + os_strcmp(token->string, "BS256") == 0) + *ret_md = EVP_sha256(); + else if (os_strcmp(token->string, "ES384") == 0 || + os_strcmp(token->string, "BS384") == 0) + *ret_md = EVP_sha384(); + else if (os_strcmp(token->string, "ES512") == 0 || + os_strcmp(token->string, "BS512") == 0) + *ret_md = EVP_sha512(); + else + *ret_md = NULL; + if (!*ret_md) { + wpa_printf(MSG_DEBUG, + "DPP: Unsupported JWS Protected Header alg=%s", + token->string); + goto fail; + } + + kid = json_get_member_base64url(root, "kid"); + if (!kid) { + wpa_printf(MSG_DEBUG, "DPP: No kid string value found"); + goto fail; + } + wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)", + kid); + +fail: + json_free(root); + return kid; +} + + +static int dpp_parse_cred_legacy(struct dpp_authentication *auth, + struct json_token *cred) +{ + struct json_token *pass, *psk_hex; + + wpa_printf(MSG_DEBUG, "DPP: Legacy akm=psk credential"); + + pass = json_get_member(cred, "pass"); + psk_hex = json_get_member(cred, "psk_hex"); + + if (pass && pass->type == JSON_STRING) { + size_t len = os_strlen(pass->string); + + wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Legacy passphrase", + pass->string, len); + if (len < 8 || len > 63) + return -1; + os_strlcpy(auth->passphrase, pass->string, + sizeof(auth->passphrase)); + } else if (psk_hex && psk_hex->type == JSON_STRING) { + if (auth->akm == DPP_AKM_SAE) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected psk_hex with akm=sae"); + return -1; + } + if (os_strlen(psk_hex->string) != PMK_LEN * 2 || + hexstr2bin(psk_hex->string, auth->psk, PMK_LEN) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Invalid psk_hex encoding"); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "DPP: Legacy PSK", + auth->psk, PMK_LEN); + auth->psk_set = 1; + } else { + wpa_printf(MSG_DEBUG, "DPP: No pass or psk_hex strings found"); + return -1; + } + + if ((auth->akm == DPP_AKM_SAE || auth->akm == DPP_AKM_PSK_SAE) && + !auth->passphrase[0]) { + wpa_printf(MSG_DEBUG, "DPP: No pass for sae found"); + return -1; + } + + return 0; +} + + +static EVP_PKEY * dpp_parse_jwk(struct json_token *jwk, + const struct dpp_curve_params **key_curve) +{ + struct json_token *token; + const struct dpp_curve_params *curve; + struct wpabuf *x = NULL, *y = NULL; + EC_GROUP *group; + EVP_PKEY *pkey = NULL; + + token = json_get_member(jwk, "kty"); + if (!token || token->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, "DPP: No kty in JWK"); + goto fail; + } + if (os_strcmp(token->string, "EC") != 0) { + wpa_printf(MSG_DEBUG, "DPP: Unexpected JWK kty '%s'", + token->string); + goto fail; + } + + token = json_get_member(jwk, "crv"); + if (!token || token->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, "DPP: No crv in JWK"); + goto fail; + } + curve = dpp_get_curve_jwk_crv(token->string); + if (!curve) { + wpa_printf(MSG_DEBUG, "DPP: Unsupported JWK crv '%s'", + token->string); + goto fail; + } + + x = json_get_member_base64url(jwk, "x"); + if (!x) { + wpa_printf(MSG_DEBUG, "DPP: No x in JWK"); + goto fail; + } + wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK x", x); + if (wpabuf_len(x) != curve->prime_len) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected JWK x length %u (expected %u for curve %s)", + (unsigned int) wpabuf_len(x), + (unsigned int) curve->prime_len, curve->name); + goto fail; + } + + y = json_get_member_base64url(jwk, "y"); + if (!y) { + wpa_printf(MSG_DEBUG, "DPP: No y in JWK"); + goto fail; + } + wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK y", y); + if (wpabuf_len(y) != curve->prime_len) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected JWK y length %u (expected %u for curve %s)", + (unsigned int) wpabuf_len(y), + (unsigned int) curve->prime_len, curve->name); + goto fail; + } + + group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name)); + if (!group) { + wpa_printf(MSG_DEBUG, "DPP: Could not prepare group for JWK"); + goto fail; + } + + pkey = dpp_set_pubkey_point_group(group, wpabuf_head(x), wpabuf_head(y), + wpabuf_len(x)); + *key_curve = curve; + +fail: + wpabuf_free(x); + wpabuf_free(y); + + return pkey; +} + + +int dpp_key_expired(const char *timestamp, os_time_t *expiry) +{ + struct os_time now; + unsigned int year, month, day, hour, min, sec; + os_time_t utime; + const char *pos; + + /* ISO 8601 date and time: + * <date>T<time> + * YYYY-MM-DDTHH:MM:SSZ + * YYYY-MM-DDTHH:MM:SS+03:00 + */ + if (os_strlen(timestamp) < 19) { + wpa_printf(MSG_DEBUG, + "DPP: Too short timestamp - assume expired key"); + return 1; + } + if (sscanf(timestamp, "%04u-%02u-%02uT%02u:%02u:%02u", + &year, &month, &day, &hour, &min, &sec) != 6) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to parse expiration day - assume expired key"); + return 1; + } + + if (os_mktime(year, month, day, hour, min, sec, &utime) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Invalid date/time information - assume expired key"); + return 1; + } + + pos = timestamp + 19; + if (*pos == 'Z' || *pos == '\0') { + /* In UTC - no need to adjust */ + } else if (*pos == '-' || *pos == '+') { + int items; + + /* Adjust local time to UTC */ + items = sscanf(pos + 1, "%02u:%02u", &hour, &min); + if (items < 1) { + wpa_printf(MSG_DEBUG, + "DPP: Invalid time zone designator (%s) - assume expired key", + pos); + return 1; + } + if (*pos == '-') + utime += 3600 * hour; + if (*pos == '+') + utime -= 3600 * hour; + if (items > 1) { + if (*pos == '-') + utime += 60 * min; + if (*pos == '+') + utime -= 60 * min; + } + } else { + wpa_printf(MSG_DEBUG, + "DPP: Invalid time zone designator (%s) - assume expired key", + pos); + return 1; + } + if (expiry) + *expiry = utime; + + if (os_get_time(&now) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Cannot get current time - assume expired key"); + return 1; + } + + if (now.sec > utime) { + wpa_printf(MSG_DEBUG, "DPP: Key has expired (%lu < %lu)", + utime, now.sec); + return 1; + } + + return 0; +} + + +static int dpp_parse_connector(struct dpp_authentication *auth, + const unsigned char *payload, + u16 payload_len) +{ + struct json_token *root, *groups, *netkey, *token; + int ret = -1; + EVP_PKEY *key = NULL; + const struct dpp_curve_params *curve; + unsigned int rules = 0; + + root = json_parse((const char *) payload, payload_len); + if (!root) { + wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed"); + goto fail; + } + + groups = json_get_member(root, "groups"); + if (!groups || groups->type != JSON_ARRAY) { + wpa_printf(MSG_DEBUG, "DPP: No groups array found"); + goto skip_groups; + } + for (token = groups->child; token; token = token->sibling) { + struct json_token *id, *role; + + id = json_get_member(token, "groupId"); + if (!id || id->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, "DPP: Missing groupId string"); + goto fail; + } + + role = json_get_member(token, "netRole"); + if (!role || role->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, "DPP: Missing netRole string"); + goto fail; + } + wpa_printf(MSG_DEBUG, + "DPP: connector group: groupId='%s' netRole='%s'", + id->string, role->string); + rules++; + } +skip_groups: + + if (!rules) { + wpa_printf(MSG_DEBUG, + "DPP: Connector includes no groups"); + goto fail; + } + + token = json_get_member(root, "expiry"); + if (!token || token->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, + "DPP: No expiry string found - connector does not expire"); + } else { + wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string); + if (dpp_key_expired(token->string, + &auth->net_access_key_expiry)) { + wpa_printf(MSG_DEBUG, + "DPP: Connector (netAccessKey) has expired"); + goto fail; + } + } + + netkey = json_get_member(root, "netAccessKey"); + if (!netkey || netkey->type != JSON_OBJECT) { + wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found"); + goto fail; + } + + key = dpp_parse_jwk(netkey, &curve); + if (!key) + goto fail; + dpp_debug_print_key("DPP: Received netAccessKey", key); + + if (EVP_PKEY_cmp(key, auth->own_protocol_key) != 1) { + wpa_printf(MSG_DEBUG, + "DPP: netAccessKey in connector does not match own protocol key"); +#ifdef CONFIG_TESTING_OPTIONS + if (auth->ignore_netaccesskey_mismatch) { + wpa_printf(MSG_DEBUG, + "DPP: TESTING - skip netAccessKey mismatch"); + } else { + goto fail; + } +#else /* CONFIG_TESTING_OPTIONS */ + goto fail; +#endif /* CONFIG_TESTING_OPTIONS */ + } + + ret = 0; +fail: + EVP_PKEY_free(key); + json_free(root); + return ret; +} + + +static int dpp_check_pubkey_match(EVP_PKEY *pub, struct wpabuf *r_hash) +{ + struct wpabuf *uncomp; + int res; + u8 hash[SHA256_MAC_LEN]; + const u8 *addr[1]; + size_t len[1]; + + if (wpabuf_len(r_hash) != SHA256_MAC_LEN) + return -1; + uncomp = dpp_get_pubkey_point(pub, 1); + if (!uncomp) + return -1; + addr[0] = wpabuf_head(uncomp); + len[0] = wpabuf_len(uncomp); + wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed public key", + addr[0], len[0]); + res = sha256_vector(1, addr, len, hash); + wpabuf_free(uncomp); + if (res < 0) + return -1; + if (os_memcmp(hash, wpabuf_head(r_hash), SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "DPP: Received hash value does not match calculated public key hash value"); + wpa_hexdump(MSG_DEBUG, "DPP: Calculated hash", + hash, SHA256_MAC_LEN); + return -1; + } + return 0; +} + + +static void dpp_copy_csign(struct dpp_authentication *auth, EVP_PKEY *csign) +{ + unsigned char *der = NULL; + int der_len; + + der_len = i2d_PUBKEY(csign, &der); + if (der_len <= 0) + return; + wpabuf_free(auth->c_sign_key); + auth->c_sign_key = wpabuf_alloc_copy(der, der_len); + OPENSSL_free(der); +} + + +static void dpp_copy_netaccesskey(struct dpp_authentication *auth) +{ + unsigned char *der = NULL; + int der_len; + EC_KEY *eckey; + + eckey = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key); + if (!eckey) + return; + + der_len = i2d_ECPrivateKey(eckey, &der); + if (der_len <= 0) { + EC_KEY_free(eckey); + return; + } + wpabuf_free(auth->net_access_key); + auth->net_access_key = wpabuf_alloc_copy(der, der_len); + OPENSSL_free(der); + EC_KEY_free(eckey); +} + + +struct dpp_signed_connector_info { + unsigned char *payload; + size_t payload_len; +}; + +static enum dpp_status_error +dpp_process_signed_connector(struct dpp_signed_connector_info *info, + EVP_PKEY *csign_pub, const char *connector) +{ + enum dpp_status_error ret = 255; + const char *pos, *end, *signed_start, *signed_end; + struct wpabuf *kid = NULL; + unsigned char *prot_hdr = NULL, *signature = NULL; + size_t prot_hdr_len = 0, signature_len = 0; + const EVP_MD *sign_md = NULL; + unsigned char *der = NULL; + int der_len; + int res; + EVP_MD_CTX *md_ctx = NULL; + ECDSA_SIG *sig = NULL; + BIGNUM *r = NULL, *s = NULL; + const struct dpp_curve_params *curve; + EC_KEY *eckey; + const EC_GROUP *group; + int nid; + + eckey = EVP_PKEY_get1_EC_KEY(csign_pub); + if (!eckey) + goto fail; + group = EC_KEY_get0_group(eckey); + if (!group) + goto fail; + nid = EC_GROUP_get_curve_name(group); + curve = dpp_get_curve_nid(nid); + if (!curve) + goto fail; + wpa_printf(MSG_DEBUG, "DPP: C-sign-key group: %s", curve->jwk_crv); + os_memset(info, 0, sizeof(*info)); + + signed_start = pos = connector; + end = os_strchr(pos, '.'); + if (!end) { + wpa_printf(MSG_DEBUG, "DPP: Missing dot(1) in signedConnector"); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + prot_hdr = base64_url_decode((const unsigned char *) pos, + end - pos, &prot_hdr_len); + if (!prot_hdr) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to base64url decode signedConnector JWS Protected Header"); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + wpa_hexdump_ascii(MSG_DEBUG, + "DPP: signedConnector - JWS Protected Header", + prot_hdr, prot_hdr_len); + kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &sign_md); + if (!kid) { + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + if (wpabuf_len(kid) != SHA256_MAC_LEN) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected signedConnector JWS Protected Header kid length: %u (expected %u)", + (unsigned int) wpabuf_len(kid), SHA256_MAC_LEN); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + + pos = end + 1; + end = os_strchr(pos, '.'); + if (!end) { + wpa_printf(MSG_DEBUG, + "DPP: Missing dot(2) in signedConnector"); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + signed_end = end - 1; + info->payload = base64_url_decode((const unsigned char *) pos, + end - pos, &info->payload_len); + if (!info->payload) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to base64url decode signedConnector JWS Payload"); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + wpa_hexdump_ascii(MSG_DEBUG, + "DPP: signedConnector - JWS Payload", + info->payload, info->payload_len); + pos = end + 1; + signature = base64_url_decode((const unsigned char *) pos, + os_strlen(pos), &signature_len); + if (!signature) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to base64url decode signedConnector signature"); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: signedConnector - signature", + signature, signature_len); + + if (dpp_check_pubkey_match(csign_pub, kid) < 0) { + ret = DPP_STATUS_NO_MATCH; + goto fail; + } + + if (signature_len & 0x01) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected signedConnector signature length (%d)", + (int) signature_len); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + + /* JWS Signature encodes the signature (r,s) as two octet strings. Need + * to convert that to DER encoded ECDSA_SIG for OpenSSL EVP routines. */ + r = BN_bin2bn(signature, signature_len / 2, NULL); + s = BN_bin2bn(signature + signature_len / 2, signature_len / 2, NULL); + sig = ECDSA_SIG_new(); + if (!r || !s || !sig || ECDSA_SIG_set0(sig, r, s) != 1) + goto fail; + r = NULL; + s = NULL; + + der_len = i2d_ECDSA_SIG(sig, &der); + if (der_len <= 0) { + wpa_printf(MSG_DEBUG, "DPP: Could not DER encode signature"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: DER encoded signature", der, der_len); + md_ctx = EVP_MD_CTX_create(); + if (!md_ctx) + goto fail; + + ERR_clear_error(); + if (EVP_DigestVerifyInit(md_ctx, NULL, sign_md, NULL, csign_pub) != 1) { + wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyInit failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + if (EVP_DigestVerifyUpdate(md_ctx, signed_start, + signed_end - signed_start + 1) != 1) { + wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyUpdate failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + res = EVP_DigestVerifyFinal(md_ctx, der, der_len); + if (res != 1) { + wpa_printf(MSG_DEBUG, + "DPP: EVP_DigestVerifyFinal failed (res=%d): %s", + res, ERR_error_string(ERR_get_error(), NULL)); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + + ret = DPP_STATUS_OK; +fail: + EC_KEY_free(eckey); + EVP_MD_CTX_destroy(md_ctx); + os_free(prot_hdr); + wpabuf_free(kid); + os_free(signature); + ECDSA_SIG_free(sig); + BN_free(r); + BN_free(s); + OPENSSL_free(der); + return ret; +} + + +static int dpp_parse_cred_dpp(struct dpp_authentication *auth, + struct json_token *cred) +{ + struct dpp_signed_connector_info info; + struct json_token *token, *csign; + int ret = -1; + EVP_PKEY *csign_pub = NULL; + const struct dpp_curve_params *key_curve = NULL; + const char *signed_connector; + + os_memset(&info, 0, sizeof(info)); + + wpa_printf(MSG_DEBUG, "DPP: Connector credential"); + + csign = json_get_member(cred, "csign"); + if (!csign || csign->type != JSON_OBJECT) { + wpa_printf(MSG_DEBUG, "DPP: No csign JWK in JSON"); + goto fail; + } + + csign_pub = dpp_parse_jwk(csign, &key_curve); + if (!csign_pub) { + wpa_printf(MSG_DEBUG, "DPP: Failed to parse csign JWK"); + goto fail; + } + dpp_debug_print_key("DPP: Received C-sign-key", csign_pub); + + token = json_get_member(cred, "signedConnector"); + if (!token || token->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, "DPP: No signedConnector string found"); + goto fail; + } + wpa_hexdump_ascii(MSG_DEBUG, "DPP: signedConnector", + token->string, os_strlen(token->string)); + signed_connector = token->string; + + if (os_strchr(signed_connector, '"') || + os_strchr(signed_connector, '\n')) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected character in signedConnector"); + goto fail; + } + + if (dpp_process_signed_connector(&info, csign_pub, + signed_connector) != DPP_STATUS_OK) + goto fail; + + if (dpp_parse_connector(auth, info.payload, info.payload_len) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Failed to parse connector"); + goto fail; + } + + os_free(auth->connector); + auth->connector = os_strdup(signed_connector); + + dpp_copy_csign(auth, csign_pub); + dpp_copy_netaccesskey(auth); + + ret = 0; +fail: + EVP_PKEY_free(csign_pub); + os_free(info.payload); + return ret; +} + + +const char * dpp_akm_str(enum dpp_akm akm) +{ + switch (akm) { + case DPP_AKM_DPP: + return "dpp"; + case DPP_AKM_PSK: + return "psk"; + case DPP_AKM_SAE: + return "sae"; + case DPP_AKM_PSK_SAE: + return "psk+sae"; + default: + return "??"; + } +} + + +static enum dpp_akm dpp_akm_from_str(const char *akm) +{ + if (os_strcmp(akm, "psk") == 0) + return DPP_AKM_PSK; + if (os_strcmp(akm, "sae") == 0) + return DPP_AKM_SAE; + if (os_strcmp(akm, "psk+sae") == 0) + return DPP_AKM_PSK_SAE; + if (os_strcmp(akm, "dpp") == 0) + return DPP_AKM_DPP; + return DPP_AKM_UNKNOWN; +} + + +static int dpp_parse_conf_obj(struct dpp_authentication *auth, + const u8 *conf_obj, u16 conf_obj_len) +{ + int ret = -1; + struct json_token *root, *token, *discovery, *cred; + + root = json_parse((const char *) conf_obj, conf_obj_len); + if (!root) + return -1; + if (root->type != JSON_OBJECT) { + dpp_auth_fail(auth, "JSON root is not an object"); + goto fail; + } + + token = json_get_member(root, "wi-fi_tech"); + if (!token || token->type != JSON_STRING) { + dpp_auth_fail(auth, "No wi-fi_tech string value found"); + goto fail; + } + if (os_strcmp(token->string, "infra") != 0) { + wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech value: '%s'", + token->string); + dpp_auth_fail(auth, "Unsupported wi-fi_tech value"); + goto fail; + } + + discovery = json_get_member(root, "discovery"); + if (!discovery || discovery->type != JSON_OBJECT) { + dpp_auth_fail(auth, "No discovery object in JSON"); + goto fail; + } + + token = json_get_member(discovery, "ssid"); + if (!token || token->type != JSON_STRING) { + dpp_auth_fail(auth, "No discovery::ssid string value found"); + goto fail; + } + wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid", + token->string, os_strlen(token->string)); + if (os_strlen(token->string) > SSID_MAX_LEN) { + dpp_auth_fail(auth, "Too long discovery::ssid string value"); + goto fail; + } + auth->ssid_len = os_strlen(token->string); + os_memcpy(auth->ssid, token->string, auth->ssid_len); + + cred = json_get_member(root, "cred"); + if (!cred || cred->type != JSON_OBJECT) { + dpp_auth_fail(auth, "No cred object in JSON"); + goto fail; + } + + token = json_get_member(cred, "akm"); + if (!token || token->type != JSON_STRING) { + dpp_auth_fail(auth, "No cred::akm string value found"); + goto fail; + } + auth->akm = dpp_akm_from_str(token->string); + + if (auth->akm == DPP_AKM_PSK || auth->akm == DPP_AKM_SAE || + auth->akm == DPP_AKM_PSK_SAE) { + if (dpp_parse_cred_legacy(auth, cred) < 0) + goto fail; + } else if (auth->akm == DPP_AKM_DPP) { + if (dpp_parse_cred_dpp(auth, cred) < 0) + goto fail; + } else { + wpa_printf(MSG_DEBUG, "DPP: Unsupported akm: %s", + token->string); + dpp_auth_fail(auth, "Unsupported akm"); + goto fail; + } + + wpa_printf(MSG_DEBUG, "DPP: JSON parsing completed successfully"); + ret = 0; +fail: + json_free(root); + return ret; +} + + +int dpp_conf_resp_rx(struct dpp_authentication *auth, + const struct wpabuf *resp) +{ + const u8 *wrapped_data, *e_nonce, *status, *conf_obj; + u16 wrapped_data_len, e_nonce_len, status_len, conf_obj_len; + const u8 *addr[1]; + size_t len[1]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + int ret = -1; + + if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) { + dpp_auth_fail(auth, "Invalid attribute in config response"); + return -1; + } + + wrapped_data = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp), + DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + dpp_auth_fail(auth, + "Missing or invalid required Wrapped Data attribute"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + return -1; + + addr[0] = wpabuf_head(resp); + len[0] = wrapped_data - 4 - (const u8 *) wpabuf_head(resp); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]); + + if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 1, addr, len, unwrapped) < 0) { + dpp_auth_fail(auth, "AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); + goto fail; + } + + e_nonce = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_ENROLLEE_NONCE, + &e_nonce_len); + if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { + dpp_auth_fail(auth, + "Missing or invalid Enrollee Nonce attribute"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); + if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) { + dpp_auth_fail(auth, "Enrollee Nonce mismatch"); + goto fail; + } + + status = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp), + DPP_ATTR_STATUS, &status_len); + if (!status || status_len < 1) { + dpp_auth_fail(auth, + "Missing or invalid required DPP Status attribute"); + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); + if (status[0] != DPP_STATUS_OK) { + dpp_auth_fail(auth, "Configurator rejected configuration"); + goto fail; + } + + conf_obj = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_CONFIG_OBJ, &conf_obj_len); + if (!conf_obj) { + dpp_auth_fail(auth, + "Missing required Configuration Object attribute"); + goto fail; + } + wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON", + conf_obj, conf_obj_len); + if (dpp_parse_conf_obj(auth, conf_obj, conf_obj_len) < 0) + goto fail; + + ret = 0; + +fail: + os_free(unwrapped); + return ret; +} + + +void dpp_configurator_free(struct dpp_configurator *conf) +{ + if (!conf) + return; + EVP_PKEY_free(conf->csign); + os_free(conf->kid); + os_free(conf); +} + + +int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf, + size_t buflen) +{ + EC_KEY *eckey; + int key_len, ret = -1; + unsigned char *key = NULL; + + if (!conf->csign) + return -1; + + eckey = EVP_PKEY_get1_EC_KEY(conf->csign); + if (!eckey) + return -1; + + key_len = i2d_ECPrivateKey(eckey, &key); + if (key_len > 0) + ret = wpa_snprintf_hex(buf, buflen, key, key_len); + + EC_KEY_free(eckey); + OPENSSL_free(key); + return ret; +} + + +struct dpp_configurator * +dpp_keygen_configurator(const char *curve, const u8 *privkey, + size_t privkey_len) +{ + struct dpp_configurator *conf; + struct wpabuf *csign_pub = NULL; + u8 kid_hash[SHA256_MAC_LEN]; + const u8 *addr[1]; + size_t len[1]; + + conf = os_zalloc(sizeof(*conf)); + if (!conf) + return NULL; + + if (!curve) { + conf->curve = &dpp_curves[0]; + } else { + conf->curve = dpp_get_curve_name(curve); + if (!conf->curve) { + wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", + curve); + os_free(conf); + return NULL; + } + } + if (privkey) + conf->csign = dpp_set_keypair(&conf->curve, privkey, + privkey_len); + else + conf->csign = dpp_gen_keypair(conf->curve); + if (!conf->csign) + goto fail; + conf->own = 1; + + csign_pub = dpp_get_pubkey_point(conf->csign, 1); + if (!csign_pub) { + wpa_printf(MSG_INFO, "DPP: Failed to extract C-sign-key"); + goto fail; + } + + /* kid = SHA256(ANSI X9.63 uncompressed C-sign-key) */ + addr[0] = wpabuf_head(csign_pub); + len[0] = wpabuf_len(csign_pub); + if (sha256_vector(1, addr, len, kid_hash) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to derive kid for C-sign-key"); + goto fail; + } + + conf->kid = (char *) base64_url_encode(kid_hash, sizeof(kid_hash), + NULL, 0); + if (!conf->kid) + goto fail; +out: + wpabuf_free(csign_pub); + return conf; +fail: + dpp_configurator_free(conf); + conf = NULL; + goto out; +} + + +int dpp_configurator_own_config(struct dpp_authentication *auth, + const char *curve, int ap) +{ + struct wpabuf *conf_obj; + int ret = -1; + + if (!auth->conf) { + wpa_printf(MSG_DEBUG, "DPP: No configurator specified"); + return -1; + } + + if (!curve) { + auth->curve = &dpp_curves[0]; + } else { + auth->curve = dpp_get_curve_name(curve); + if (!auth->curve) { + wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", + curve); + return -1; + } + } + wpa_printf(MSG_DEBUG, + "DPP: Building own configuration/connector with curve %s", + auth->curve->name); + + auth->own_protocol_key = dpp_gen_keypair(auth->curve); + if (!auth->own_protocol_key) + return -1; + dpp_copy_netaccesskey(auth); + auth->peer_protocol_key = auth->own_protocol_key; + dpp_copy_csign(auth, auth->conf->csign); + + conf_obj = dpp_build_conf_obj(auth, ap); + if (!conf_obj) + goto fail; + ret = dpp_parse_conf_obj(auth, wpabuf_head(conf_obj), + wpabuf_len(conf_obj)); +fail: + wpabuf_free(conf_obj); + auth->peer_protocol_key = NULL; + return ret; +} + + +static int dpp_compatible_netrole(const char *role1, const char *role2) +{ + return (os_strcmp(role1, "sta") == 0 && os_strcmp(role2, "ap") == 0) || + (os_strcmp(role1, "ap") == 0 && os_strcmp(role2, "sta") == 0); +} + + +static int dpp_connector_compatible_group(struct json_token *root, + const char *group_id, + const char *net_role) +{ + struct json_token *groups, *token; + + groups = json_get_member(root, "groups"); + if (!groups || groups->type != JSON_ARRAY) + return 0; + + for (token = groups->child; token; token = token->sibling) { + struct json_token *id, *role; + + id = json_get_member(token, "groupId"); + if (!id || id->type != JSON_STRING) + continue; + + role = json_get_member(token, "netRole"); + if (!role || role->type != JSON_STRING) + continue; + + if (os_strcmp(id->string, "*") != 0 && + os_strcmp(group_id, "*") != 0 && + os_strcmp(id->string, group_id) != 0) + continue; + + if (dpp_compatible_netrole(role->string, net_role)) + return 1; + } + + return 0; +} + + +static int dpp_connector_match_groups(struct json_token *own_root, + struct json_token *peer_root) +{ + struct json_token *groups, *token; + + groups = json_get_member(peer_root, "groups"); + if (!groups || groups->type != JSON_ARRAY) { + wpa_printf(MSG_DEBUG, "DPP: No peer groups array found"); + return 0; + } + + for (token = groups->child; token; token = token->sibling) { + struct json_token *id, *role; + + id = json_get_member(token, "groupId"); + if (!id || id->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, + "DPP: Missing peer groupId string"); + continue; + } + + role = json_get_member(token, "netRole"); + if (!role || role->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, + "DPP: Missing peer groups::netRole string"); + continue; + } + wpa_printf(MSG_DEBUG, + "DPP: peer connector group: groupId='%s' netRole='%s'", + id->string, role->string); + if (dpp_connector_compatible_group(own_root, id->string, + role->string)) { + wpa_printf(MSG_DEBUG, + "DPP: Compatible group/netRole in own connector"); + return 1; + } + } + + return 0; +} + + +static int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk, + unsigned int hash_len) +{ + u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN]; + const char *info = "DPP PMK"; + int res; + + /* PMK = HKDF(<>, "DPP PMK", N.x) */ + + /* HKDF-Extract(<>, N.x) */ + os_memset(salt, 0, hash_len); + if (dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)", + prk, hash_len); + + /* HKDF-Expand(PRK, info, L) */ + res = dpp_hkdf_expand(hash_len, prk, hash_len, info, pmk, hash_len); + os_memset(prk, 0, hash_len); + if (res < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "DPP: PMK = HKDF-Expand(PRK, info, L)", + pmk, hash_len); + return 0; +} + + +static int dpp_derive_pmkid(const struct dpp_curve_params *curve, + EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid) +{ + struct wpabuf *nkx, *pkx; + int ret = -1, res; + const u8 *addr[2]; + size_t len[2]; + u8 hash[SHA256_MAC_LEN]; + + /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */ + nkx = dpp_get_pubkey_point(own_key, 0); + pkx = dpp_get_pubkey_point(peer_key, 0); + if (!nkx || !pkx) + goto fail; + addr[0] = wpabuf_head(nkx); + len[0] = wpabuf_len(nkx) / 2; + addr[1] = wpabuf_head(pkx); + len[1] = wpabuf_len(pkx) / 2; + if (len[0] != len[1]) + goto fail; + if (os_memcmp(addr[0], addr[1], len[0]) > 0) { + addr[0] = wpabuf_head(pkx); + addr[1] = wpabuf_head(nkx); + } + wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 1", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 2", addr[1], len[1]); + res = sha256_vector(2, addr, len, hash); + if (res < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash output", hash, SHA256_MAC_LEN); + os_memcpy(pmkid, hash, PMKID_LEN); + wpa_hexdump(MSG_DEBUG, "DPP: PMKID", pmkid, PMKID_LEN); + ret = 0; +fail: + wpabuf_free(nkx); + wpabuf_free(pkx); + return ret; +} + + +enum dpp_status_error +dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector, + const u8 *net_access_key, size_t net_access_key_len, + const u8 *csign_key, size_t csign_key_len, + const u8 *peer_connector, size_t peer_connector_len, + os_time_t *expiry) +{ + struct json_token *root = NULL, *netkey, *token; + struct json_token *own_root = NULL; + enum dpp_status_error ret = 255, res; + EVP_PKEY *own_key = NULL, *peer_key = NULL; + struct wpabuf *own_key_pub = NULL; + const struct dpp_curve_params *curve, *own_curve; + struct dpp_signed_connector_info info; + const unsigned char *p; + EVP_PKEY *csign = NULL; + char *signed_connector = NULL; + const char *pos, *end; + unsigned char *own_conn = NULL; + size_t own_conn_len; + EVP_PKEY_CTX *ctx = NULL; + size_t Nx_len; + u8 Nx[DPP_MAX_SHARED_SECRET_LEN]; + + os_memset(intro, 0, sizeof(*intro)); + os_memset(&info, 0, sizeof(info)); + if (expiry) + *expiry = 0; + + p = csign_key; + csign = d2i_PUBKEY(NULL, &p, csign_key_len); + if (!csign) { + wpa_printf(MSG_ERROR, + "DPP: Failed to parse local C-sign-key information"); + goto fail; + } + + own_key = dpp_set_keypair(&own_curve, net_access_key, + net_access_key_len); + if (!own_key) { + wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey"); + goto fail; + } + + pos = os_strchr(own_connector, '.'); + if (!pos) { + wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the first dot (.)"); + goto fail; + } + pos++; + end = os_strchr(pos, '.'); + if (!end) { + wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)"); + goto fail; + } + own_conn = base64_url_decode((const unsigned char *) pos, + end - pos, &own_conn_len); + if (!own_conn) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to base64url decode own signedConnector JWS Payload"); + goto fail; + } + + own_root = json_parse((const char *) own_conn, own_conn_len); + if (!own_root) { + wpa_printf(MSG_DEBUG, "DPP: Failed to parse local connector"); + goto fail; + } + + wpa_hexdump_ascii(MSG_DEBUG, "DPP: Peer signedConnector", + peer_connector, peer_connector_len); + signed_connector = os_malloc(peer_connector_len + 1); + if (!signed_connector) + goto fail; + os_memcpy(signed_connector, peer_connector, peer_connector_len); + signed_connector[peer_connector_len] = '\0'; + + res = dpp_process_signed_connector(&info, csign, signed_connector); + if (res != DPP_STATUS_OK) { + ret = res; + goto fail; + } + + root = json_parse((const char *) info.payload, info.payload_len); + if (!root) { + wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed"); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + + if (!dpp_connector_match_groups(own_root, root)) { + wpa_printf(MSG_DEBUG, + "DPP: Peer connector does not include compatible group netrole with own connector"); + ret = DPP_STATUS_NO_MATCH; + goto fail; + } + + token = json_get_member(root, "expiry"); + if (!token || token->type != JSON_STRING) { + wpa_printf(MSG_DEBUG, + "DPP: No expiry string found - connector does not expire"); + } else { + wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string); + if (dpp_key_expired(token->string, expiry)) { + wpa_printf(MSG_DEBUG, + "DPP: Connector (netAccessKey) has expired"); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + } + + netkey = json_get_member(root, "netAccessKey"); + if (!netkey || netkey->type != JSON_OBJECT) { + wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found"); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + + peer_key = dpp_parse_jwk(netkey, &curve); + if (!peer_key) { + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + dpp_debug_print_key("DPP: Received netAccessKey", peer_key); + + if (own_curve != curve) { + wpa_printf(MSG_DEBUG, + "DPP: Mismatching netAccessKey curves (%s != %s)", + own_curve->name, curve->name); + ret = DPP_STATUS_INVALID_CONNECTOR; + goto fail; + } + + /* ECDH: N = nk * PK */ + ctx = EVP_PKEY_CTX_new(own_key, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, peer_key) != 1 || + EVP_PKEY_derive(ctx, NULL, &Nx_len) != 1 || + Nx_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, Nx, &Nx_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", + Nx, Nx_len); + + /* PMK = HKDF(<>, "DPP PMK", N.x) */ + if (dpp_derive_pmk(Nx, Nx_len, intro->pmk, curve->hash_len) < 0) { + wpa_printf(MSG_ERROR, "DPP: Failed to derive PMK"); + goto fail; + } + intro->pmk_len = curve->hash_len; + + /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */ + if (dpp_derive_pmkid(curve, own_key, peer_key, intro->pmkid) < 0) { + wpa_printf(MSG_ERROR, "DPP: Failed to derive PMKID"); + goto fail; + } + + ret = DPP_STATUS_OK; +fail: + if (ret != DPP_STATUS_OK) + os_memset(intro, 0, sizeof(*intro)); + os_memset(Nx, 0, sizeof(Nx)); + EVP_PKEY_CTX_free(ctx); + os_free(own_conn); + os_free(signed_connector); + os_free(info.payload); + EVP_PKEY_free(own_key); + wpabuf_free(own_key_pub); + EVP_PKEY_free(peer_key); + EVP_PKEY_free(csign); + json_free(root); + json_free(own_root); + return ret; +} + + +static EVP_PKEY * dpp_pkex_get_role_elem(const struct dpp_curve_params *curve, + int init) +{ + EC_GROUP *group; + size_t len = curve->prime_len; + const u8 *x, *y; + + switch (curve->ike_group) { + case 19: + x = init ? pkex_init_x_p256 : pkex_resp_x_p256; + y = init ? pkex_init_y_p256 : pkex_resp_y_p256; + break; + case 20: + x = init ? pkex_init_x_p384 : pkex_resp_x_p384; + y = init ? pkex_init_y_p384 : pkex_resp_y_p384; + break; + case 21: + x = init ? pkex_init_x_p521 : pkex_resp_x_p521; + y = init ? pkex_init_y_p521 : pkex_resp_y_p521; + break; + case 28: + x = init ? pkex_init_x_bp_p256r1 : pkex_resp_x_bp_p256r1; + y = init ? pkex_init_y_bp_p256r1 : pkex_resp_y_bp_p256r1; + break; + case 29: + x = init ? pkex_init_x_bp_p384r1 : pkex_resp_x_bp_p384r1; + y = init ? pkex_init_y_bp_p384r1 : pkex_resp_y_bp_p384r1; + break; + case 30: + x = init ? pkex_init_x_bp_p512r1 : pkex_resp_x_bp_p512r1; + y = init ? pkex_init_y_bp_p512r1 : pkex_resp_y_bp_p512r1; + break; + default: + return NULL; + } + + group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name)); + if (!group) + return NULL; + return dpp_set_pubkey_point_group(group, x, y, len); +} + + +static EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve, + const u8 *mac_init, const char *code, + const char *identifier, BN_CTX *bnctx, + const EC_GROUP **ret_group) +{ + u8 hash[DPP_MAX_HASH_LEN]; + const u8 *addr[3]; + size_t len[3]; + unsigned int num_elem = 0; + EC_POINT *Qi = NULL; + EVP_PKEY *Pi = NULL; + EC_KEY *Pi_ec = NULL; + const EC_POINT *Pi_point; + BIGNUM *hash_bn = NULL; + const EC_GROUP *group = NULL; + EC_GROUP *group2 = NULL; + + /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */ + + wpa_printf(MSG_DEBUG, "DPP: MAC-Initiator: " MACSTR, MAC2STR(mac_init)); + addr[num_elem] = mac_init; + len[num_elem] = ETH_ALEN; + num_elem++; + if (identifier) { + wpa_printf(MSG_DEBUG, "DPP: code identifier: %s", + identifier); + addr[num_elem] = (const u8 *) identifier; + len[num_elem] = os_strlen(identifier); + num_elem++; + } + wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code)); + addr[num_elem] = (const u8 *) code; + len[num_elem] = os_strlen(code); + num_elem++; + if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0) + goto fail; + wpa_hexdump_key(MSG_DEBUG, + "DPP: H(MAC-Initiator | [identifier |] code)", + hash, curve->hash_len); + Pi = dpp_pkex_get_role_elem(curve, 1); + if (!Pi) + goto fail; + dpp_debug_print_key("DPP: Pi", Pi); + Pi_ec = EVP_PKEY_get1_EC_KEY(Pi); + if (!Pi_ec) + goto fail; + Pi_point = EC_KEY_get0_public_key(Pi_ec); + + group = EC_KEY_get0_group(Pi_ec); + if (!group) + goto fail; + group2 = EC_GROUP_dup(group); + if (!group2) + goto fail; + Qi = EC_POINT_new(group2); + if (!Qi) { + EC_GROUP_free(group2); + goto fail; + } + hash_bn = BN_bin2bn(hash, curve->hash_len, NULL); + if (!hash_bn || + EC_POINT_mul(group2, Qi, NULL, Pi_point, hash_bn, bnctx) != 1) + goto fail; + if (EC_POINT_is_at_infinity(group, Qi)) { + wpa_printf(MSG_INFO, "DPP: Qi is the point-at-infinity"); + goto fail; + } + dpp_debug_print_point("DPP: Qi", group, Qi); +out: + EC_KEY_free(Pi_ec); + EVP_PKEY_free(Pi); + BN_clear_free(hash_bn); + if (ret_group) + *ret_group = group2; + return Qi; +fail: + EC_POINT_free(Qi); + Qi = NULL; + goto out; +} + + +static EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve, + const u8 *mac_resp, const char *code, + const char *identifier, BN_CTX *bnctx, + const EC_GROUP **ret_group) +{ + u8 hash[DPP_MAX_HASH_LEN]; + const u8 *addr[3]; + size_t len[3]; + unsigned int num_elem = 0; + EC_POINT *Qr = NULL; + EVP_PKEY *Pr = NULL; + EC_KEY *Pr_ec = NULL; + const EC_POINT *Pr_point; + BIGNUM *hash_bn = NULL; + const EC_GROUP *group = NULL; + EC_GROUP *group2 = NULL; + + /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */ + + wpa_printf(MSG_DEBUG, "DPP: MAC-Responder: " MACSTR, MAC2STR(mac_resp)); + addr[num_elem] = mac_resp; + len[num_elem] = ETH_ALEN; + num_elem++; + if (identifier) { + wpa_printf(MSG_DEBUG, "DPP: code identifier: %s", + identifier); + addr[num_elem] = (const u8 *) identifier; + len[num_elem] = os_strlen(identifier); + num_elem++; + } + wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code)); + addr[num_elem] = (const u8 *) code; + len[num_elem] = os_strlen(code); + num_elem++; + if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0) + goto fail; + wpa_hexdump_key(MSG_DEBUG, + "DPP: H(MAC-Responder | [identifier |] code)", + hash, curve->hash_len); + Pr = dpp_pkex_get_role_elem(curve, 0); + if (!Pr) + goto fail; + dpp_debug_print_key("DPP: Pr", Pr); + Pr_ec = EVP_PKEY_get1_EC_KEY(Pr); + if (!Pr_ec) + goto fail; + Pr_point = EC_KEY_get0_public_key(Pr_ec); + + group = EC_KEY_get0_group(Pr_ec); + if (!group) + goto fail; + group2 = EC_GROUP_dup(group); + if (!group2) + goto fail; + Qr = EC_POINT_new(group2); + if (!Qr) { + EC_GROUP_free(group2); + goto fail; + } + hash_bn = BN_bin2bn(hash, curve->hash_len, NULL); + if (!hash_bn || + EC_POINT_mul(group2, Qr, NULL, Pr_point, hash_bn, bnctx) != 1) + goto fail; + if (EC_POINT_is_at_infinity(group, Qr)) { + wpa_printf(MSG_INFO, "DPP: Qr is the point-at-infinity"); + goto fail; + } + dpp_debug_print_point("DPP: Qr", group, Qr); +out: + EC_KEY_free(Pr_ec); + EVP_PKEY_free(Pr); + BN_clear_free(hash_bn); + if (ret_group) + *ret_group = group2; + return Qr; +fail: + EC_POINT_free(Qr); + Qr = NULL; + goto out; +} + + +#ifdef CONFIG_TESTING_OPTIONS +static int dpp_test_gen_invalid_key(struct wpabuf *msg, + const struct dpp_curve_params *curve) +{ + BN_CTX *ctx; + BIGNUM *x, *y; + int ret = -1; + EC_GROUP *group; + EC_POINT *point; + + group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name)); + if (!group) + return -1; + + ctx = BN_CTX_new(); + point = EC_POINT_new(group); + x = BN_new(); + y = BN_new(); + if (!ctx || !point || !x || !y) + goto fail; + + if (BN_rand(x, curve->prime_len * 8, 0, 0) != 1) + goto fail; + + /* Generate a random y coordinate that results in a point that is not + * on the curve. */ + for (;;) { + if (BN_rand(y, curve->prime_len * 8, 0, 0) != 1) + goto fail; + + if (EC_POINT_set_affine_coordinates_GFp(group, point, x, y, + ctx) != 1) { +#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(OPENSSL_IS_BORINGSSL) + /* Unlike older OpenSSL versions, OpenSSL 1.1.1 and BoringSSL + * return an error from EC_POINT_set_affine_coordinates_GFp() + * when the point is not on the curve. */ + break; +#else /* >=1.1.0 or OPENSSL_IS_BORINGSSL */ + goto fail; +#endif /* >= 1.1.0 or OPENSSL_IS_BORINGSSL */ + } + + if (!EC_POINT_is_on_curve(group, point, ctx)) + break; + } + + if (dpp_bn2bin_pad(x, wpabuf_put(msg, curve->prime_len), + curve->prime_len) < 0 || + dpp_bn2bin_pad(y, wpabuf_put(msg, curve->prime_len), + curve->prime_len) < 0) + goto fail; + + ret = 0; +fail: + if (ret < 0) + wpa_printf(MSG_INFO, "DPP: Failed to generate invalid key"); + BN_free(x); + BN_free(y); + EC_POINT_free(point); + BN_CTX_free(ctx); + + return ret; +} +#endif /* CONFIG_TESTING_OPTIONS */ + + +static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex) +{ + EC_KEY *X_ec = NULL; + const EC_POINT *X_point; + BN_CTX *bnctx = NULL; + const EC_GROUP *group; + EC_POINT *Qi = NULL, *M = NULL; + struct wpabuf *M_buf = NULL; + BIGNUM *Mx = NULL, *My = NULL; + struct wpabuf *msg = NULL; + size_t attr_len; + const struct dpp_curve_params *curve = pkex->own_bi->curve; + + wpa_printf(MSG_DEBUG, "DPP: Build PKEX Exchange Request"); + + /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */ + bnctx = BN_CTX_new(); + if (!bnctx) + goto fail; + Qi = dpp_pkex_derive_Qi(curve, pkex->own_mac, pkex->code, + pkex->identifier, bnctx, &group); + if (!Qi) + goto fail; + + /* Generate a random ephemeral keypair x/X */ +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_pkex_ephemeral_key_override_len) { + const struct dpp_curve_params *tmp_curve; + + wpa_printf(MSG_INFO, + "DPP: TESTING - override ephemeral key x/X"); + pkex->x = dpp_set_keypair(&tmp_curve, + dpp_pkex_ephemeral_key_override, + dpp_pkex_ephemeral_key_override_len); + } else { + pkex->x = dpp_gen_keypair(curve); + } +#else /* CONFIG_TESTING_OPTIONS */ + pkex->x = dpp_gen_keypair(curve); +#endif /* CONFIG_TESTING_OPTIONS */ + if (!pkex->x) + goto fail; + + /* M = X + Qi */ + X_ec = EVP_PKEY_get1_EC_KEY(pkex->x); + if (!X_ec) + goto fail; + X_point = EC_KEY_get0_public_key(X_ec); + if (!X_point) + goto fail; + dpp_debug_print_point("DPP: X", group, X_point); + M = EC_POINT_new(group); + Mx = BN_new(); + My = BN_new(); + if (!M || !Mx || !My || + EC_POINT_add(group, M, X_point, Qi, bnctx) != 1 || + EC_POINT_get_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1) + goto fail; + dpp_debug_print_point("DPP: M", group, M); + + /* Initiator -> Responder: group, [identifier,] M */ + attr_len = 4 + 2; + if (pkex->identifier) + attr_len += 4 + os_strlen(pkex->identifier); + attr_len += 4 + 2 * curve->prime_len; + msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_REQ, attr_len); + if (!msg) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Finite Cyclic Group"); + goto skip_finite_cyclic_group; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* Finite Cyclic Group attribute */ + wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP); + wpabuf_put_le16(msg, 2); + wpabuf_put_le16(msg, curve->ike_group); + +#ifdef CONFIG_TESTING_OPTIONS +skip_finite_cyclic_group: +#endif /* CONFIG_TESTING_OPTIONS */ + + /* Code Identifier attribute */ + if (pkex->identifier) { + wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER); + wpabuf_put_le16(msg, os_strlen(pkex->identifier)); + wpabuf_put_str(msg, pkex->identifier); + } + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key"); + goto out; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* M in Encrypted Key attribute */ + wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY); + wpabuf_put_le16(msg, 2 * curve->prime_len); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key"); + if (dpp_test_gen_invalid_key(msg, curve) < 0) + goto fail; + goto out; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (dpp_bn2bin_pad(Mx, wpabuf_put(msg, curve->prime_len), + curve->prime_len) < 0 || + dpp_bn2bin_pad(Mx, pkex->Mx, curve->prime_len) < 0 || + dpp_bn2bin_pad(My, wpabuf_put(msg, curve->prime_len), + curve->prime_len) < 0) + goto fail; + +out: + wpabuf_free(M_buf); + EC_KEY_free(X_ec); + EC_POINT_free(M); + EC_POINT_free(Qi); + BN_clear_free(Mx); + BN_clear_free(My); + BN_CTX_free(bnctx); + return msg; +fail: + wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request"); + wpabuf_free(msg); + msg = NULL; + goto out; +} + + +static void dpp_pkex_fail(struct dpp_pkex *pkex, const char *txt) +{ + wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt); +} + + +struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi, + const u8 *own_mac, + const char *identifier, + const char *code) +{ + struct dpp_pkex *pkex; + +#ifdef CONFIG_TESTING_OPTIONS + if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) { + wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR, + MAC2STR(dpp_pkex_own_mac_override)); + own_mac = dpp_pkex_own_mac_override; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + pkex = os_zalloc(sizeof(*pkex)); + if (!pkex) + return NULL; + pkex->msg_ctx = msg_ctx; + pkex->initiator = 1; + pkex->own_bi = bi; + os_memcpy(pkex->own_mac, own_mac, ETH_ALEN); + if (identifier) { + pkex->identifier = os_strdup(identifier); + if (!pkex->identifier) + goto fail; + } + pkex->code = os_strdup(code); + if (!pkex->code) + goto fail; + pkex->exchange_req = dpp_pkex_build_exchange_req(pkex); + if (!pkex->exchange_req) + goto fail; + return pkex; +fail: + dpp_pkex_free(pkex); + return NULL; +} + + +static struct wpabuf * +dpp_pkex_build_exchange_resp(struct dpp_pkex *pkex, + enum dpp_status_error status, + const BIGNUM *Nx, const BIGNUM *Ny) +{ + struct wpabuf *msg = NULL; + size_t attr_len; + const struct dpp_curve_params *curve = pkex->own_bi->curve; + + /* Initiator -> Responder: DPP Status, [identifier,] N */ + attr_len = 4 + 1; + if (pkex->identifier) + attr_len += 4 + os_strlen(pkex->identifier); + attr_len += 4 + 2 * curve->prime_len; + msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len); + if (!msg) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); + goto skip_status; + } + + if (dpp_test == DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); + status = 255; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* DPP Status */ + dpp_build_attr_status(msg, status); + +#ifdef CONFIG_TESTING_OPTIONS +skip_status: +#endif /* CONFIG_TESTING_OPTIONS */ + + /* Code Identifier attribute */ + if (pkex->identifier) { + wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER); + wpabuf_put_le16(msg, os_strlen(pkex->identifier)); + wpabuf_put_str(msg, pkex->identifier); + } + + if (status != DPP_STATUS_OK) + goto skip_encrypted_key; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key"); + goto skip_encrypted_key; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* N in Encrypted Key attribute */ + wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY); + wpabuf_put_le16(msg, 2 * curve->prime_len); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key"); + if (dpp_test_gen_invalid_key(msg, curve) < 0) + goto fail; + goto skip_encrypted_key; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (dpp_bn2bin_pad(Nx, wpabuf_put(msg, curve->prime_len), + curve->prime_len) < 0 || + dpp_bn2bin_pad(Nx, pkex->Nx, curve->prime_len) < 0 || + dpp_bn2bin_pad(Ny, wpabuf_put(msg, curve->prime_len), + curve->prime_len) < 0) + goto fail; + +skip_encrypted_key: + if (status == DPP_STATUS_BAD_GROUP) { + /* Finite Cyclic Group attribute */ + wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP); + wpabuf_put_le16(msg, 2); + wpabuf_put_le16(msg, curve->ike_group); + } + + return msg; +fail: + wpabuf_free(msg); + return NULL; +} + + +static int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp, + const u8 *Mx, size_t Mx_len, + const u8 *Nx, size_t Nx_len, + const char *code, + const u8 *Kx, size_t Kx_len, + u8 *z, unsigned int hash_len) +{ + u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN]; + int res; + u8 *info, *pos; + size_t info_len; + + /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x) + */ + + /* HKDF-Extract(<>, IKM=K.x) */ + os_memset(salt, 0, hash_len); + if (dpp_hmac(hash_len, salt, hash_len, Kx, Kx_len, prk) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)", + prk, hash_len); + info_len = 2 * ETH_ALEN + Mx_len + Nx_len + os_strlen(code); + info = os_malloc(info_len); + if (!info) + return -1; + pos = info; + os_memcpy(pos, mac_init, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, mac_resp, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, Mx, Mx_len); + pos += Mx_len; + os_memcpy(pos, Nx, Nx_len); + pos += Nx_len; + os_memcpy(pos, code, os_strlen(code)); + + /* HKDF-Expand(PRK, info, L) */ + if (hash_len == 32) + res = hmac_sha256_kdf(prk, hash_len, NULL, info, info_len, + z, hash_len); + else if (hash_len == 48) + res = hmac_sha384_kdf(prk, hash_len, NULL, info, info_len, + z, hash_len); + else if (hash_len == 64) + res = hmac_sha512_kdf(prk, hash_len, NULL, info, info_len, + z, hash_len); + else + res = -1; + os_free(info); + os_memset(prk, 0, hash_len); + if (res < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)", + z, hash_len); + return 0; +} + + +static int dpp_pkex_identifier_match(const u8 *attr_id, u16 attr_id_len, + const char *identifier) +{ + if (!attr_id && identifier) { + wpa_printf(MSG_DEBUG, + "DPP: No PKEX code identifier received, but expected one"); + return 0; + } + + if (attr_id && !identifier) { + wpa_printf(MSG_DEBUG, + "DPP: PKEX code identifier received, but not expecting one"); + return 0; + } + + if (attr_id && identifier && + (os_strlen(identifier) != attr_id_len || + os_memcmp(identifier, attr_id, attr_id_len) != 0)) { + wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch"); + return 0; + } + + return 1; +} + + +struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx, + struct dpp_bootstrap_info *bi, + const u8 *own_mac, + const u8 *peer_mac, + const char *identifier, + const char *code, + const u8 *buf, size_t len) +{ + const u8 *attr_group, *attr_id, *attr_key; + u16 attr_group_len, attr_id_len, attr_key_len; + const struct dpp_curve_params *curve = bi->curve; + u16 ike_group; + struct dpp_pkex *pkex = NULL; + EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL; + BN_CTX *bnctx = NULL; + const EC_GROUP *group; + BIGNUM *Mx = NULL, *My = NULL; + EC_KEY *Y_ec = NULL, *X_ec = NULL;; + const EC_POINT *Y_point; + BIGNUM *Nx = NULL, *Ny = NULL; + u8 Kx[DPP_MAX_SHARED_SECRET_LEN]; + size_t Kx_len; + int res; + EVP_PKEY_CTX *ctx = NULL; + + if (bi->pkex_t >= PKEX_COUNTER_T_LIMIT) { + wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "PKEX counter t limit reached - ignore message"); + return NULL; + } + +#ifdef CONFIG_TESTING_OPTIONS + if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) { + wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR, + MAC2STR(dpp_pkex_peer_mac_override)); + peer_mac = dpp_pkex_peer_mac_override; + } + if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) { + wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR, + MAC2STR(dpp_pkex_own_mac_override)); + own_mac = dpp_pkex_own_mac_override; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + attr_id_len = 0; + attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER, + &attr_id_len); + if (!dpp_pkex_identifier_match(attr_id, attr_id_len, identifier)) + return NULL; + + attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP, + &attr_group_len); + if (!attr_group || attr_group_len != 2) { + wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Missing or invalid Finite Cyclic Group attribute"); + return NULL; + } + ike_group = WPA_GET_LE16(attr_group); + if (ike_group != curve->ike_group) { + wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Mismatching PKEX curve: peer=%u own=%u", + ike_group, curve->ike_group); + pkex = os_zalloc(sizeof(*pkex)); + if (!pkex) + goto fail; + pkex->own_bi = bi; + pkex->failed = 1; + pkex->exchange_resp = dpp_pkex_build_exchange_resp( + pkex, DPP_STATUS_BAD_GROUP, NULL, NULL); + if (!pkex->exchange_resp) + goto fail; + return pkex; + } + + /* M in Encrypted Key attribute */ + attr_key = dpp_get_attr(buf, len, DPP_ATTR_ENCRYPTED_KEY, + &attr_key_len); + if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 || + attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) { + wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Missing Encrypted Key attribute"); + return NULL; + } + + /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */ + bnctx = BN_CTX_new(); + if (!bnctx) + goto fail; + Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, bnctx, + &group); + if (!Qi) + goto fail; + + /* X' = M - Qi */ + X = EC_POINT_new(group); + M = EC_POINT_new(group); + Mx = BN_bin2bn(attr_key, attr_key_len / 2, NULL); + My = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL); + if (!X || !M || !Mx || !My || + EC_POINT_set_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1 || + EC_POINT_is_at_infinity(group, M) || + !EC_POINT_is_on_curve(group, M, bnctx) || + EC_POINT_invert(group, Qi, bnctx) != 1 || + EC_POINT_add(group, X, M, Qi, bnctx) != 1 || + EC_POINT_is_at_infinity(group, X) || + !EC_POINT_is_on_curve(group, X, bnctx)) { + wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Invalid Encrypted Key value"); + bi->pkex_t++; + goto fail; + } + dpp_debug_print_point("DPP: M", group, M); + dpp_debug_print_point("DPP: X'", group, X); + + pkex = os_zalloc(sizeof(*pkex)); + if (!pkex) + goto fail; + pkex->t = bi->pkex_t; + pkex->msg_ctx = msg_ctx; + pkex->own_bi = bi; + os_memcpy(pkex->own_mac, own_mac, ETH_ALEN); + os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN); + if (identifier) { + pkex->identifier = os_strdup(identifier); + if (!pkex->identifier) + goto fail; + } + pkex->code = os_strdup(code); + if (!pkex->code) + goto fail; + + os_memcpy(pkex->Mx, attr_key, attr_key_len / 2); + + X_ec = EC_KEY_new(); + if (!X_ec || + EC_KEY_set_group(X_ec, group) != 1 || + EC_KEY_set_public_key(X_ec, X) != 1) + goto fail; + pkex->x = EVP_PKEY_new(); + if (!pkex->x || + EVP_PKEY_set1_EC_KEY(pkex->x, X_ec) != 1) + goto fail; + + /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */ + Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, bnctx, NULL); + if (!Qr) + goto fail; + + /* Generate a random ephemeral keypair y/Y */ +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_pkex_ephemeral_key_override_len) { + const struct dpp_curve_params *tmp_curve; + + wpa_printf(MSG_INFO, + "DPP: TESTING - override ephemeral key y/Y"); + pkex->y = dpp_set_keypair(&tmp_curve, + dpp_pkex_ephemeral_key_override, + dpp_pkex_ephemeral_key_override_len); + } else { + pkex->y = dpp_gen_keypair(curve); + } +#else /* CONFIG_TESTING_OPTIONS */ + pkex->y = dpp_gen_keypair(curve); +#endif /* CONFIG_TESTING_OPTIONS */ + if (!pkex->y) + goto fail; + + /* N = Y + Qr */ + Y_ec = EVP_PKEY_get1_EC_KEY(pkex->y); + if (!Y_ec) + goto fail; + Y_point = EC_KEY_get0_public_key(Y_ec); + if (!Y_point) + goto fail; + dpp_debug_print_point("DPP: Y", group, Y_point); + N = EC_POINT_new(group); + Nx = BN_new(); + Ny = BN_new(); + if (!N || !Nx || !Ny || + EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 || + EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1) + goto fail; + dpp_debug_print_point("DPP: N", group, N); + + pkex->exchange_resp = dpp_pkex_build_exchange_resp(pkex, DPP_STATUS_OK, + Nx, Ny); + if (!pkex->exchange_resp) + goto fail; + + /* K = y * X' */ + ctx = EVP_PKEY_CTX_new(pkex->y, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 || + EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 || + Kx_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)", + Kx, Kx_len); + + /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x) + */ + res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac, + pkex->Mx, curve->prime_len, + pkex->Nx, curve->prime_len, pkex->code, + Kx, Kx_len, pkex->z, curve->hash_len); + os_memset(Kx, 0, Kx_len); + if (res < 0) + goto fail; + + pkex->exchange_done = 1; + +out: + EVP_PKEY_CTX_free(ctx); + BN_CTX_free(bnctx); + EC_POINT_free(Qi); + EC_POINT_free(Qr); + BN_free(Mx); + BN_free(My); + BN_free(Nx); + BN_free(Ny); + EC_POINT_free(M); + EC_POINT_free(N); + EC_POINT_free(X); + EC_KEY_free(X_ec); + EC_KEY_free(Y_ec); + return pkex; +fail: + wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing failed"); + dpp_pkex_free(pkex); + pkex = NULL; + goto out; +} + + +static struct wpabuf * +dpp_pkex_build_commit_reveal_req(struct dpp_pkex *pkex, + const struct wpabuf *A_pub, const u8 *u) +{ + const struct dpp_curve_params *curve = pkex->own_bi->curve; + struct wpabuf *msg = NULL; + size_t clear_len, attr_len; + struct wpabuf *clear = NULL; + u8 *wrapped; + u8 octet; + const u8 *addr[2]; + size_t len[2]; + + /* {A, u, [bootstrapping info]}z */ + clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len; + clear = wpabuf_alloc(clear_len); + attr_len = 4 + clear_len + AES_BLOCK_SIZE; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ) + attr_len += 5; +#endif /* CONFIG_TESTING_OPTIONS */ + msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ, attr_len); + if (!clear || !msg) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key"); + goto skip_bootstrap_key; + } + if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key"); + wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY); + wpabuf_put_le16(clear, 2 * curve->prime_len); + if (dpp_test_gen_invalid_key(clear, curve) < 0) + goto fail; + goto skip_bootstrap_key; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* A in Bootstrap Key attribute */ + wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY); + wpabuf_put_le16(clear, wpabuf_len(A_pub)); + wpabuf_put_buf(clear, A_pub); + +#ifdef CONFIG_TESTING_OPTIONS +skip_bootstrap_key: + if (dpp_test == DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no I-Auth tag"); + goto skip_i_auth_tag; + } + if (dpp_test == DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - I-Auth tag mismatch"); + wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG); + wpabuf_put_le16(clear, curve->hash_len); + wpabuf_put_data(clear, u, curve->hash_len - 1); + wpabuf_put_u8(clear, u[curve->hash_len - 1] ^ 0x01); + goto skip_i_auth_tag; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* u in I-Auth tag attribute */ + wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG); + wpabuf_put_le16(clear, curve->hash_len); + wpabuf_put_data(clear, u, curve->hash_len); + +#ifdef CONFIG_TESTING_OPTIONS +skip_i_auth_tag: + if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); + goto skip_wrapped_data; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + addr[0] = wpabuf_head_u8(msg) + 2; + len[0] = DPP_HDR_LEN; + octet = 0; + addr[1] = &octet; + len[1] = sizeof(octet); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + + wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); + if (aes_siv_encrypt(pkex->z, curve->hash_len, + wpabuf_head(clear), wpabuf_len(clear), + 2, addr, len, wrapped) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ) { + wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); + dpp_build_attr_status(msg, DPP_STATUS_OK); + } +skip_wrapped_data: +#endif /* CONFIG_TESTING_OPTIONS */ + +out: + wpabuf_free(clear); + return msg; + +fail: + wpabuf_free(msg); + msg = NULL; + goto out; +} + + +struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex, + const u8 *peer_mac, + const u8 *buf, size_t buflen) +{ + const u8 *attr_status, *attr_id, *attr_key, *attr_group; + u16 attr_status_len, attr_id_len, attr_key_len, attr_group_len; + const EC_GROUP *group; + BN_CTX *bnctx = NULL; + struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL; + const struct dpp_curve_params *curve = pkex->own_bi->curve; + EC_POINT *Qr = NULL, *Y = NULL, *N = NULL; + BIGNUM *Nx = NULL, *Ny = NULL; + EVP_PKEY_CTX *ctx = NULL; + EC_KEY *Y_ec = NULL; + size_t Jx_len, Kx_len; + u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN]; + const u8 *addr[4]; + size_t len[4]; + u8 u[DPP_MAX_HASH_LEN]; + int res; + + if (pkex->failed || pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator) + return NULL; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at PKEX Exchange Response"); + pkex->failed = 1; + return NULL; + } + + if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) { + wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR, + MAC2STR(dpp_pkex_peer_mac_override)); + peer_mac = dpp_pkex_peer_mac_override; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN); + + attr_status = dpp_get_attr(buf, buflen, DPP_ATTR_STATUS, + &attr_status_len); + if (!attr_status || attr_status_len != 1) { + dpp_pkex_fail(pkex, "No DPP Status attribute"); + return NULL; + } + wpa_printf(MSG_DEBUG, "DPP: Status %u", attr_status[0]); + + if (attr_status[0] == DPP_STATUS_BAD_GROUP) { + attr_group = dpp_get_attr(buf, buflen, + DPP_ATTR_FINITE_CYCLIC_GROUP, + &attr_group_len); + if (attr_group && attr_group_len == 2) { + wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL + "Peer indicated mismatching PKEX group - proposed %u", + WPA_GET_LE16(attr_group)); + return NULL; + } + } + + if (attr_status[0] != DPP_STATUS_OK) { + dpp_pkex_fail(pkex, "PKEX failed (peer indicated failure)"); + return NULL; + } + + attr_id_len = 0; + attr_id = dpp_get_attr(buf, buflen, DPP_ATTR_CODE_IDENTIFIER, + &attr_id_len); + if (!dpp_pkex_identifier_match(attr_id, attr_id_len, + pkex->identifier)) { + dpp_pkex_fail(pkex, "PKEX code identifier mismatch"); + return NULL; + } + + /* N in Encrypted Key attribute */ + attr_key = dpp_get_attr(buf, buflen, DPP_ATTR_ENCRYPTED_KEY, + &attr_key_len); + if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2) { + dpp_pkex_fail(pkex, "Missing Encrypted Key attribute"); + return NULL; + } + + /* Qr = H(MAC-Responder | [identifier |] code) * Pr */ + bnctx = BN_CTX_new(); + if (!bnctx) + goto fail; + Qr = dpp_pkex_derive_Qr(curve, pkex->peer_mac, pkex->code, + pkex->identifier, bnctx, &group); + if (!Qr) + goto fail; + + /* Y' = N - Qr */ + Y = EC_POINT_new(group); + N = EC_POINT_new(group); + Nx = BN_bin2bn(attr_key, attr_key_len / 2, NULL); + Ny = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL); + if (!Y || !N || !Nx || !Ny || + EC_POINT_set_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1 || + EC_POINT_is_at_infinity(group, N) || + !EC_POINT_is_on_curve(group, N, bnctx) || + EC_POINT_invert(group, Qr, bnctx) != 1 || + EC_POINT_add(group, Y, N, Qr, bnctx) != 1 || + EC_POINT_is_at_infinity(group, Y) || + !EC_POINT_is_on_curve(group, Y, bnctx)) { + dpp_pkex_fail(pkex, "Invalid Encrypted Key value"); + pkex->t++; + goto fail; + } + dpp_debug_print_point("DPP: N", group, N); + dpp_debug_print_point("DPP: Y'", group, Y); + + pkex->exchange_done = 1; + + /* ECDH: J = a * Y’ */ + Y_ec = EC_KEY_new(); + if (!Y_ec || + EC_KEY_set_group(Y_ec, group) != 1 || + EC_KEY_set_public_key(Y_ec, Y) != 1) + goto fail; + pkex->y = EVP_PKEY_new(); + if (!pkex->y || + EVP_PKEY_set1_EC_KEY(pkex->y, Y_ec) != 1) + goto fail; + ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 || + EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 || + Jx_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)", + Jx, Jx_len); + + /* u = HMAC(J.x, MAC-Initiator | A.x | Y’.x | X.x ) */ + A_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0); + Y_pub = dpp_get_pubkey_point(pkex->y, 0); + X_pub = dpp_get_pubkey_point(pkex->x, 0); + if (!A_pub || !Y_pub || !X_pub) + goto fail; + addr[0] = pkex->own_mac; + len[0] = ETH_ALEN; + addr[1] = wpabuf_head(A_pub); + len[1] = wpabuf_len(A_pub) / 2; + addr[2] = wpabuf_head(Y_pub); + len[2] = wpabuf_len(Y_pub) / 2; + addr[3] = wpabuf_head(X_pub); + len[3] = wpabuf_len(X_pub) / 2; + if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: u", u, curve->hash_len); + + /* K = x * Y’ */ + EVP_PKEY_CTX_free(ctx); + ctx = EVP_PKEY_CTX_new(pkex->x, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pkex->y) != 1 || + EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 || + Kx_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)", + Kx, Kx_len); + + /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x) + */ + res = dpp_pkex_derive_z(pkex->own_mac, pkex->peer_mac, + pkex->Mx, curve->prime_len, + attr_key /* N.x */, attr_key_len / 2, + pkex->code, Kx, Kx_len, + pkex->z, curve->hash_len); + os_memset(Kx, 0, Kx_len); + if (res < 0) + goto fail; + + msg = dpp_pkex_build_commit_reveal_req(pkex, A_pub, u); + if (!msg) + goto fail; + +out: + wpabuf_free(A_pub); + wpabuf_free(X_pub); + wpabuf_free(Y_pub); + EC_POINT_free(Qr); + EC_POINT_free(Y); + EC_POINT_free(N); + BN_free(Nx); + BN_free(Ny); + EC_KEY_free(Y_ec); + EVP_PKEY_CTX_free(ctx); + BN_CTX_free(bnctx); + return msg; +fail: + wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing failed"); + goto out; +} + + +static struct wpabuf * +dpp_pkex_build_commit_reveal_resp(struct dpp_pkex *pkex, + const struct wpabuf *B_pub, const u8 *v) +{ + const struct dpp_curve_params *curve = pkex->own_bi->curve; + struct wpabuf *msg = NULL; + const u8 *addr[2]; + size_t len[2]; + u8 octet; + u8 *wrapped; + struct wpabuf *clear = NULL; + size_t clear_len, attr_len; + + /* {B, v [bootstrapping info]}z */ + clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len; + clear = wpabuf_alloc(clear_len); + attr_len = 4 + clear_len + AES_BLOCK_SIZE; +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP) + attr_len += 5; +#endif /* CONFIG_TESTING_OPTIONS */ + msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP, attr_len); + if (!clear || !msg) + goto fail; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key"); + goto skip_bootstrap_key; + } + if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key"); + wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY); + wpabuf_put_le16(clear, 2 * curve->prime_len); + if (dpp_test_gen_invalid_key(clear, curve) < 0) + goto fail; + goto skip_bootstrap_key; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* B in Bootstrap Key attribute */ + wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY); + wpabuf_put_le16(clear, wpabuf_len(B_pub)); + wpabuf_put_buf(clear, B_pub); + +#ifdef CONFIG_TESTING_OPTIONS +skip_bootstrap_key: + if (dpp_test == DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth tag"); + goto skip_r_auth_tag; + } + if (dpp_test == DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - R-Auth tag mismatch"); + wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG); + wpabuf_put_le16(clear, curve->hash_len); + wpabuf_put_data(clear, v, curve->hash_len - 1); + wpabuf_put_u8(clear, v[curve->hash_len - 1] ^ 0x01); + goto skip_r_auth_tag; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + /* v in R-Auth tag attribute */ + wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG); + wpabuf_put_le16(clear, curve->hash_len); + wpabuf_put_data(clear, v, curve->hash_len); + +#ifdef CONFIG_TESTING_OPTIONS +skip_r_auth_tag: + if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); + goto skip_wrapped_data; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + addr[0] = wpabuf_head_u8(msg) + 2; + len[0] = DPP_HDR_LEN; + octet = 1; + addr[1] = &octet; + len[1] = sizeof(octet); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); + + wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); + if (aes_siv_encrypt(pkex->z, curve->hash_len, + wpabuf_head(clear), wpabuf_len(clear), + 2, addr, len, wrapped) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP) { + wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); + dpp_build_attr_status(msg, DPP_STATUS_OK); + } +skip_wrapped_data: +#endif /* CONFIG_TESTING_OPTIONS */ + +out: + wpabuf_free(clear); + return msg; + +fail: + wpabuf_free(msg); + msg = NULL; + goto out; +} + + +struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex, + const u8 *hdr, + const u8 *buf, size_t buflen) +{ + const struct dpp_curve_params *curve = pkex->own_bi->curve; + EVP_PKEY_CTX *ctx = NULL; + size_t Jx_len, Lx_len; + u8 Jx[DPP_MAX_SHARED_SECRET_LEN]; + u8 Lx[DPP_MAX_SHARED_SECRET_LEN]; + const u8 *wrapped_data, *b_key, *peer_u; + u16 wrapped_data_len, b_key_len, peer_u_len = 0; + const u8 *addr[4]; + size_t len[4]; + u8 octet; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL; + struct wpabuf *B_pub = NULL; + u8 u[DPP_MAX_HASH_LEN], v[DPP_MAX_HASH_LEN]; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_REQ) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at PKEX CR Request"); + pkex->failed = 1; + return NULL; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (!pkex->exchange_done || pkex->failed || + pkex->t >= PKEX_COUNTER_T_LIMIT || pkex->initiator) + goto fail; + + wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + dpp_pkex_fail(pkex, + "Missing or invalid required Wrapped Data attribute"); + goto fail; + } + + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + goto fail; + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + octet = 0; + addr[1] = &octet; + len[1] = sizeof(octet); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + if (aes_siv_decrypt(pkex->z, curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_pkex_fail(pkex, + "AES-SIV decryption failed - possible PKEX code mismatch"); + pkex->failed = 1; + pkex->t++; + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data"); + goto fail; + } + + b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY, + &b_key_len); + if (!b_key || b_key_len != 2 * curve->prime_len) { + dpp_pkex_fail(pkex, "No valid peer bootstrapping key found"); + goto fail; + } + pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key, + b_key_len); + if (!pkex->peer_bootstrap_key) { + dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid"); + goto fail; + } + dpp_debug_print_key("DPP: Peer bootstrap public key", + pkex->peer_bootstrap_key); + + /* ECDH: J' = y * A' */ + ctx = EVP_PKEY_CTX_new(pkex->y, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 || + EVP_PKEY_derive(ctx, NULL, &Jx_len) != 1 || + Jx_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, Jx, &Jx_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)", + Jx, Jx_len); + + /* u' = HMAC(J'.x, MAC-Initiator | A'.x | Y.x | X'.x) */ + A_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0); + Y_pub = dpp_get_pubkey_point(pkex->y, 0); + X_pub = dpp_get_pubkey_point(pkex->x, 0); + if (!A_pub || !Y_pub || !X_pub) + goto fail; + addr[0] = pkex->peer_mac; + len[0] = ETH_ALEN; + addr[1] = wpabuf_head(A_pub); + len[1] = wpabuf_len(A_pub) / 2; + addr[2] = wpabuf_head(Y_pub); + len[2] = wpabuf_len(Y_pub) / 2; + addr[3] = wpabuf_head(X_pub); + len[3] = wpabuf_len(X_pub) / 2; + if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0) + goto fail; + + peer_u = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG, + &peer_u_len); + if (!peer_u || peer_u_len != curve->hash_len || + os_memcmp(peer_u, u, curve->hash_len) != 0) { + dpp_pkex_fail(pkex, "No valid u (I-Auth tag) found"); + wpa_hexdump(MSG_DEBUG, "DPP: Calculated u'", + u, curve->hash_len); + wpa_hexdump(MSG_DEBUG, "DPP: Received u", peer_u, peer_u_len); + pkex->t++; + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received"); + + /* ECDH: L = b * X' */ + EVP_PKEY_CTX_free(ctx); + ctx = EVP_PKEY_CTX_new(pkex->own_bi->pubkey, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 || + EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 || + Lx_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)", + Lx, Lx_len); + + /* v = HMAC(L.x, MAC-Responder | B.x | X'.x | Y.x) */ + B_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0); + if (!B_pub) + goto fail; + addr[0] = pkex->own_mac; + len[0] = ETH_ALEN; + addr[1] = wpabuf_head(B_pub); + len[1] = wpabuf_len(B_pub) / 2; + addr[2] = wpabuf_head(X_pub); + len[2] = wpabuf_len(X_pub) / 2; + addr[3] = wpabuf_head(Y_pub); + len[3] = wpabuf_len(Y_pub) / 2; + if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: v", v, curve->hash_len); + + msg = dpp_pkex_build_commit_reveal_resp(pkex, B_pub, v); + if (!msg) + goto fail; + +out: + EVP_PKEY_CTX_free(ctx); + os_free(unwrapped); + wpabuf_free(A_pub); + wpabuf_free(B_pub); + wpabuf_free(X_pub); + wpabuf_free(Y_pub); + return msg; +fail: + wpa_printf(MSG_DEBUG, + "DPP: PKEX Commit-Reveal Request processing failed"); + goto out; +} + + +int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr, + const u8 *buf, size_t buflen) +{ + const struct dpp_curve_params *curve = pkex->own_bi->curve; + const u8 *wrapped_data, *b_key, *peer_v; + u16 wrapped_data_len, b_key_len, peer_v_len = 0; + const u8 *addr[4]; + size_t len[4]; + u8 octet; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + int ret = -1; + u8 v[DPP_MAX_HASH_LEN]; + size_t Lx_len; + u8 Lx[DPP_MAX_SHARED_SECRET_LEN]; + EVP_PKEY_CTX *ctx = NULL; + struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL; + +#ifdef CONFIG_TESTING_OPTIONS + if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_RESP) { + wpa_printf(MSG_INFO, + "DPP: TESTING - stop at PKEX CR Response"); + pkex->failed = 1; + goto fail; + } +#endif /* CONFIG_TESTING_OPTIONS */ + + if (!pkex->exchange_done || pkex->failed || + pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator) + goto fail; + + wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { + dpp_pkex_fail(pkex, + "Missing or invalid required Wrapped Data attribute"); + goto fail; + } + + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + goto fail; + + addr[0] = hdr; + len[0] = DPP_HDR_LEN; + octet = 1; + addr[1] = &octet; + len[1] = sizeof(octet); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); + + if (aes_siv_decrypt(pkex->z, curve->hash_len, + wrapped_data, wrapped_data_len, + 2, addr, len, unwrapped) < 0) { + dpp_pkex_fail(pkex, + "AES-SIV decryption failed - possible PKEX code mismatch"); + pkex->t++; + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data"); + goto fail; + } + + b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY, + &b_key_len); + if (!b_key || b_key_len != 2 * curve->prime_len) { + dpp_pkex_fail(pkex, "No valid peer bootstrapping key found"); + goto fail; + } + pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key, + b_key_len); + if (!pkex->peer_bootstrap_key) { + dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid"); + goto fail; + } + dpp_debug_print_key("DPP: Peer bootstrap public key", + pkex->peer_bootstrap_key); + + /* ECDH: L' = x * B' */ + ctx = EVP_PKEY_CTX_new(pkex->x, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pkex->peer_bootstrap_key) != 1 || + EVP_PKEY_derive(ctx, NULL, &Lx_len) != 1 || + Lx_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, Lx, &Lx_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)", + Lx, Lx_len); + + /* v' = HMAC(L.x, MAC-Responder | B'.x | X.x | Y'.x) */ + B_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0); + X_pub = dpp_get_pubkey_point(pkex->x, 0); + Y_pub = dpp_get_pubkey_point(pkex->y, 0); + if (!B_pub || !X_pub || !Y_pub) + goto fail; + addr[0] = pkex->peer_mac; + len[0] = ETH_ALEN; + addr[1] = wpabuf_head(B_pub); + len[1] = wpabuf_len(B_pub) / 2; + addr[2] = wpabuf_head(X_pub); + len[2] = wpabuf_len(X_pub) / 2; + addr[3] = wpabuf_head(Y_pub); + len[3] = wpabuf_len(Y_pub) / 2; + if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0) + goto fail; + + peer_v = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_AUTH_TAG, + &peer_v_len); + if (!peer_v || peer_v_len != curve->hash_len || + os_memcmp(peer_v, v, curve->hash_len) != 0) { + dpp_pkex_fail(pkex, "No valid v (R-Auth tag) found"); + wpa_hexdump(MSG_DEBUG, "DPP: Calculated v'", + v, curve->hash_len); + wpa_hexdump(MSG_DEBUG, "DPP: Received v", peer_v, peer_v_len); + pkex->t++; + goto fail; + } + wpa_printf(MSG_DEBUG, "DPP: Valid v (R-Auth tag) received"); + + ret = 0; +out: + wpabuf_free(B_pub); + wpabuf_free(X_pub); + wpabuf_free(Y_pub); + EVP_PKEY_CTX_free(ctx); + os_free(unwrapped); + return ret; +fail: + goto out; +} + + +void dpp_pkex_free(struct dpp_pkex *pkex) +{ + if (!pkex) + return; + + os_free(pkex->identifier); + os_free(pkex->code); + EVP_PKEY_free(pkex->x); + EVP_PKEY_free(pkex->y); + EVP_PKEY_free(pkex->peer_bootstrap_key); + wpabuf_free(pkex->exchange_req); + wpabuf_free(pkex->exchange_resp); + os_free(pkex); +} + + +#ifdef CONFIG_TESTING_OPTIONS +char * dpp_corrupt_connector_signature(const char *connector) +{ + char *tmp, *pos, *signed3 = NULL; + unsigned char *signature = NULL; + size_t signature_len = 0, signed3_len; + + tmp = os_zalloc(os_strlen(connector) + 5); + if (!tmp) + goto fail; + os_memcpy(tmp, connector, os_strlen(connector)); + + pos = os_strchr(tmp, '.'); + if (!pos) + goto fail; + + pos = os_strchr(pos + 1, '.'); + if (!pos) + goto fail; + pos++; + + wpa_printf(MSG_DEBUG, "DPP: Original base64url encoded signature: %s", + pos); + signature = base64_url_decode((const unsigned char *) pos, + os_strlen(pos), &signature_len); + if (!signature || signature_len == 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: Original Connector signature", + signature, signature_len); + signature[signature_len - 1] ^= 0x01; + wpa_hexdump(MSG_DEBUG, "DPP: Corrupted Connector signature", + signature, signature_len); + signed3 = (char *) base64_url_encode(signature, signature_len, + &signed3_len, 0); + if (!signed3) + goto fail; + os_memcpy(pos, signed3, signed3_len); + pos[signed3_len] = '\0'; + wpa_printf(MSG_DEBUG, "DPP: Corrupted base64url encoded signature: %s", + pos); + +out: + os_free(signature); + os_free(signed3); + return tmp; +fail: + os_free(tmp); + tmp = NULL; + goto out; +} +#endif /* CONFIG_TESTING_OPTIONS */ diff --git a/src/common/dpp.h b/src/common/dpp.h new file mode 100644 index 0000000000000..25759088a57ef --- /dev/null +++ b/src/common/dpp.h @@ -0,0 +1,435 @@ +/* + * DPP functionality shared between hostapd and wpa_supplicant + * 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_H +#define DPP_H + +#include <openssl/x509.h> + +#include "utils/list.h" +#include "common/wpa_common.h" +#include "crypto/sha256.h" + +#define DPP_HDR_LEN (4 + 2) /* OUI, OUI Type, Crypto Suite, DPP frame type */ + +enum dpp_public_action_frame_type { + DPP_PA_AUTHENTICATION_REQ = 0, + DPP_PA_AUTHENTICATION_RESP = 1, + DPP_PA_AUTHENTICATION_CONF = 2, + DPP_PA_PEER_DISCOVERY_REQ = 5, + DPP_PA_PEER_DISCOVERY_RESP = 6, + DPP_PA_PKEX_EXCHANGE_REQ = 7, + DPP_PA_PKEX_EXCHANGE_RESP = 8, + DPP_PA_PKEX_COMMIT_REVEAL_REQ = 9, + DPP_PA_PKEX_COMMIT_REVEAL_RESP = 10, +}; + +enum dpp_attribute_id { + DPP_ATTR_STATUS = 0x1000, + DPP_ATTR_I_BOOTSTRAP_KEY_HASH = 0x1001, + DPP_ATTR_R_BOOTSTRAP_KEY_HASH = 0x1002, + DPP_ATTR_I_PROTOCOL_KEY = 0x1003, + DPP_ATTR_WRAPPED_DATA = 0x1004, + DPP_ATTR_I_NONCE = 0x1005, + DPP_ATTR_I_CAPABILITIES = 0x1006, + DPP_ATTR_R_NONCE = 0x1007, + DPP_ATTR_R_CAPABILITIES = 0x1008, + DPP_ATTR_R_PROTOCOL_KEY = 0x1009, + DPP_ATTR_I_AUTH_TAG = 0x100A, + DPP_ATTR_R_AUTH_TAG = 0x100B, + DPP_ATTR_CONFIG_OBJ = 0x100C, + DPP_ATTR_CONNECTOR = 0x100D, + DPP_ATTR_CONFIG_ATTR_OBJ = 0x100E, + DPP_ATTR_BOOTSTRAP_KEY = 0x100F, + DPP_ATTR_OWN_NET_NK_HASH = 0x1011, + DPP_ATTR_FINITE_CYCLIC_GROUP = 0x1012, + DPP_ATTR_ENCRYPTED_KEY = 0x1013, + DPP_ATTR_ENROLLEE_NONCE = 0x1014, + DPP_ATTR_CODE_IDENTIFIER = 0x1015, + DPP_ATTR_TRANSACTION_ID = 0x1016, + DPP_ATTR_BOOTSTRAP_INFO = 0x1017, + DPP_ATTR_CHANNEL = 0x1018, +}; + +enum dpp_status_error { + DPP_STATUS_OK = 0, + DPP_STATUS_NOT_COMPATIBLE = 1, + DPP_STATUS_AUTH_FAILURE = 2, + DPP_STATUS_UNWRAP_FAILURE = 3, + DPP_STATUS_BAD_GROUP = 4, + DPP_STATUS_CONFIGURE_FAILURE = 5, + DPP_STATUS_RESPONSE_PENDING = 6, + DPP_STATUS_INVALID_CONNECTOR = 7, + DPP_STATUS_NO_MATCH = 8, +}; + +#define DPP_CAPAB_ENROLLEE BIT(0) +#define DPP_CAPAB_CONFIGURATOR BIT(1) +#define DPP_CAPAB_ROLE_MASK (BIT(0) | BIT(1)) + +#define DPP_BOOTSTRAP_MAX_FREQ 30 +#define DPP_MAX_NONCE_LEN 32 +#define DPP_MAX_HASH_LEN 64 +#define DPP_MAX_SHARED_SECRET_LEN 66 + +struct dpp_curve_params { + const char *name; + size_t hash_len; + size_t aes_siv_key_len; + size_t nonce_len; + size_t prime_len; + const char *jwk_crv; + u16 ike_group; + const char *jws_alg; +}; + +enum dpp_bootstrap_type { + DPP_BOOTSTRAP_QR_CODE, + DPP_BOOTSTRAP_PKEX, +}; + +struct dpp_bootstrap_info { + struct dl_list list; + unsigned int id; + enum dpp_bootstrap_type type; + char *uri; + u8 mac_addr[ETH_ALEN]; + char *info; + unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ]; + unsigned int num_freq; + int own; + EVP_PKEY *pubkey; + u8 pubkey_hash[SHA256_MAC_LEN]; + const struct dpp_curve_params *curve; + unsigned int pkex_t; /* number of failures before dpp_pkex + * instantiation */ +}; + +#define PKEX_COUNTER_T_LIMIT 5 + +struct dpp_pkex { + void *msg_ctx; + unsigned int initiator:1; + unsigned int exchange_done:1; + unsigned int failed:1; + struct dpp_bootstrap_info *own_bi; + u8 own_mac[ETH_ALEN]; + u8 peer_mac[ETH_ALEN]; + char *identifier; + char *code; + EVP_PKEY *x; + EVP_PKEY *y; + u8 Mx[DPP_MAX_SHARED_SECRET_LEN]; + u8 Nx[DPP_MAX_SHARED_SECRET_LEN]; + u8 z[DPP_MAX_HASH_LEN]; + EVP_PKEY *peer_bootstrap_key; + struct wpabuf *exchange_req; + struct wpabuf *exchange_resp; + unsigned int t; /* number of failures on code use */ + unsigned int exch_req_wait_time; + unsigned int exch_req_tries; + unsigned int freq; +}; + +enum dpp_akm { + DPP_AKM_UNKNOWN, + DPP_AKM_DPP, + DPP_AKM_PSK, + DPP_AKM_SAE, + DPP_AKM_PSK_SAE +}; + +struct dpp_configuration { + u8 ssid[32]; + size_t ssid_len; + enum dpp_akm akm; + + /* For DPP configuration (connector) */ + os_time_t netaccesskey_expiry; + + /* TODO: groups */ + char *group_id; + + /* For legacy configuration */ + char *passphrase; + u8 psk[32]; +}; + +struct dpp_authentication { + void *msg_ctx; + const struct dpp_curve_params *curve; + struct dpp_bootstrap_info *peer_bi; + struct dpp_bootstrap_info *own_bi; + struct dpp_bootstrap_info *tmp_own_bi; + u8 waiting_pubkey_hash[SHA256_MAC_LEN]; + int response_pending; + enum dpp_status_error auth_resp_status; + u8 peer_mac_addr[ETH_ALEN]; + u8 i_nonce[DPP_MAX_NONCE_LEN]; + u8 r_nonce[DPP_MAX_NONCE_LEN]; + u8 e_nonce[DPP_MAX_NONCE_LEN]; + u8 i_capab; + u8 r_capab; + EVP_PKEY *own_protocol_key; + EVP_PKEY *peer_protocol_key; + struct wpabuf *req_msg; + struct wpabuf *resp_msg; + /* Intersection of possible frequencies for initiating DPP + * Authentication exchange */ + unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ]; + unsigned int num_freq, freq_idx; + unsigned int curr_freq; + unsigned int neg_freq; + unsigned int num_freq_iters; + size_t secret_len; + u8 Mx[DPP_MAX_SHARED_SECRET_LEN]; + size_t Mx_len; + u8 Nx[DPP_MAX_SHARED_SECRET_LEN]; + size_t Nx_len; + u8 Lx[DPP_MAX_SHARED_SECRET_LEN]; + size_t Lx_len; + u8 k1[DPP_MAX_HASH_LEN]; + u8 k2[DPP_MAX_HASH_LEN]; + u8 ke[DPP_MAX_HASH_LEN]; + int initiator; + int waiting_auth_resp; + int waiting_auth_conf; + int auth_req_ack; + unsigned int auth_resp_tries; + u8 allowed_roles; + int configurator; + int remove_on_tx_status; + int auth_success; + struct wpabuf *conf_req; + const struct wpabuf *conf_resp; /* owned by GAS server */ + struct dpp_configuration *conf_ap; + struct dpp_configuration *conf_sta; + struct dpp_configurator *conf; + char *connector; /* received signedConnector */ + u8 ssid[SSID_MAX_LEN]; + u8 ssid_len; + char passphrase[64]; + u8 psk[PMK_LEN]; + int psk_set; + enum dpp_akm akm; + struct wpabuf *net_access_key; + os_time_t net_access_key_expiry; + struct wpabuf *c_sign_key; +#ifdef CONFIG_TESTING_OPTIONS + char *config_obj_override; + char *discovery_override; + char *groups_override; + unsigned int ignore_netaccesskey_mismatch:1; +#endif /* CONFIG_TESTING_OPTIONS */ +}; + +struct dpp_configurator { + struct dl_list list; + unsigned int id; + int own; + EVP_PKEY *csign; + char *kid; + const struct dpp_curve_params *curve; +}; + +struct dpp_introduction { + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN_MAX]; + size_t pmk_len; +}; + +#ifdef CONFIG_TESTING_OPTIONS +enum dpp_test_behavior { + DPP_TEST_DISABLED = 0, + DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ = 1, + DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP = 2, + DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF = 3, + DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ = 4, + DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP = 5, + DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ = 6, + DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP = 7, + DPP_TEST_ZERO_I_CAPAB = 8, + DPP_TEST_ZERO_R_CAPAB = 9, + DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ = 10, + DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ = 11, + DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ = 12, + DPP_TEST_NO_I_NONCE_AUTH_REQ = 13, + DPP_TEST_NO_I_CAPAB_AUTH_REQ = 14, + DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ = 15, + DPP_TEST_NO_STATUS_AUTH_RESP = 16, + DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP = 17, + DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP = 18, + DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP = 19, + DPP_TEST_NO_R_NONCE_AUTH_RESP = 20, + DPP_TEST_NO_I_NONCE_AUTH_RESP = 21, + DPP_TEST_NO_R_CAPAB_AUTH_RESP = 22, + DPP_TEST_NO_R_AUTH_AUTH_RESP = 23, + DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP = 24, + DPP_TEST_NO_STATUS_AUTH_CONF = 25, + DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF = 26, + DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF = 27, + DPP_TEST_NO_I_AUTH_AUTH_CONF = 28, + DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF = 29, + DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP = 30, + DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP = 31, + DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP = 32, + DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF = 33, + DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ = 34, + DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ = 35, + DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP = 36, + DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP = 37, + DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ = 38, + DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ = 39, + DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ = 40, + DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP = 41, + DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP = 42, + DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP = 43, + DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ = 44, + DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP = 45, + DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP = 46, + DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ = 47, + DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP = 48, + DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ = 49, + DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP = 50, + DPP_TEST_NO_E_NONCE_CONF_REQ = 51, + DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ = 52, + DPP_TEST_NO_WRAPPED_DATA_CONF_REQ = 53, + DPP_TEST_NO_E_NONCE_CONF_RESP = 54, + DPP_TEST_NO_CONFIG_OBJ_CONF_RESP = 55, + DPP_TEST_NO_STATUS_CONF_RESP = 56, + DPP_TEST_NO_WRAPPED_DATA_CONF_RESP = 57, + DPP_TEST_INVALID_STATUS_CONF_RESP = 58, + DPP_TEST_E_NONCE_MISMATCH_CONF_RESP = 59, + DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_REQ = 60, + DPP_TEST_NO_CONNECTOR_PEER_DISC_REQ = 61, + DPP_TEST_NO_TRANSACTION_ID_PEER_DISC_RESP = 62, + DPP_TEST_NO_STATUS_PEER_DISC_RESP = 63, + DPP_TEST_NO_CONNECTOR_PEER_DISC_RESP = 64, + DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF = 65, + DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ = 66, + DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP = 67, + DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ = 68, + DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ = 69, + DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP = 70, + DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP = 71, + DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF = 72, + DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF = 73, + DPP_TEST_INVALID_STATUS_AUTH_RESP = 74, + DPP_TEST_INVALID_STATUS_AUTH_CONF = 75, + DPP_TEST_INVALID_CONFIG_ATTR_OBJ_CONF_REQ = 76, + DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_RESP = 77, + DPP_TEST_INVALID_STATUS_PEER_DISC_RESP = 78, + DPP_TEST_INVALID_CONNECTOR_PEER_DISC_RESP = 79, + DPP_TEST_INVALID_CONNECTOR_PEER_DISC_REQ = 80, + DPP_TEST_INVALID_I_NONCE_AUTH_REQ = 81, + DPP_TEST_INVALID_TRANSACTION_ID_PEER_DISC_REQ = 82, + DPP_TEST_INVALID_E_NONCE_CONF_REQ = 83, + DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP = 84, + DPP_TEST_STOP_AT_PKEX_CR_REQ = 85, + DPP_TEST_STOP_AT_PKEX_CR_RESP = 86, + DPP_TEST_STOP_AT_AUTH_REQ = 87, + DPP_TEST_STOP_AT_AUTH_RESP = 88, + DPP_TEST_STOP_AT_AUTH_CONF = 89, + DPP_TEST_STOP_AT_CONF_REQ = 90, +}; + +extern enum dpp_test_behavior dpp_test; +extern u8 dpp_pkex_own_mac_override[ETH_ALEN]; +extern u8 dpp_pkex_peer_mac_override[ETH_ALEN]; +extern u8 dpp_pkex_ephemeral_key_override[600]; +extern size_t dpp_pkex_ephemeral_key_override_len; +extern u8 dpp_protocol_key_override[600]; +extern size_t dpp_protocol_key_override_len; +extern u8 dpp_nonce_override[DPP_MAX_NONCE_LEN]; +extern size_t dpp_nonce_override_len; +#endif /* CONFIG_TESTING_OPTIONS */ + +void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info); +const char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type); +int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi); +int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi, + const char *chan_list); +int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac); +int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info); +struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri); +char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve, + const u8 *privkey, size_t privkey_len); +struct hostapd_hw_modes; +struct dpp_authentication * dpp_auth_init(void *msg_ctx, + struct dpp_bootstrap_info *peer_bi, + struct dpp_bootstrap_info *own_bi, + u8 dpp_allowed_roles, + unsigned int neg_freq, + struct hostapd_hw_modes *own_modes, + u16 num_modes); +struct dpp_authentication * +dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual, + struct dpp_bootstrap_info *peer_bi, + struct dpp_bootstrap_info *own_bi, + unsigned int freq, const u8 *hdr, const u8 *attr_start, + size_t attr_len); +struct wpabuf * +dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr, + const u8 *attr_start, size_t attr_len); +struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth, + const char *json); +int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr, + const u8 *attr_start, size_t attr_len); +int dpp_notify_new_qr_code(struct dpp_authentication *auth, + struct dpp_bootstrap_info *peer_bi); +void dpp_configuration_free(struct dpp_configuration *conf); +void dpp_auth_deinit(struct dpp_authentication *auth); +struct wpabuf * +dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, + size_t attr_len); +int dpp_conf_resp_rx(struct dpp_authentication *auth, + const struct wpabuf *resp); +struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type, + size_t len); +const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len); +int dpp_check_attrs(const u8 *buf, size_t len); +int dpp_key_expired(const char *timestamp, os_time_t *expiry); +const char * dpp_akm_str(enum dpp_akm akm); +int dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf, + size_t buflen); +void dpp_configurator_free(struct dpp_configurator *conf); +struct dpp_configurator * +dpp_keygen_configurator(const char *curve, const u8 *privkey, + size_t privkey_len); +int dpp_configurator_own_config(struct dpp_authentication *auth, + const char *curve, int ap); +enum dpp_status_error +dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector, + const u8 *net_access_key, size_t net_access_key_len, + const u8 *csign_key, size_t csign_key_len, + const u8 *peer_connector, size_t peer_connector_len, + os_time_t *expiry); +struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi, + const u8 *own_mac, + const char *identifier, + const char *code); +struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx, + struct dpp_bootstrap_info *bi, + const u8 *own_mac, + const u8 *peer_mac, + const char *identifier, + const char *code, + const u8 *buf, size_t len); +struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex, + const u8 *peer_mac, + const u8 *buf, size_t len); +struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex, + const u8 *hdr, + const u8 *buf, size_t len); +int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr, + const u8 *buf, size_t len); +void dpp_pkex_free(struct dpp_pkex *pkex); + +char * dpp_corrupt_connector_signature(const char *connector); + +#endif /* DPP_H */ diff --git a/src/common/gas.c b/src/common/gas.c index cff9254b74401..ba21b225efc95 100644 --- a/src/common/gas.c +++ b/src/common/gas.c @@ -75,7 +75,7 @@ gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay, } -static struct wpabuf * +struct wpabuf * gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more, u16 comeback_delay, size_t size) { diff --git a/src/common/gas.h b/src/common/gas.h index 306adc58c6ee2..4c93e3114ccd1 100644 --- a/src/common/gas.h +++ b/src/common/gas.h @@ -14,6 +14,9 @@ struct wpabuf * gas_build_initial_req(u8 dialog_token, size_t size); struct wpabuf * gas_build_comeback_req(u8 dialog_token); struct wpabuf * gas_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay, size_t size); +struct wpabuf * +gas_build_comeback_resp(u8 dialog_token, u16 status_code, u8 frag_id, u8 more, + u16 comeback_delay, size_t size); struct wpabuf * gas_anqp_build_initial_req(u8 dialog_token, size_t size); struct wpabuf * gas_anqp_build_initial_resp(u8 dialog_token, u16 status_code, u16 comeback_delay, size_t size); diff --git a/src/common/gas_server.c b/src/common/gas_server.c new file mode 100644 index 0000000000000..ca46758cec7cd --- /dev/null +++ b/src/common/gas_server.c @@ -0,0 +1,487 @@ +/* + * Generic advertisement service (GAS) server + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "utils/common.h" +#include "utils/list.h" +#include "utils/eloop.h" +#include "ieee802_11_defs.h" +#include "gas.h" +#include "gas_server.h" + + +#define MAX_ADV_PROTO_ID_LEN 10 +#define GAS_QUERY_TIMEOUT 10 + +struct gas_server_handler { + struct dl_list list; + u8 adv_proto_id[MAX_ADV_PROTO_ID_LEN]; + u8 adv_proto_id_len; + struct wpabuf * (*req_cb)(void *ctx, const u8 *sa, + const u8 *query, size_t query_len); + void (*status_cb)(void *ctx, struct wpabuf *resp, int ok); + void *ctx; + struct gas_server *gas; +}; + +struct gas_server_response { + struct dl_list list; + size_t offset; + u8 frag_id; + struct wpabuf *resp; + int freq; + u8 dst[ETH_ALEN]; + u8 dialog_token; + struct gas_server_handler *handler; +}; + +struct gas_server { + struct dl_list handlers; /* struct gas_server_handler::list */ + struct dl_list responses; /* struct gas_server_response::list */ + void (*tx)(void *ctx, int freq, const u8 *da, struct wpabuf *resp, + unsigned int wait_time); + void *ctx; +}; + +static void gas_server_free_response(struct gas_server_response *response); + + +static void gas_server_response_timeout(void *eloop_ctx, void *user_ctx) +{ + struct gas_server_response *response = eloop_ctx; + + wpa_printf(MSG_DEBUG, "GAS: Response @%p timeout for " MACSTR + " (dialog_token=%u freq=%d frag_id=%u sent=%lu/%lu) - drop pending data", + response, MAC2STR(response->dst), response->dialog_token, + response->freq, response->frag_id, + (unsigned long) response->offset, + (unsigned long) wpabuf_len(response->resp)); + response->handler->status_cb(response->handler->ctx, + response->resp, 0); + response->resp = NULL; + dl_list_del(&response->list); + gas_server_free_response(response); +} + + +static void gas_server_free_response(struct gas_server_response *response) +{ + if (!response) + return; + wpa_printf(MSG_DEBUG, "DPP: Free GAS response @%p", response); + eloop_cancel_timeout(gas_server_response_timeout, response, NULL); + wpabuf_free(response->resp); + os_free(response); +} + + +static void +gas_server_send_resp(struct gas_server *gas, struct gas_server_handler *handler, + const u8 *da, int freq, u8 dialog_token, + struct wpabuf *query_resp) +{ + size_t max_len = (freq > 56160) ? 928 : 1400; + size_t hdr_len = 24 + 2 + 5 + 3 + handler->adv_proto_id_len + 2; + size_t resp_frag_len; + struct wpabuf *resp; + u16 comeback_delay; + struct gas_server_response *response; + + if (!query_resp) + return; + + response = os_zalloc(sizeof(*response)); + if (!response) { + wpabuf_free(query_resp); + return; + } + wpa_printf(MSG_DEBUG, "DPP: Allocated GAS response @%p", response); + response->freq = freq; + response->handler = handler; + os_memcpy(response->dst, da, ETH_ALEN); + response->dialog_token = dialog_token; + if (hdr_len + wpabuf_len(query_resp) > max_len) { + /* Need to use comeback to initiate fragmentation */ + comeback_delay = 1; + resp_frag_len = 0; + } else { + /* Full response fits into the initial response */ + comeback_delay = 0; + resp_frag_len = wpabuf_len(query_resp); + } + + resp = gas_build_initial_resp(dialog_token, WLAN_STATUS_SUCCESS, + comeback_delay, + handler->adv_proto_id_len + + resp_frag_len); + if (!resp) { + wpabuf_free(query_resp); + gas_server_free_response(response); + return; + } + + /* Advertisement Protocol element */ + wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */ + wpabuf_put_u8(resp, 0x7f); + /* Advertisement Protocol ID */ + wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len); + + /* Query Response Length */ + wpabuf_put_le16(resp, resp_frag_len); + if (!comeback_delay) + wpabuf_put_buf(resp, query_resp); + + if (comeback_delay) { + wpa_printf(MSG_DEBUG, + "GAS: Need to fragment query response"); + } else { + wpa_printf(MSG_DEBUG, + "GAS: Full query response fits in the GAS Initial Response frame"); + } + response->offset = resp_frag_len; + response->resp = query_resp; + dl_list_add(&gas->responses, &response->list); + gas->tx(gas->ctx, freq, da, resp, comeback_delay ? 2000 : 0); + wpabuf_free(resp); + eloop_register_timeout(GAS_QUERY_TIMEOUT, 0, + gas_server_response_timeout, response, NULL); +} + + +static int +gas_server_rx_initial_req(struct gas_server *gas, const u8 *da, const u8 *sa, + const u8 *bssid, int freq, u8 dialog_token, + const u8 *data, size_t len) +{ + const u8 *pos, *end, *adv_proto, *query_req; + u8 adv_proto_len; + u16 query_req_len; + struct gas_server_handler *handler; + struct wpabuf *resp; + + wpa_hexdump(MSG_MSGDUMP, "GAS: Received GAS Initial Request frame", + data, len); + pos = data; + end = data + len; + + if (end - pos < 2 || pos[0] != WLAN_EID_ADV_PROTO) { + wpa_printf(MSG_DEBUG, + "GAS: No Advertisement Protocol element found"); + return -1; + } + pos++; + adv_proto_len = *pos++; + if (end - pos < adv_proto_len || adv_proto_len < 2) { + wpa_printf(MSG_DEBUG, + "GAS: Truncated Advertisement Protocol element"); + return -1; + } + + adv_proto = pos; + pos += adv_proto_len; + wpa_hexdump(MSG_MSGDUMP, "GAS: Advertisement Protocol element", + adv_proto, adv_proto_len); + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "GAS: No Query Request Length field"); + return -1; + } + query_req_len = WPA_GET_LE16(pos); + pos += 2; + if (end - pos < query_req_len) { + wpa_printf(MSG_DEBUG, "GAS: Truncated Query Request field"); + return -1; + } + query_req = pos; + pos += query_req_len; + wpa_hexdump(MSG_MSGDUMP, "GAS: Query Request", + query_req, query_req_len); + + if (pos < end) { + wpa_hexdump(MSG_MSGDUMP, + "GAS: Ignored extra data after Query Request field", + pos, end - pos); + } + + dl_list_for_each(handler, &gas->handlers, struct gas_server_handler, + list) { + if (adv_proto_len < 1 + handler->adv_proto_id_len || + os_memcmp(adv_proto + 1, handler->adv_proto_id, + handler->adv_proto_id_len) != 0) + continue; + + wpa_printf(MSG_DEBUG, + "GAS: Calling handler for the requested Advertisement Protocol ID"); + resp = handler->req_cb(handler->ctx, sa, query_req, + query_req_len); + wpa_hexdump_buf(MSG_MSGDUMP, "GAS: Response from the handler", + resp); + gas_server_send_resp(gas, handler, sa, freq, dialog_token, + resp); + return 0; + } + + wpa_printf(MSG_DEBUG, + "GAS: No registered handler for the requested Advertisement Protocol ID"); + return -1; +} + + +static void +gas_server_handle_rx_comeback_req(struct gas_server_response *response) +{ + struct gas_server_handler *handler = response->handler; + struct gas_server *gas = handler->gas; + size_t max_len = (response->freq > 56160) ? 928 : 1400; + size_t hdr_len = 24 + 2 + 6 + 3 + handler->adv_proto_id_len + 2; + size_t remaining, resp_frag_len; + struct wpabuf *resp; + + remaining = wpabuf_len(response->resp) - response->offset; + if (hdr_len + remaining > max_len) + resp_frag_len = max_len - hdr_len; + else + resp_frag_len = remaining; + wpa_printf(MSG_DEBUG, + "GAS: Sending out %u/%u remaining Query Response octets", + (unsigned int) resp_frag_len, (unsigned int) remaining); + + resp = gas_build_comeback_resp(response->dialog_token, + WLAN_STATUS_SUCCESS, + response->frag_id++, + resp_frag_len < remaining, 0, + handler->adv_proto_id_len + + resp_frag_len); + if (!resp) { + dl_list_del(&response->list); + gas_server_free_response(response); + return; + } + + /* Advertisement Protocol element */ + wpabuf_put_u8(resp, WLAN_EID_ADV_PROTO); + wpabuf_put_u8(resp, 1 + handler->adv_proto_id_len); /* Length */ + wpabuf_put_u8(resp, 0x7f); + /* Advertisement Protocol ID */ + wpabuf_put_data(resp, handler->adv_proto_id, handler->adv_proto_id_len); + + /* Query Response Length */ + wpabuf_put_le16(resp, resp_frag_len); + wpabuf_put_data(resp, wpabuf_head_u8(response->resp) + response->offset, + resp_frag_len); + + response->offset += resp_frag_len; + + gas->tx(gas->ctx, response->freq, response->dst, resp, + remaining > resp_frag_len ? 2000 : 0); + wpabuf_free(resp); +} + + +static int +gas_server_rx_comeback_req(struct gas_server *gas, const u8 *da, const u8 *sa, + const u8 *bssid, int freq, u8 dialog_token) +{ + struct gas_server_response *response; + + dl_list_for_each(response, &gas->responses, struct gas_server_response, + list) { + if (response->dialog_token != dialog_token || + os_memcmp(sa, response->dst, ETH_ALEN) != 0) + continue; + gas_server_handle_rx_comeback_req(response); + return 0; + } + + wpa_printf(MSG_DEBUG, "GAS: No pending GAS response for " MACSTR + " (dialog token %u)", MAC2STR(sa), dialog_token); + return -1; +} + + +/** + * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame + * @gas: GAS query data from gas_server_init() + * @da: Destination MAC address of the Action frame + * @sa: Source MAC address of the Action frame + * @bssid: BSSID of the Action frame + * @categ: Category of the Action frame + * @data: Payload of the Action frame + * @len: Length of @data + * @freq: Frequency (in MHz) on which the frame was received + * Returns: 0 if the Public Action frame was a GAS request frame or -1 if not + */ +int gas_server_rx(struct gas_server *gas, const u8 *da, const u8 *sa, + const u8 *bssid, u8 categ, const u8 *data, size_t len, + int freq) +{ + u8 action, dialog_token; + const u8 *pos, *end; + + if (!gas || len < 2) + return -1; + + if (categ == WLAN_ACTION_PROTECTED_DUAL) + return -1; /* Not supported for now */ + + pos = data; + end = data + len; + action = *pos++; + dialog_token = *pos++; + + if (action != WLAN_PA_GAS_INITIAL_REQ && + action != WLAN_PA_GAS_COMEBACK_REQ) + return -1; /* Not a GAS request */ + + wpa_printf(MSG_DEBUG, "GAS: Received GAS %s Request frame DA=" MACSTR + " SA=" MACSTR " BSSID=" MACSTR + " freq=%d dialog_token=%u len=%u", + action == WLAN_PA_GAS_INITIAL_REQ ? "Initial" : "Comeback", + MAC2STR(da), MAC2STR(sa), MAC2STR(bssid), freq, dialog_token, + (unsigned int) len); + + if (action == WLAN_PA_GAS_INITIAL_REQ) + return gas_server_rx_initial_req(gas, da, sa, bssid, + freq, dialog_token, + pos, end - pos); + return gas_server_rx_comeback_req(gas, da, sa, bssid, + freq, dialog_token); +} + + +static void gas_server_handle_tx_status(struct gas_server_response *response, + int ack) +{ + if (ack && response->offset < wpabuf_len(response->resp)) { + wpa_printf(MSG_DEBUG, + "GAS: More fragments remaining - keep pending entry"); + return; + } + + if (!ack) + wpa_printf(MSG_DEBUG, + "GAS: No ACK received - drop pending entry"); + else + wpa_printf(MSG_DEBUG, + "GAS: Last fragment of the response sent out - drop pending entry"); + + response->handler->status_cb(response->handler->ctx, + response->resp, ack); + response->resp = NULL; + dl_list_del(&response->list); + gas_server_free_response(response); +} + + +void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data, + size_t data_len, int ack) +{ + const u8 *pos; + u8 action, code, dialog_token; + struct gas_server_response *response; + + if (data_len < 24 + 3) + return; + pos = data + 24; + action = *pos++; + code = *pos++; + dialog_token = *pos++; + if (action != WLAN_ACTION_PUBLIC || + (code != WLAN_PA_GAS_INITIAL_RESP && + code != WLAN_PA_GAS_COMEBACK_RESP)) + return; + wpa_printf(MSG_DEBUG, "GAS: TX status dst=" MACSTR + " ack=%d %s dialog_token=%u", + MAC2STR(dst), ack, + code == WLAN_PA_GAS_INITIAL_RESP ? "initial" : "comeback", + dialog_token); + dl_list_for_each(response, &gas->responses, struct gas_server_response, + list) { + if (response->dialog_token != dialog_token || + os_memcmp(dst, response->dst, ETH_ALEN) != 0) + continue; + gas_server_handle_tx_status(response, ack); + return; + } + + wpa_printf(MSG_DEBUG, "GAS: No pending response matches TX status"); +} + + +struct gas_server * gas_server_init(void *ctx, + void (*tx)(void *ctx, int freq, + const u8 *da, + struct wpabuf *buf, + unsigned int wait_time)) +{ + struct gas_server *gas; + + gas = os_zalloc(sizeof(*gas)); + if (!gas) + return NULL; + gas->ctx = ctx; + gas->tx = tx; + dl_list_init(&gas->handlers); + dl_list_init(&gas->responses); + return gas; +} + + +void gas_server_deinit(struct gas_server *gas) +{ + struct gas_server_handler *handler, *tmp; + struct gas_server_response *response, *tmp_r; + + if (!gas) + return; + + dl_list_for_each_safe(handler, tmp, &gas->handlers, + struct gas_server_handler, list) { + dl_list_del(&handler->list); + os_free(handler); + } + + dl_list_for_each_safe(response, tmp_r, &gas->responses, + struct gas_server_response, list) { + dl_list_del(&response->list); + gas_server_free_response(response); + } + + os_free(gas); +} + + +int gas_server_register(struct gas_server *gas, + const u8 *adv_proto_id, u8 adv_proto_id_len, + struct wpabuf * + (*req_cb)(void *ctx, const u8 *sa, + const u8 *query, size_t query_len), + void (*status_cb)(void *ctx, struct wpabuf *resp, + int ok), + void *ctx) +{ + struct gas_server_handler *handler; + + if (!gas || adv_proto_id_len > MAX_ADV_PROTO_ID_LEN) + return -1; + handler = os_zalloc(sizeof(*handler)); + if (!handler) + return -1; + + os_memcpy(handler->adv_proto_id, adv_proto_id, adv_proto_id_len); + handler->adv_proto_id_len = adv_proto_id_len; + handler->req_cb = req_cb; + handler->status_cb = status_cb; + handler->ctx = ctx; + handler->gas = gas; + dl_list_add(&gas->handlers, &handler->list); + + return 0; +} diff --git a/src/common/gas_server.h b/src/common/gas_server.h new file mode 100644 index 0000000000000..299f529f7c56b --- /dev/null +++ b/src/common/gas_server.h @@ -0,0 +1,44 @@ +/* + * Generic advertisement service (GAS) server + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef GAS_SERVER_H +#define GAS_SERVER_H + +#ifdef CONFIG_GAS_SERVER + +struct gas_server; + +struct gas_server * gas_server_init(void *ctx, + void (*tx)(void *ctx, int freq, + const u8 *da, + struct wpabuf *buf, + unsigned int wait_time)); +void gas_server_deinit(struct gas_server *gas); +int gas_server_register(struct gas_server *gas, + const u8 *adv_proto_id, u8 adv_proto_id_len, + struct wpabuf * + (*req_cb)(void *ctx, const u8 *sa, + const u8 *query, size_t query_len), + void (*status_cb)(void *ctx, struct wpabuf *resp, + int ok), + void *ctx); +int gas_server_rx(struct gas_server *gas, const u8 *da, const u8 *sa, + const u8 *bssid, u8 categ, const u8 *data, size_t len, + int freq); +void gas_server_tx_status(struct gas_server *gas, const u8 *dst, const u8 *data, + size_t data_len, int ack); + +#else /* CONFIG_GAS_SERVER */ + +static inline void gas_server_deinit(struct gas_server *gas) +{ +} + +#endif /* CONFIG_GAS_SERVER */ + +#endif /* GAS_SERVER_H */ diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c index 9c37ea63ca878..db40379271850 100644 --- a/src/common/hw_features_common.c +++ b/src/common/hw_features_common.c @@ -89,7 +89,7 @@ int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan, { int ok, j, first; int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140, - 149, 157, 184, 192 }; + 149, 157, 165, 184, 192 }; size_t k; if (pri_chan == sec_chan || !sec_chan) @@ -388,8 +388,10 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, /* fall through */ case VHT_CHANWIDTH_80MHZ: data->bandwidth = 80; - if ((vht_oper_chwidth == 1 && center_segment1) || - (vht_oper_chwidth == 3 && !center_segment1) || + if ((vht_oper_chwidth == VHT_CHANWIDTH_80MHZ && + center_segment1) || + (vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ && + !center_segment1) || !sec_channel_offset) return -1; if (!center_segment0) { @@ -453,3 +455,101 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, return 0; } + + +void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps, + int disabled) +{ + /* Masking these out disables HT40 */ + le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET | + HT_CAP_INFO_SHORT_GI40MHZ); + + if (disabled) + htcaps->ht_capabilities_info &= ~msk; + else + htcaps->ht_capabilities_info |= msk; +} + + +#ifdef CONFIG_IEEE80211AC + +static int _ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, + const char *name) +{ + u32 req_cap = conf & cap; + + /* + * Make sure we support all requested capabilities. + * NOTE: We assume that 'cap' represents a capability mask, + * not a discrete value. + */ + if ((hw & req_cap) != req_cap) { + wpa_printf(MSG_ERROR, + "Driver does not support configured VHT capability [%s]", + name); + return 0; + } + return 1; +} + + +static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask, + unsigned int shift, + const char *name) +{ + u32 hw_max = hw & mask; + u32 conf_val = conf & mask; + + if (conf_val > hw_max) { + wpa_printf(MSG_ERROR, + "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)", + name, conf_val >> shift, hw_max >> shift); + return 0; + } + return 1; +} + + +int ieee80211ac_cap_check(u32 hw, u32 conf) +{ +#define VHT_CAP_CHECK(cap) \ + do { \ + if (!_ieee80211ac_cap_check(hw, conf, cap, #cap)) \ + return 0; \ + } while (0) + +#define VHT_CAP_CHECK_MAX(cap) \ + do { \ + if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \ + #cap)) \ + return 0; \ + } while (0) + + VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK); + VHT_CAP_CHECK_MAX(VHT_CAP_SUPP_CHAN_WIDTH_MASK); + VHT_CAP_CHECK(VHT_CAP_RXLDPC); + VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80); + VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160); + VHT_CAP_CHECK(VHT_CAP_TXSTBC); + VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK); + VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE); + VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE); + VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX); + VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX); + VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE); + VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE); + VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS); + VHT_CAP_CHECK(VHT_CAP_HTC_VHT); + VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX); + VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB); + VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB); + VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN); + VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN); + +#undef VHT_CAP_CHECK +#undef VHT_CAP_CHECK_MAX + + return 1; +} + +#endif /* CONFIG_IEEE80211AC */ diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h index 7360b4e3efede..9cddbd50e56a9 100644 --- a/src/common/hw_features_common.h +++ b/src/common/hw_features_common.h @@ -35,5 +35,8 @@ int hostapd_set_freq_params(struct hostapd_freq_params *data, int vht_enabled, int sec_channel_offset, int vht_oper_chwidth, int center_segment0, int center_segment1, u32 vht_caps); +void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps, + int disabled); +int ieee80211ac_cap_check(u32 hw, u32 conf); #endif /* HW_FEATURES_COMMON_H */ diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c index b6bc449bf7dcd..e1ef27795b99a 100644 --- a/src/common/ieee802_11_common.c +++ b/src/common/ieee802_11_common.c @@ -11,6 +11,7 @@ #include "common.h" #include "defs.h" #include "wpa_common.h" +#include "drivers/driver.h" #include "qca-vendor.h" #include "ieee802_11_defs.h" #include "ieee802_11_common.h" @@ -120,6 +121,11 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, elems->mbo = pos; elems->mbo_len = elen; break; + case HS20_ROAMING_CONS_SEL_OUI_TYPE: + /* Hotspot 2.0 Roaming Consortium Selection */ + elems->roaming_cons_sel = pos; + elems->roaming_cons_sel_len = elen; + break; default: wpa_printf(MSG_MSGDUMP, "Unknown WFA " "information element ignored " @@ -179,6 +185,100 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, } +static int ieee802_11_parse_extension(const u8 *pos, size_t elen, + struct ieee802_11_elems *elems, + int show_errors) +{ + u8 ext_id; + + if (elen < 1) { + if (show_errors) { + wpa_printf(MSG_MSGDUMP, + "short information element (Ext)"); + } + return -1; + } + + ext_id = *pos++; + elen--; + + switch (ext_id) { + case WLAN_EID_EXT_ASSOC_DELAY_INFO: + if (elen != 1) + break; + elems->assoc_delay_info = pos; + break; + case WLAN_EID_EXT_FILS_REQ_PARAMS: + if (elen < 3) + break; + elems->fils_req_params = pos; + elems->fils_req_params_len = elen; + break; + case WLAN_EID_EXT_FILS_KEY_CONFIRM: + elems->fils_key_confirm = pos; + elems->fils_key_confirm_len = elen; + break; + case WLAN_EID_EXT_FILS_SESSION: + if (elen != FILS_SESSION_LEN) + break; + elems->fils_session = pos; + break; + case WLAN_EID_EXT_FILS_HLP_CONTAINER: + if (elen < 2 * ETH_ALEN) + break; + elems->fils_hlp = pos; + elems->fils_hlp_len = elen; + break; + case WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN: + if (elen < 1) + break; + elems->fils_ip_addr_assign = pos; + elems->fils_ip_addr_assign_len = elen; + break; + case WLAN_EID_EXT_KEY_DELIVERY: + if (elen < WPA_KEY_RSC_LEN) + break; + elems->key_delivery = pos; + elems->key_delivery_len = elen; + break; + case WLAN_EID_EXT_FILS_WRAPPED_DATA: + elems->fils_wrapped_data = pos; + elems->fils_wrapped_data_len = elen; + break; + case WLAN_EID_EXT_FILS_PUBLIC_KEY: + if (elen < 1) + break; + elems->fils_pk = pos; + elems->fils_pk_len = elen; + break; + case WLAN_EID_EXT_FILS_NONCE: + if (elen != FILS_NONCE_LEN) + break; + elems->fils_nonce = pos; + break; + case WLAN_EID_EXT_OWE_DH_PARAM: + if (elen < 2) + break; + elems->owe_dh = pos; + elems->owe_dh_len = elen; + break; + case WLAN_EID_EXT_PASSWORD_IDENTIFIER: + elems->password_id = pos; + elems->password_id_len = elen; + break; + default: + if (show_errors) { + wpa_printf(MSG_MSGDUMP, + "IEEE 802.11 element parsing ignored unknown element extension (ext_id=%u elen=%u)", + ext_id, (unsigned int) elen); + } + return -1; + } + + return 0; +} + + /** * ieee802_11_parse_elems - Parse information elements in management frames * @start: Pointer to the start of IEs @@ -262,6 +362,10 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->rsn_ie_len = elen; break; case WLAN_EID_PWR_CAPABILITY: + if (elen < 2) + break; + elems->power_capab = pos; + elems->power_capab_len = elen; break; case WLAN_EID_SUPPORTED_CHANNELS: elems->supp_channels = pos; @@ -379,6 +483,35 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, elems->rrm_enabled = pos; elems->rrm_enabled_len = elen; break; + case WLAN_EID_CAG_NUMBER: + elems->cag_number = pos; + elems->cag_number_len = elen; + break; + case WLAN_EID_AP_CSN: + if (elen < 1) + break; + elems->ap_csn = pos; + break; + case WLAN_EID_FILS_INDICATION: + if (elen < 2) + break; + elems->fils_indic = pos; + elems->fils_indic_len = elen; + break; + case WLAN_EID_DILS: + if (elen < 2) + break; + elems->dils = pos; + elems->dils_len = elen; + break; + case WLAN_EID_FRAGMENT: + /* TODO */ + break; + case WLAN_EID_EXTENSION: + if (ieee802_11_parse_extension(pos, elen, elems, + show_errors)) + unknown++; + break; default: unknown++; if (!show_errors) @@ -681,6 +814,25 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, return HOSTAPD_MODE_IEEE80211A; } + /* 5 GHz, channels 52..64 */ + if (freq >= 5260 && freq <= 5320) { + if ((freq - 5000) % 5) + return NUM_HOSTAPD_MODES; + + if (vht_opclass) + *op_class = vht_opclass; + else if (sec_channel == 1) + *op_class = 119; + else if (sec_channel == -1) + *op_class = 120; + else + *op_class = 118; + + *channel = (freq - 5000) / 5; + + return HOSTAPD_MODE_IEEE80211A; + } + /* 5 GHz, channels 149..169 */ if (freq >= 5745 && freq <= 5845) { if ((freq - 5000) % 5) @@ -981,7 +1133,7 @@ static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan) return -1; return 5000 + 5 * chan; case 129: /* center freqs 50, 114; 160 MHz */ - if (chan < 50 || chan > 114) + if (chan < 36 || chan > 128) return -1; return 5000 + 5 * chan; case 180: /* 60 GHz band, channels 1..4 */ @@ -1031,10 +1183,24 @@ int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan) } -int ieee80211_is_dfs(int freq) +int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes, + u16 num_modes) { - /* TODO: this could be more accurate to better cover all domains */ - return (freq >= 5260 && freq <= 5320) || (freq >= 5500 && freq <= 5700); + int i, j; + + if (!modes || !num_modes) + return (freq >= 5260 && freq <= 5320) || + (freq >= 5500 && freq <= 5700); + + for (i = 0; i < num_modes; i++) { + for (j = 0; j < modes[i].num_channels; j++) { + if (modes[i].channels[j].freq == freq && + (modes[i].channels[j].flag & HOSTAPD_CHAN_RADAR)) + return 1; + } + } + + return 0; } @@ -1295,6 +1461,40 @@ const u8 * get_ie(const u8 *ies, size_t len, u8 eid) } +/** + * get_ie_ext - Fetch a specified extended information element from IEs buffer + * @ies: Information elements buffer + * @len: Information elements buffer length + * @ext: Information element extension identifier (WLAN_EID_EXT_*) + * Returns: Pointer to the information element (id field) or %NULL if not found + * + * This function returns the first matching information element in the IEs + * buffer or %NULL in case the element is not found. + */ +const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext) +{ + const u8 *end; + + if (!ies) + return NULL; + + end = ies + len; + + while (end - ies > 1) { + if (2 + ies[1] > end - ies) + break; + + if (ies[0] == WLAN_EID_EXTENSION && ies[1] >= 1 && + ies[2] == ext) + return ies; + + ies += 2 + ies[1]; + } + + return NULL; +} + + size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len) { /* @@ -1317,3 +1517,250 @@ size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len) return 6 + attr_len; } + + +static const struct country_op_class us_op_class[] = { + { 1, 115 }, + { 2, 118 }, + { 3, 124 }, + { 4, 121 }, + { 5, 125 }, + { 12, 81 }, + { 22, 116 }, + { 23, 119 }, + { 24, 122 }, + { 25, 126 }, + { 26, 126 }, + { 27, 117 }, + { 28, 120 }, + { 29, 123 }, + { 30, 127 }, + { 31, 127 }, + { 32, 83 }, + { 33, 84 }, + { 34, 180 }, +}; + +static const struct country_op_class eu_op_class[] = { + { 1, 115 }, + { 2, 118 }, + { 3, 121 }, + { 4, 81 }, + { 5, 116 }, + { 6, 119 }, + { 7, 122 }, + { 8, 117 }, + { 9, 120 }, + { 10, 123 }, + { 11, 83 }, + { 12, 84 }, + { 17, 125 }, + { 18, 180 }, +}; + +static const struct country_op_class jp_op_class[] = { + { 1, 115 }, + { 30, 81 }, + { 31, 82 }, + { 32, 118 }, + { 33, 118 }, + { 34, 121 }, + { 35, 121 }, + { 36, 116 }, + { 37, 119 }, + { 38, 119 }, + { 39, 122 }, + { 40, 122 }, + { 41, 117 }, + { 42, 120 }, + { 43, 120 }, + { 44, 123 }, + { 45, 123 }, + { 56, 83 }, + { 57, 84 }, + { 58, 121 }, + { 59, 180 }, +}; + +static const struct country_op_class cn_op_class[] = { + { 1, 115 }, + { 2, 118 }, + { 3, 125 }, + { 4, 116 }, + { 5, 119 }, + { 6, 126 }, + { 7, 81 }, + { 8, 83 }, + { 9, 84 }, +}; + +static u8 +global_op_class_from_country_array(u8 op_class, size_t array_size, + const struct country_op_class *country_array) +{ + size_t i; + + for (i = 0; i < array_size; i++) { + if (country_array[i].country_op_class == op_class) + return country_array[i].global_op_class; + } + + return 0; +} + + +u8 country_to_global_op_class(const char *country, u8 op_class) +{ + const struct country_op_class *country_array; + size_t size; + u8 g_op_class; + + if (country_match(us_op_class_cc, country)) { + country_array = us_op_class; + size = ARRAY_SIZE(us_op_class); + } else if (country_match(eu_op_class_cc, country)) { + country_array = eu_op_class; + size = ARRAY_SIZE(eu_op_class); + } else if (country_match(jp_op_class_cc, country)) { + country_array = jp_op_class; + size = ARRAY_SIZE(jp_op_class); + } else if (country_match(cn_op_class_cc, country)) { + country_array = cn_op_class; + size = ARRAY_SIZE(cn_op_class); + } else { + /* + * Countries that do not match any of the above countries use + * global operating classes + */ + return op_class; + } + + g_op_class = global_op_class_from_country_array(op_class, size, + country_array); + + /* + * If the given operating class did not match any of the country's + * operating classes, assume that global operating class is used. + */ + return g_op_class ? g_op_class : op_class; +} + + +const struct oper_class_map * get_oper_class(const char *country, u8 op_class) +{ + const struct oper_class_map *op; + + if (country) + op_class = country_to_global_op_class(country, op_class); + + op = &global_op_class[0]; + while (op->op_class && op->op_class != op_class) + op++; + + if (!op->op_class) + return NULL; + + return op; +} + + +int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep, + size_t nei_rep_len) +{ + u8 *nei_pos = nei_rep; + const char *end; + + /* + * BSS Transition Candidate List Entries - Neighbor Report elements + * neighbor=<BSSID>,<BSSID Information>,<Operating Class>, + * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>] + */ + while (pos) { + u8 *nei_start; + long int val; + char *endptr, *tmp; + + pos = os_strstr(pos, " neighbor="); + if (!pos) + break; + if (nei_pos + 15 > nei_rep + nei_rep_len) { + wpa_printf(MSG_DEBUG, + "Not enough room for additional neighbor"); + return -1; + } + pos += 10; + + nei_start = nei_pos; + *nei_pos++ = WLAN_EID_NEIGHBOR_REPORT; + nei_pos++; /* length to be filled in */ + + if (hwaddr_aton(pos, nei_pos)) { + wpa_printf(MSG_DEBUG, "Invalid BSSID"); + return -1; + } + nei_pos += ETH_ALEN; + pos += 17; + if (*pos != ',') { + wpa_printf(MSG_DEBUG, "Missing BSSID Information"); + return -1; + } + pos++; + + val = strtol(pos, &endptr, 0); + WPA_PUT_LE32(nei_pos, val); + nei_pos += 4; + if (*endptr != ',') { + wpa_printf(MSG_DEBUG, "Missing Operating Class"); + return -1; + } + pos = endptr + 1; + + *nei_pos++ = atoi(pos); /* Operating Class */ + pos = os_strchr(pos, ','); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "Missing Channel Number"); + return -1; + } + pos++; + + *nei_pos++ = atoi(pos); /* Channel Number */ + pos = os_strchr(pos, ','); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, "Missing PHY Type"); + return -1; + } + pos++; + + *nei_pos++ = atoi(pos); /* PHY Type */ + end = os_strchr(pos, ' '); + tmp = os_strchr(pos, ','); + if (tmp && (!end || tmp < end)) { + /* Optional Subelements (hexdump) */ + size_t len; + + pos = tmp + 1; + end = os_strchr(pos, ' '); + if (end) + len = end - pos; + else + len = os_strlen(pos); + if (nei_pos + len / 2 > nei_rep + nei_rep_len) { + wpa_printf(MSG_DEBUG, + "Not enough room for neighbor subelements"); + return -1; + } + if (len & 0x01 || + hexstr2bin(pos, nei_pos, len / 2) < 0) { + wpa_printf(MSG_DEBUG, + "Invalid neighbor subelement info"); + return -1; + } + nei_pos += len / 2; + pos = end; + } + + nei_start[1] = nei_pos - nei_start - 2; + } + + return nei_pos - nei_rep; +} diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h index 42f39096f86ca..ff7e51de3dc9b 100644 --- a/src/common/ieee802_11_common.h +++ b/src/common/ieee802_11_common.h @@ -11,6 +11,8 @@ #include "defs.h" +struct hostapd_hw_modes; + #define MAX_NOF_MB_IES_SUPPORTED 5 struct mb_ies_info { @@ -64,6 +66,24 @@ struct ieee802_11_elems { const u8 *pref_freq_list; const u8 *supp_op_classes; const u8 *rrm_enabled; + const u8 *cag_number; + const u8 *ap_csn; + const u8 *fils_indic; + const u8 *dils; + const u8 *assoc_delay_info; + const u8 *fils_req_params; + const u8 *fils_key_confirm; + const u8 *fils_session; + const u8 *fils_hlp; + const u8 *fils_ip_addr_assign; + const u8 *key_delivery; + const u8 *fils_wrapped_data; + const u8 *fils_pk; + const u8 *fils_nonce; + const u8 *owe_dh; + const u8 *power_capab; + const u8 *roaming_cons_sel; + const u8 *password_id; u8 ssid_len; u8 supp_rates_len; @@ -96,6 +116,20 @@ struct ieee802_11_elems { u8 pref_freq_list_len; u8 supp_op_classes_len; u8 rrm_enabled_len; + u8 cag_number_len; + u8 fils_indic_len; + u8 dils_len; + u8 fils_req_params_len; + u8 fils_key_confirm_len; + u8 fils_hlp_len; + u8 fils_ip_addr_assign_len; + u8 key_delivery_len; + u8 fils_wrapped_data_len; + u8 fils_pk_len; + u8 owe_dh_len; + u8 power_capab_len; + u8 roaming_cons_sel_len; + u8 password_id_len; struct mb_ies_info mb_ies; }; @@ -126,7 +160,8 @@ int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan); enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq, int sec_channel, int vht, u8 *op_class, u8 *channel); -int ieee80211_is_dfs(int freq); +int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes, + u16 num_modes); enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht); int supp_rates_11b_only(struct ieee802_11_elems *elems); @@ -150,7 +185,20 @@ extern const struct oper_class_map global_op_class[]; extern size_t global_op_class_size; const u8 * get_ie(const u8 *ies, size_t len, u8 eid); +const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext); size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len); +struct country_op_class { + u8 country_op_class; + u8 global_op_class; +}; + +u8 country_to_global_op_class(const char *country, u8 op_class); + +const struct oper_class_map * get_oper_class(const char *country, u8 op_class); + +int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep, + size_t nei_rep_len); + #endif /* IEEE802_11_COMMON_H */ diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index d453aec790add..762e731ab022c 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -81,6 +81,9 @@ #define WLAN_AUTH_SHARED_KEY 1 #define WLAN_AUTH_FT 2 #define WLAN_AUTH_SAE 3 +#define WLAN_AUTH_FILS_SK 4 +#define WLAN_AUTH_FILS_SK_PFS 5 +#define WLAN_AUTH_FILS_PK 6 #define WLAN_AUTH_LEAP 128 #define WLAN_AUTH_CHALLENGE_LEN 128 @@ -102,7 +105,7 @@ #define WLAN_CAPABILITY_DELAYED_BLOCK_ACK BIT(14) #define WLAN_CAPABILITY_IMM_BLOCK_ACK BIT(15) -/* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */ +/* Status codes (IEEE Std 802.11-2016, 9.4.1.9, Table 9-46) */ #define WLAN_STATUS_SUCCESS 0 #define WLAN_STATUS_UNSPECIFIED_FAILURE 1 #define WLAN_STATUS_TDLS_WAKEUP_ALTERNATE 2 @@ -119,27 +122,23 @@ #define WLAN_STATUS_AUTH_TIMEOUT 16 #define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17 #define WLAN_STATUS_ASSOC_DENIED_RATES 18 -/* IEEE 802.11b */ #define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19 -#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20 -#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21 -/* IEEE 802.11h */ #define WLAN_STATUS_SPEC_MGMT_REQUIRED 22 #define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23 #define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24 -/* IEEE 802.11g */ #define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25 -#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 26 #define WLAN_STATUS_ASSOC_DENIED_NO_HT 27 #define WLAN_STATUS_R0KH_UNREACHABLE 28 #define WLAN_STATUS_ASSOC_DENIED_NO_PCO 29 -/* IEEE 802.11w */ #define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30 #define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31 #define WLAN_STATUS_UNSPECIFIED_QOS_FAILURE 32 +#define WLAN_STATUS_DENIED_INSUFFICIENT_BANDWIDTH 33 +#define WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS 34 +#define WLAN_STATUS_DENIED_QOS_NOT_SUPPORTED 35 #define WLAN_STATUS_REQUEST_DECLINED 37 #define WLAN_STATUS_INVALID_PARAMETERS 38 -/* IEEE 802.11i */ +#define WLAN_STATUS_REJECTED_WITH_SUGGESTED_CHANGES 39 #define WLAN_STATUS_INVALID_IE 40 #define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41 #define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42 @@ -152,11 +151,13 @@ #define WLAN_STATUS_DEST_STA_NOT_PRESENT 49 #define WLAN_STATUS_DEST_STA_NOT_QOS_STA 50 #define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51 -/* IEEE 802.11r */ #define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52 #define WLAN_STATUS_INVALID_PMKID 53 #define WLAN_STATUS_INVALID_MDIE 54 #define WLAN_STATUS_INVALID_FTIE 55 +#define WLAN_STATUS_REQUESTED_TCLAS_NOT_SUPPORTED 56 +#define WLAN_STATUS_INSUFFICIENT_TCLAS_PROCESSING_RESOURCES 57 +#define WLAN_STATUS_TRY_ANOTHER_BSS 58 #define WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED 59 #define WLAN_STATUS_NO_OUTSTANDING_GAS_REQ 60 #define WLAN_STATUS_GAS_RESP_NOT_RECEIVED 61 @@ -167,16 +168,44 @@ #define WLAN_STATUS_REQ_REFUSED_SSPN 67 #define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68 #define WLAN_STATUS_INVALID_RSNIE 72 +#define WLAN_STATUS_U_APSD_COEX_NOT_SUPPORTED 73 +#define WLAN_STATUS_U_APSD_COEX_MODE_NOT_SUPPORTED 74 +#define WLAN_STATUS_BAD_INTERVAL_WITH_U_APSD_COEX 75 #define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76 #define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77 +#define WLAN_STATUS_CANNOT_FIND_ALT_TBTT 78 #define WLAN_STATUS_TRANSMISSION_FAILURE 79 +#define WLAN_STATUS_REQ_TCLAS_NOT_SUPPORTED 80 +#define WLAN_STATUS_TCLAS_RESOURCES_EXCHAUSTED 81 #define WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION 82 +#define WLAN_STATUS_REJECT_WITH_SCHEDULE 83 +#define WLAN_STATUS_REJECT_NO_WAKEUP_SPECIFIED 84 +#define WLAN_STATUS_SUCCESS_POWER_SAVE_MODE 85 #define WLAN_STATUS_PENDING_ADMITTING_FST_SESSION 86 +#define WLAN_STATUS_PERFORMING_FST_NOW 87 +#define WLAN_STATUS_PENDING_GAP_IN_BA_WINDOW 88 +#define WLAN_STATUS_REJECT_U_PID_SETTING 89 +#define WLAN_STATUS_REFUSED_EXTERNAL_REASON 92 +#define WLAN_STATUS_REFUSED_AP_OUT_OF_MEMORY 93 +#define WLAN_STATUS_REJECTED_EMERGENCY_SERVICE_NOT_SUPPORTED 94 #define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95 +#define WLAN_STATUS_REJECT_DSE_BAND 96 +#define WLAN_STATUS_TCLAS_PROCESSING_TERMINATED 97 +#define WLAN_STATUS_TS_SCHEDULE_CONFLICT 98 #define WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL 99 +#define WLAN_STATUS_MCCAOP_RESERVATION_CONFLICT 100 +#define WLAN_STATUS_MAF_LIMIT_EXCEEDED 101 +#define WLAN_STATUS_MCCA_TRACK_LIMIT_EXCEEDED 102 +#define WLAN_STATUS_DENIED_DUE_TO_SPECTRUM_MANAGEMENT 103 #define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104 - -/* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */ +#define WLAN_STATUS_ENABLEMENT_DENIED 105 +#define WLAN_STATUS_RESTRICTION_FROM_AUTHORIZED_GDB 106 +#define WLAN_STATUS_AUTHORIZATION_DEENABLED 107 +#define WLAN_STATUS_FILS_AUTHENTICATION_FAILURE 112 +#define WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER 113 +#define WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER 123 + +/* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */ #define WLAN_REASON_UNSPECIFIED 1 #define WLAN_REASON_PREV_AUTH_NOT_VALID 2 #define WLAN_REASON_DEAUTH_LEAVING 3 @@ -186,10 +215,9 @@ #define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7 #define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8 #define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 -/* IEEE 802.11h */ #define WLAN_REASON_PWR_CAPABILITY_NOT_VALID 10 #define WLAN_REASON_SUPPORTED_CHANNEL_NOT_VALID 11 -/* IEEE 802.11i */ +#define WLAN_REASON_BSS_TRANSITION_DISASSOC 12 #define WLAN_REASON_INVALID_IE 13 #define WLAN_REASON_MICHAEL_MIC_FAILURE 14 #define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15 @@ -204,9 +232,26 @@ #define WLAN_REASON_CIPHER_SUITE_REJECTED 24 #define WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE 25 #define WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26 -/* IEEE 802.11e */ +#define WLAN_REASON_SSP_REQUESTED_DISASSOC 27 +#define WLAN_REASON_NO_SSP_ROAMING_AGREEMENT 28 +#define WLAN_REASON_BAD_CIPHER_OR_AKM 29 +#define WLAN_REASON_NOT_AUTHORIZED_THIS_LOCATION 30 +#define WLAN_REASON_SERVICE_CHANGE_PRECLUDES_TS 31 +#define WLAN_REASON_UNSPECIFIED_QOS_REASON 32 +#define WLAN_REASON_NOT_ENOUGH_BANDWIDTH 33 #define WLAN_REASON_DISASSOC_LOW_ACK 34 -/* IEEE 802.11s */ +#define WLAN_REASON_EXCEEDED_TXOP 35 +#define WLAN_REASON_STA_LEAVING 36 +#define WLAN_REASON_END_TS_BA_DLS 37 +#define WLAN_REASON_UNKNOWN_TS_BA 38 +#define WLAN_REASON_TIMEOUT 39 +#define WLAN_REASON_PEERKEY_MISMATCH 45 +#define WLAN_REASON_AUTHORIZED_ACCESS_LIMIT_REACHED 46 +#define WLAN_REASON_EXTERNAL_SERVICE_REQUIREMENTS 47 +#define WLAN_REASON_INVALID_FT_ACTION_FRAME_COUNT 48 +#define WLAN_REASON_INVALID_PMKID 49 +#define WLAN_REASON_INVALID_MDE 50 +#define WLAN_REASON_INVALID_FTE 51 #define WLAN_REASON_MESH_PEERING_CANCELLED 52 #define WLAN_REASON_MESH_MAX_PEERS 53 #define WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION 54 @@ -216,20 +261,29 @@ #define WLAN_REASON_MESH_INVALID_GTK 58 #define WLAN_REASON_MESH_INCONSISTENT_PARAMS 59 #define WLAN_REASON_MESH_INVALID_SECURITY_CAP 60 +#define WLAN_REASON_MESH_PATH_ERROR_NO_PROXY_INFO 61 +#define WLAN_REASON_MESH_PATH_ERROR_NO_FORWARDING_INFO 62 +#define WLAN_REASON_MESH_PATH_ERROR_DEST_UNREACHABLE 63 +#define WLAN_REASON_MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS 64 +#define WLAN_REASON_MESH_CHANNEL_SWITCH_REGULATORY_REQ 65 +#define WLAN_REASON_MESH_CHANNEL_SWITCH_UNSPECIFIED 66 -/* Information Element IDs */ +/* Information Element IDs (IEEE Std 802.11-2016, 9.4.2.1, Table 9-77) */ #define WLAN_EID_SSID 0 #define WLAN_EID_SUPP_RATES 1 -#define WLAN_EID_FH_PARAMS 2 #define WLAN_EID_DS_PARAMS 3 #define WLAN_EID_CF_PARAMS 4 #define WLAN_EID_TIM 5 #define WLAN_EID_IBSS_PARAMS 6 #define WLAN_EID_COUNTRY 7 +#define WLAN_EID_REQUEST 10 #define WLAN_EID_BSS_LOAD 11 +#define WLAN_EID_EDCA_PARAM_SET 12 +#define WLAN_EID_TSPEC 13 +#define WLAN_EID_TCLAS 14 +#define WLAN_EID_SCHEDULE 15 #define WLAN_EID_CHALLENGE 16 -/* EIDs defined by IEEE 802.11h - START */ #define WLAN_EID_PWR_CONSTRAINT 32 #define WLAN_EID_PWR_CAPABILITY 33 #define WLAN_EID_TPC_REQUEST 34 @@ -238,50 +292,139 @@ #define WLAN_EID_CHANNEL_SWITCH 37 #define WLAN_EID_MEASURE_REQUEST 38 #define WLAN_EID_MEASURE_REPORT 39 -#define WLAN_EID_QUITE 40 +#define WLAN_EID_QUIET 40 #define WLAN_EID_IBSS_DFS 41 -/* EIDs defined by IEEE 802.11h - END */ #define WLAN_EID_ERP_INFO 42 +#define WLAN_EID_TS_DELAY 43 +#define WLAN_EID_TCLAS_PROCESSING 44 #define WLAN_EID_HT_CAP 45 #define WLAN_EID_QOS 46 #define WLAN_EID_RSN 48 #define WLAN_EID_EXT_SUPP_RATES 50 +#define WLAN_EID_AP_CHANNEL_REPORT 51 #define WLAN_EID_NEIGHBOR_REPORT 52 +#define WLAN_EID_RCPI 53 #define WLAN_EID_MOBILITY_DOMAIN 54 #define WLAN_EID_FAST_BSS_TRANSITION 55 #define WLAN_EID_TIMEOUT_INTERVAL 56 #define WLAN_EID_RIC_DATA 57 +#define WLAN_EID_DSE_REGISTERED_LOCATION 58 #define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59 #define WLAN_EID_EXT_CHANSWITCH_ANN 60 #define WLAN_EID_HT_OPERATION 61 #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62 -#define WLAN_EID_WAPI 68 +#define WLAN_EID_BSS_AVERAGE_ACCESS_DELAY 63 +#define WLAN_EID_ANTENNA 64 +#define WLAN_EID_RSNI 65 +#define WLAN_EID_MEASUREMENT_PILOT_TRANSMISSION 66 +#define WLAN_EID_BSS_AVAILABLE_ADM_CAPA 67 +#define WLAN_EID_BSS_AC_ACCESS_DELAY 68 /* note: also used by WAPI */ #define WLAN_EID_TIME_ADVERTISEMENT 69 #define WLAN_EID_RRM_ENABLED_CAPABILITIES 70 +#define WLAN_EID_MULTIPLE_BSSID 71 #define WLAN_EID_20_40_BSS_COEXISTENCE 72 #define WLAN_EID_20_40_BSS_INTOLERANT 73 #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74 +#define WLAN_EID_RIC_DESCRIPTOR 75 #define WLAN_EID_MMIE 76 +#define WLAN_EID_EVENT_REQUEST 78 +#define WLAN_EID_EVENT_REPORT 79 +#define WLAN_EID_DIAGNOSTIC_REQUEST 80 +#define WLAN_EID_DIAGNOSTIC_REPORT 81 +#define WLAN_EID_LOCATION_PARAMETERS 82 +#define WLAN_EID_NONTRANSMITTED_BSSID_CAPA 83 #define WLAN_EID_SSID_LIST 84 +#define WLAN_EID_MLTIPLE_BSSID_INDEX 85 +#define WLAN_EID_FMS_DESCRIPTOR 86 +#define WLAN_EID_FMS_REQUEST 87 +#define WLAN_EID_FMS_RESPONSE 88 +#define WLAN_EID_QOS_TRAFFIC_CAPABILITY 89 #define WLAN_EID_BSS_MAX_IDLE_PERIOD 90 #define WLAN_EID_TFS_REQ 91 #define WLAN_EID_TFS_RESP 92 #define WLAN_EID_WNMSLEEP 93 +#define WLAN_EID_TIM_BROADCAST_REQUEST 94 +#define WLAN_EID_TIM_BROADCAST_RESPONSE 95 +#define WLAN_EID_COLLOCATED_INTERFERENCE_REPORT 96 +#define WLAN_EID_CHANNEL_USAGE 97 #define WLAN_EID_TIME_ZONE 98 +#define WLAN_EID_DMS_REQUEST 99 +#define WLAN_EID_DMS_RESPONSE 100 #define WLAN_EID_LINK_ID 101 +#define WLAN_EID_WAKEUP_SCHEDULE 102 +#define WLAN_EID_CHANNEL_SWITCH_TIMING 104 +#define WLAN_EID_PTI_CONTROL 105 +#define WLAN_EID_TPU_BUFFER_STATUS 106 #define WLAN_EID_INTERWORKING 107 #define WLAN_EID_ADV_PROTO 108 +#define WLAN_EID_EXPEDITED_BANDWIDTH_REQ 109 #define WLAN_EID_QOS_MAP_SET 110 #define WLAN_EID_ROAMING_CONSORTIUM 111 +#define WLAN_EID_EMERGENCY_ALERT_ID 112 #define WLAN_EID_MESH_CONFIG 113 #define WLAN_EID_MESH_ID 114 +#define WLAN_EID_MESH_LINK_METRIC_REPORT 115 +#define WLAN_EID_CONGESTION_NOTIFICATION 116 #define WLAN_EID_PEER_MGMT 117 +#define WLAN_EID_MESH_CHANNEL_SWITCH_PARAMETERS 118 +#define WLAN_EID_MESH_AWAKE_WINDOW 119 +#define WLAN_EID_BEACON_TIMING 120 +#define WLAN_EID_MCCAOP_SETUP_REQUEST 121 +#define WLAN_EID_MCCAOP_SETUP_REPLY 122 +#define WLAN_EID_MCCAOP_ADVERTISEMENT 123 +#define WLAN_EID_MCCAOP_TEARDOWN 124 +#define WLAN_EID_GANN 125 +#define WLAN_EID_RANN 126 #define WLAN_EID_EXT_CAPAB 127 +#define WLAN_EID_PREQ 130 +#define WLAN_EID_PREP 131 +#define WLAN_EID_PERR 132 +#define WLAN_EID_PXU 137 +#define WLAN_EID_PXUC 138 #define WLAN_EID_AMPE 139 #define WLAN_EID_MIC 140 +#define WLAN_EID_DESTINATION_URI 141 +#define WLAN_EID_U_APSD_COEX 142 +#define WLAN_EID_DMG_WAKEUP_SCHEDULE 143 +#define WLAN_EID_EXTENDED_SCHEDULE 144 +#define WLAN_EID_STA_AVAILABILITY 145 +#define WLAN_EID_DMG_TSPEC 146 +#define WLAN_EID_NEXT_DMG_ATI 147 +#define WLAN_EID_DMG_CAPABILITIES 148 +#define WLAN_EID_DMG_OPERATION 151 +#define WLAN_EID_DMG_BSS_PARAMETER_CHANGE 152 +#define WLAN_EID_DMG_BEAM_REFINEMENT 153 +#define WLAN_EID_CHANNEL_MEASUREMENT_FEEDBACK 154 #define WLAN_EID_CCKM 156 +#define WLAN_EID_AWAKE_WINDOW 157 #define WLAN_EID_MULTI_BAND 158 +#define WLAN_EID_ADDBA_EXTENSION 159 +#define WLAN_EID_NEXTPCP_LIST 160 +#define WLAN_EID_PCP_HANDOVER 161 +#define WLAN_EID_DMG_LINK_MARGIN 162 +#define WLAN_EID_SWITCHING_STREAM 163 #define WLAN_EID_SESSION_TRANSITION 164 +#define WLAN_EID_DYNAMIC_TONE_PAIRING_REPORT 165 +#define WLAN_EID_CLUSTER_REPORT 166 +#define WLAN_EID_REPLAY_CAPABILITIES 167 +#define WLAN_EID_RELAY_TRANSFER_PARAM_SET 168 +#define WLAN_EID_BEAMLINK_MAINTENANCE 169 +#define WLAN_EID_MULTIPLE_MAC_SUBLAYERS 170 +#define WLAN_EID_U_PID 171 +#define WLAN_EID_DMG_LINK_ADAPTATION_ACK 172 +#define WLAN_EID_MCCAOP_ADVERTISEMENT_OVERVIEW 174 +#define WLAN_EID_QUIET_PERIOD_REQUEST 175 +#define WLAN_EID_QUIET_PERIOD_RESPONSE 177 +#define WLAN_EID_QMF_POLICY 181 +#define WLAN_EID_ECAPC_POLICY 182 +#define WLAN_EID_CLUSTER_TIME_OFFSET 183 +#define WLAN_EID_INTRA_ACCESS_CATEGORY_PRIORITY 184 +#define WLAN_EID_SCS_DESCRIPTOR 185 +#define WLAN_EID_QLOAD_REPORT 186 +#define WLAN_EID_HCCA_TXOP_UPDATE_COUNT 187 +#define WLAN_EID_HIGHER_LAYER_STREAM_ID 188 +#define WLAN_EID_GCR_GROUP_ADDRESS 189 +#define WLAN_EID_ANTENNA_SECTOR_ID_PATTERN 190 #define WLAN_EID_VHT_CAP 191 #define WLAN_EID_VHT_OPERATION 192 #define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193 @@ -291,10 +434,42 @@ #define WLAN_EID_VHT_AID 197 #define WLAN_EID_VHT_QUIET_CHANNEL 198 #define WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION 199 +#define WLAN_EID_UPSIM 200 +#define WLAN_EID_REDUCED_NEIGHBOR_REPORT 201 +#define WLAN_EID_TVHT_OPERATION 202 +#define WLAN_EID_DEVICE_LOCATION 204 +#define WLAN_EID_WHITE_SPACE_MAP 205 +#define WLAN_EID_FTM_PARAMETERS 206 #define WLAN_EID_VENDOR_SPECIFIC 221 - - -/* Action frame categories (IEEE 802.11-2007, 7.3.1.11, Table 7-24) */ +#define WLAN_EID_CAG_NUMBER 237 +#define WLAN_EID_AP_CSN 239 +#define WLAN_EID_FILS_INDICATION 240 +#define WLAN_EID_DILS 241 +#define WLAN_EID_FRAGMENT 242 +#define WLAN_EID_EXTENSION 255 + +/* Element ID Extension (EID 255) values */ +#define WLAN_EID_EXT_ASSOC_DELAY_INFO 1 +#define WLAN_EID_EXT_FILS_REQ_PARAMS 2 +#define WLAN_EID_EXT_FILS_KEY_CONFIRM 3 +#define WLAN_EID_EXT_FILS_SESSION 4 +#define WLAN_EID_EXT_FILS_HLP_CONTAINER 5 +#define WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN 6 +#define WLAN_EID_EXT_KEY_DELIVERY 7 +#define WLAN_EID_EXT_FILS_WRAPPED_DATA 8 +#define WLAN_EID_EXT_FTM_SYNC_INFO 9 +#define WLAN_EID_EXT_EXTENDED_REQUEST 10 +#define WLAN_EID_EXT_ESTIMATED_SERVICE_PARAMS 11 +#define WLAN_EID_EXT_FILS_PUBLIC_KEY 12 +#define WLAN_EID_EXT_FILS_NONCE 13 +#define WLAN_EID_EXT_FUTURE_CHANNEL_GUIDANCE 14 +#define WLAN_EID_EXT_OWE_DH_PARAM 32 +#define WLAN_EID_EXT_PASSWORD_IDENTIFIER 33 +#define WLAN_EID_EXT_HE_CAPABILITIES 35 +#define WLAN_EID_EXT_HE_OPERATION 36 + + +/* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */ #define WLAN_ACTION_SPECTRUM_MGMT 0 #define WLAN_ACTION_QOS 1 #define WLAN_ACTION_DLS 2 @@ -308,21 +483,59 @@ #define WLAN_ACTION_WNM 10 #define WLAN_ACTION_UNPROTECTED_WNM 11 #define WLAN_ACTION_TDLS 12 +#define WLAN_ACTION_MESH 13 +#define WLAN_ACTION_MULTIHOP 14 #define WLAN_ACTION_SELF_PROTECTED 15 +#define WLAN_ACTION_DMG 16 #define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */ #define WLAN_ACTION_FST 18 +#define WLAN_ACTION_ROBUST_AV_STREAMING 19 +#define WLAN_ACTION_UNPROTECTED_DMG 20 +#define WLAN_ACTION_VHT 21 +#define WLAN_ACTION_FILS 26 +#define WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED 126 #define WLAN_ACTION_VENDOR_SPECIFIC 127 +/* Note: 128-255 used to report errors by setting category | 0x80 */ -/* Public action codes */ +/* Public action codes (IEEE Std 802.11-2016, 9.6.8.1, Table 9-307) */ #define WLAN_PA_20_40_BSS_COEX 0 +#define WLAN_PA_DSE_ENABLEMENT 1 +#define WLAN_PA_DSE_DEENABLEMENT 2 +#define WLAN_PA_DSE_REG_LOCATION_ANNOUNCE 3 +#define WLAN_PA_EXT_CHANNEL_SWITCH_ANNOUNCE 4 +#define WLAN_PA_DSE_MEASUREMENT_REQ 5 +#define WLAN_PA_DSE_MEASUREMENT_RESP 6 +#define WLAN_PA_MEASUREMENT_PILOT 7 +#define WLAN_PA_DSE_POWER_CONSTRAINT 8 #define WLAN_PA_VENDOR_SPECIFIC 9 #define WLAN_PA_GAS_INITIAL_REQ 10 #define WLAN_PA_GAS_INITIAL_RESP 11 #define WLAN_PA_GAS_COMEBACK_REQ 12 #define WLAN_PA_GAS_COMEBACK_RESP 13 #define WLAN_TDLS_DISCOVERY_RESPONSE 14 - -/* Protected Dual of Public Action frames */ +#define WLAN_PA_LOCATION_TRACK_NOTIFICATION 15 +#define WLAN_PA_QAB_REQUEST_FRAME 16 +#define WLAN_PA_QAB_RESPONSE_FRAME 17 +#define WLAN_PA_QMF_POLICY 18 +#define WLAN_PA_QMF_POLICY_CHANGE 19 +#define WLAN_PA_QLOAD_REQUEST 20 +#define WLAN_PA_QLOAD_REPORT 21 +#define WLAN_PA_HCCA_TXOP_ADVERTISEMENT 22 +#define WLAN_PA_HCCA_TXOP_RESPONSE 23 +#define WLAN_PA_PUBLIC_KEY 24 +#define WLAN_PA_CHANNEL_AVAILABILITY_QUERY 25 +#define WLAN_PA_CHANNEL_SCHEDULE_MANAGEMENT 26 +#define WLAN_PA_CONTACT_VERIFICATION_SIGNAL 27 +#define WLAN_PA_GDD_ENABLEMENT_REQ 28 +#define WLAN_PA_GDD_ENABLEMENT_RESP 29 +#define WLAN_PA_NETWORK_CHANNEL_CONTROL 30 +#define WLAN_PA_WHITE_SPACE_MAP_ANNOUNCEMENT 31 +#define WLAN_PA_FTM_REQUEST 32 +#define WLAN_PA_FTM 33 +#define WLAN_PA_FILS_DISCOVERY 34 + +/* Protected Dual of Public Action frames (IEEE Std 802.11-2016, 9.6.11, + * Table 9-332) */ #define WLAN_PROT_DSE_ENABLEMENT 1 #define WLAN_PROT_DSE_DEENABLEMENT 2 #define WLAN_PROT_EXT_CSA 4 @@ -334,6 +547,21 @@ #define WLAN_PROT_GAS_INITIAL_RESP 11 #define WLAN_PROT_GAS_COMEBACK_REQ 12 #define WLAN_PROT_GAS_COMEBACK_RESP 13 +#define WLAN_PROT_QAB_REQUEST_FRAME 16 +#define WLAN_PROT_QAB_RESPONSE_FRAME 17 +#define WLAN_PROT_QMF_POLICY 18 +#define WLAN_PROT_QMF_POLICY_CHANGE 19 +#define WLAN_PROT_QLOAD_REQUEST 20 +#define WLAN_PROT_QLOAD_REPORT 21 +#define WLAN_PROT_HCCA_TXOP_ADVERTISEMENT 22 +#define WLAN_PROT_HCCA_TXOP_RESPONSE 23 +#define WLAN_PROT_CHANNEL_AVAILABILITY_QUERY 25 +#define WLAN_PROT_CHANNEL_SCHEDULE_MANAGEMENT 26 +#define WLAN_PROT_CONTACT_VERIFICATION_SIGNAL 27 +#define WLAN_PROT_GDD_ENABLEMENT_REQ 28 +#define WLAN_PROT_GDD_ENABLEMENT_RESP 29 +#define WLAN_PROT_NETWORK_CHANNEL_CONTROL 30 +#define WLAN_PROT_WHITE_SPACE_MAP_ANNOUNCEMENT 31 /* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */ #define WLAN_SA_QUERY_REQUEST 0 @@ -362,10 +590,14 @@ #define WLAN_RRM_NEIGHBOR_REPORT_REQUEST 4 #define WLAN_RRM_NEIGHBOR_REPORT_RESPONSE 5 -/* Radio Measurement capabilities (from RRM Capabilities IE) */ +/* Radio Measurement capabilities (from RM Enabled Capabilities element) + * IEEE Std 802.11-2016, 9.4.2.45, Table 9-157 */ /* byte 1 (out of 5) */ #define WLAN_RRM_CAPS_LINK_MEASUREMENT BIT(0) #define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1) +#define WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE BIT(4) +#define WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE BIT(5) +#define WLAN_RRM_CAPS_BEACON_REPORT_TABLE BIT(6) /* byte 2 (out of 5) */ #define WLAN_RRM_CAPS_LCI_MEASUREMENT BIT(4) /* byte 5 (out of 5) */ @@ -398,16 +630,18 @@ #define INTERWORKING_ANT_TEST 6 #define INTERWORKING_ANT_WILDCARD 15 -/* Advertisement Protocol ID definitions (IEEE Std 802.11u-2011) */ +/* Advertisement Protocol ID definitions (IEEE Std 802.11-2016, Table 9-215) */ enum adv_proto_id { ACCESS_NETWORK_QUERY_PROTOCOL = 0, MIH_INFO_SERVICE = 1, MIH_CMD_AND_EVENT_DISCOVERY = 2, EMERGENCY_ALERT_SYSTEM = 3, + REGISTERED_LOCATION_QUERY_PROTO = 4, ADV_PROTO_VENDOR_SPECIFIC = 221 }; -/* Access Network Query Protocol info ID definitions (IEEE Std 802.11u-2011) */ +/* Access Network Query Protocol info ID definitions (IEEE Std 802.11-2016, + * Table 9-271; P802.11ai) */ enum anqp_info_id { ANQP_QUERY_LIST = 256, ANQP_CAPABILITY_LIST = 257, @@ -426,9 +660,14 @@ enum anqp_info_id { ANQP_TDLS_CAPABILITY = 270, ANQP_EMERGENCY_NAI = 271, ANQP_NEIGHBOR_REPORT = 272, + ANQP_QUERY_AP_LIST = 273, + ANQP_AP_LIST_RESPONSE = 274, + ANQP_FILS_REALM_INFO = 275, + ANQP_CAG = 276, ANQP_VENUE_URL = 277, ANQP_ADVICE_OF_CHARGE = 278, ANQP_LOCAL_CONTENT = 279, + ANQP_NETWORK_AUTH_TYPE_TIMESTAMP = 280, ANQP_VENDOR_SPECIFIC = 56797 }; @@ -505,6 +744,11 @@ enum lci_req_subelem { LCI_REQ_SUBELEM_MAX_AGE = 4, }; +#define FILS_NONCE_LEN 16 +#define FILS_SESSION_LEN 8 +#define FILS_CACHE_ID_LEN 2 +#define FILS_MAX_KEY_AUTH_LEN 48 + #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ @@ -678,6 +922,16 @@ struct ieee80211_mgmt { u8 variable[]; } STRUCT_PACKED bss_tm_query; struct { + u8 action; /* 11 */ + u8 dialog_token; + u8 req_info; + } STRUCT_PACKED coloc_intf_req; + struct { + u8 action; /* 12 */ + u8 dialog_token; + u8 variable[]; + } STRUCT_PACKED coloc_intf_report; + struct { u8 action; /* 15 */ u8 variable[]; } STRUCT_PACKED slf_prot_action; @@ -887,6 +1141,7 @@ struct ieee80211_ampe_ie { #define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ((u32) BIT(2)) #define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ ((u32) BIT(3)) #define VHT_CAP_SUPP_CHAN_WIDTH_MASK ((u32) BIT(2) | BIT(3)) +#define VHT_CAP_SUPP_CHAN_WIDTH_MASK_SHIFT 2 #define VHT_CAP_RXLDPC ((u32) BIT(4)) #define VHT_CAP_SHORT_GI_80 ((u32) BIT(5)) #define VHT_CAP_SHORT_GI_160 ((u32) BIT(6)) @@ -953,6 +1208,8 @@ struct ieee80211_ampe_ie { #define OSEN_IE_VENDOR_TYPE 0x506f9a12 #define MBO_IE_VENDOR_TYPE 0x506f9a16 #define MBO_OUI_TYPE 22 +#define OWE_IE_VENDOR_TYPE 0x506f9a1c +#define OWE_OUI_TYPE 28 #define WMM_OUI_TYPE 2 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0 @@ -1072,6 +1329,7 @@ enum wmm_ac { #define HS20_INDICATION_OUI_TYPE 16 #define HS20_ANQP_OUI_TYPE 17 #define HS20_OSEN_OUI_TYPE 18 +#define HS20_ROAMING_CONS_SEL_OUI_TYPE 29 #define HS20_STYPE_QUERY_LIST 1 #define HS20_STYPE_CAPABILITY_LIST 2 #define HS20_STYPE_OPERATOR_FRIENDLY_NAME 3 @@ -1082,21 +1340,27 @@ enum wmm_ac { #define HS20_STYPE_OSU_PROVIDERS_LIST 8 #define HS20_STYPE_ICON_REQUEST 10 #define HS20_STYPE_ICON_BINARY_FILE 11 +#define HS20_STYPE_OPERATOR_ICON_METADATA 12 +#define HS20_STYPE_OSU_PROVIDERS_NAI_LIST 13 #define HS20_DGAF_DISABLED 0x01 #define HS20_PPS_MO_ID_PRESENT 0x02 #define HS20_ANQP_DOMAIN_ID_PRESENT 0x04 +#ifndef HS20_VERSION #define HS20_VERSION 0x10 /* Release 2 */ +#endif /* HS20_VERSION */ /* WNM-Notification WFA vendors specific subtypes */ #define HS20_WNM_SUB_REM_NEEDED 0 #define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1 +#define HS20_WNM_T_C_ACCEPTANCE 2 #define HS20_DEAUTH_REASON_CODE_BSS 0 #define HS20_DEAUTH_REASON_CODE_ESS 1 /* MBO v0.0_r19, 4.2: MBO Attributes */ /* Table 4-5: MBO Attributes */ +/* OCE v0.0.10, Table 4-3: OCE Attributes */ enum mbo_attr_id { MBO_ATTR_ID_AP_CAPA_IND = 1, MBO_ATTR_ID_NON_PREF_CHAN_REPORT = 2, @@ -1106,6 +1370,10 @@ enum mbo_attr_id { MBO_ATTR_ID_TRANSITION_REASON = 6, MBO_ATTR_ID_TRANSITION_REJECT_REASON = 7, MBO_ATTR_ID_ASSOC_RETRY_DELAY = 8, + OCE_ATTR_ID_CAPA_IND = 101, + OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT = 102, + OCE_ATTR_ID_REDUCED_WAN_METRICS = 103, + OCE_ATTR_ID_RNR_COMPLETENESS = 104, }; /* MBO v0.0_r19, 4.2.1: MBO AP Capability Indication Attribute */ @@ -1180,9 +1448,17 @@ enum wfa_wnm_notif_subelem_id { WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA = 3, }; -/* MBO v0.0_r25, 4.3: MBO ANQP-elements */ +/* MBO v0.0_r27, 4.3: MBO ANQP-elements */ #define MBO_ANQP_OUI_TYPE 0x12 -#define MBO_ANQP_SUBTYPE_CELL_CONN_PREF 1 +#define MBO_ANQP_SUBTYPE_QUERY_LIST 1 +#define MBO_ANQP_SUBTYPE_CELL_CONN_PREF 2 +#define MAX_MBO_ANQP_SUBTYPE MBO_ANQP_SUBTYPE_CELL_CONN_PREF + +/* OCE v0.0.10, 4.2.1: OCE Capability Indication Attribute */ +#define OCE_RELEASE 1 +#define OCE_RELEASE_MASK (BIT(0) | BIT(1) | BIT(2)) +#define OCE_IS_STA_CFON BIT(3) +#define OCE_IS_NON_OCE_AP_PRESENT BIT(4) /* Wi-Fi Direct (P2P) */ @@ -1331,7 +1607,9 @@ enum wifi_display_subelem { WFD_SUBELEM_COUPLED_SINK = 6, WFD_SUBELEM_EXT_CAPAB = 7, WFD_SUBELEM_LOCAL_IP_ADDRESS = 8, - WFD_SUBELEM_SESSION_INFO = 9 + WFD_SUBELEM_SESSION_INFO = 9, + WFD_SUBELEM_MAC_INFO = 10, + WFD_SUBELEM_R2_DEVICE_INFO = 11, }; /* 802.11s */ @@ -1363,41 +1641,6 @@ enum plink_action_field { #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */ -/* cipher suite selectors */ -#define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00 -#define WLAN_CIPHER_SUITE_WEP40 0x000FAC01 -#define WLAN_CIPHER_SUITE_TKIP 0x000FAC02 -/* reserved: 0x000FAC03 */ -#define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 -#define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 -#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 -#define WLAN_CIPHER_SUITE_NO_GROUP_ADDR 0x000FAC07 -#define WLAN_CIPHER_SUITE_GCMP 0x000FAC08 -#define WLAN_CIPHER_SUITE_GCMP_256 0x000FAC09 -#define WLAN_CIPHER_SUITE_CCMP_256 0x000FAC0A -#define WLAN_CIPHER_SUITE_BIP_GMAC_128 0x000FAC0B -#define WLAN_CIPHER_SUITE_BIP_GMAC_256 0x000FAC0C -#define WLAN_CIPHER_SUITE_BIP_CMAC_256 0x000FAC0D - -#define WLAN_CIPHER_SUITE_SMS4 0x00147201 - -#define WLAN_CIPHER_SUITE_CKIP 0x00409600 -#define WLAN_CIPHER_SUITE_CKIP_CMIC 0x00409601 -#define WLAN_CIPHER_SUITE_CMIC 0x00409602 -#define WLAN_CIPHER_SUITE_KRK 0x004096FF /* for nl80211 use only */ - -/* AKM suite selectors */ -#define WLAN_AKM_SUITE_8021X 0x000FAC01 -#define WLAN_AKM_SUITE_PSK 0x000FAC02 -#define WLAN_AKM_SUITE_FT_8021X 0x000FAC03 -#define WLAN_AKM_SUITE_FT_PSK 0x000FAC04 -#define WLAN_AKM_SUITE_8021X_SHA256 0x000FAC05 -#define WLAN_AKM_SUITE_PSK_SHA256 0x000FAC06 -#define WLAN_AKM_SUITE_8021X_SUITE_B 0x000FAC11 -#define WLAN_AKM_SUITE_8021X_SUITE_B_192 0x000FAC12 -#define WLAN_AKM_SUITE_CCKM 0x00409600 -#define WLAN_AKM_SUITE_OSEN 0x506f9a01 - /* IEEE 802.11v - WNM Action field values */ enum wnm_action { @@ -1559,6 +1802,102 @@ struct rrm_link_measurement_report { u8 variable[0]; } STRUCT_PACKED; +/* IEEE Std 802.11-2016, 9.4.2.21 - Measurement Request element */ +struct rrm_measurement_request_element { + u8 eid; /* Element ID */ + u8 len; /* Length */ + u8 token; /* Measurement Token */ + u8 mode; /* Measurement Request Mode */ + u8 type; /* Measurement Type */ + u8 variable[0]; /* Measurement Request */ +} STRUCT_PACKED; + +/* IEEE Std 802.11-2016, Figure 9-148 - Measurement Request Mode field */ +#define MEASUREMENT_REQUEST_MODE_PARALLEL BIT(0) +#define MEASUREMENT_REQUEST_MODE_ENABLE BIT(1) +#define MEASUREMENT_REQUEST_MODE_REQUEST BIT(2) +#define MEASUREMENT_REQUEST_MODE_REPORT BIT(3) +#define MEASUREMENT_REQUEST_MODE_DURATION_MANDATORY BIT(4) + +/* IEEE Std 802.11-2016, 9.4.2.21.7 - Beacon request */ +struct rrm_measurement_beacon_request { + u8 oper_class; /* Operating Class */ + u8 channel; /* Channel Number */ + le16 rand_interval; /* Randomization Interval (in TUs) */ + le16 duration; /* Measurement Duration (in TUs) */ + u8 mode; /* Measurement Mode */ + u8 bssid[ETH_ALEN]; /* BSSID */ + u8 variable[0]; /* Optional Subelements */ +} STRUCT_PACKED; + +/* + * IEEE Std 802.11-2016, Table 9-87 - Measurement Mode definitions for Beacon + * request + */ +enum beacon_report_mode { + BEACON_REPORT_MODE_PASSIVE = 0, + BEACON_REPORT_MODE_ACTIVE = 1, + BEACON_REPORT_MODE_TABLE = 2, +}; + +/* IEEE Std 802.11-2016, Table 9-88 - Beacon Request subelement IDs */ +#define WLAN_BEACON_REQUEST_SUBELEM_SSID 0 +#define WLAN_BEACON_REQUEST_SUBELEM_INFO 1 /* Beacon Reporting */ +#define WLAN_BEACON_REQUEST_SUBELEM_DETAIL 2 /* Reporting Detail */ +#define WLAN_BEACON_REQUEST_SUBELEM_REQUEST 10 +#define WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL 51 /* AP Channel Report */ +#define WLAN_BEACON_REQUEST_SUBELEM_VENDOR 221 + +/* + * IEEE Std 802.11-2016, Table 9-90 - Reporting Detail values + */ +enum beacon_report_detail { + /* No fixed-length fields or elements */ + BEACON_REPORT_DETAIL_NONE = 0, + /* All fixed-length fields and any requested elements in the Request + * element if present */ + BEACON_REPORT_DETAIL_REQUESTED_ONLY = 1, + /* All fixed-length fields and elements (default, used when Reporting + * Detail subelement is not included in a Beacon request) */ + BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS = 2, +}; + +/* IEEE Std 802.11-2016, 9.4.2.22 - Measurement Report element */ +struct rrm_measurement_report_element { + u8 eid; /* Element ID */ + u8 len; /* Length */ + u8 token; /* Measurement Token */ + u8 mode; /* Measurement Report Mode */ + u8 type; /* Measurement Type */ + u8 variable[0]; /* Measurement Report */ +} STRUCT_PACKED; + +/* IEEE Std 802.11-2016, Figure 9-192 - Measurement Report Mode field */ +#define MEASUREMENT_REPORT_MODE_ACCEPT 0 +#define MEASUREMENT_REPORT_MODE_REJECT_LATE BIT(0) +#define MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE BIT(1) +#define MEASUREMENT_REPORT_MODE_REJECT_REFUSED BIT(2) + +/* IEEE Std 802.11-2016, 9.4.2.22.7 - Beacon report */ +struct rrm_measurement_beacon_report { + u8 op_class; /* Operating Class */ + u8 channel; /* Channel Number */ + le64 start_time; /* Actual Measurement Start Time + * (in TSF of the BSS requesting the measurement) */ + le16 duration; /* in TUs */ + u8 report_info; /* Reported Frame Information */ + u8 rcpi; /* RCPI */ + u8 rsni; /* RSNI */ + u8 bssid[ETH_ALEN]; /* BSSID */ + u8 antenna_id; /* Antenna ID */ + le32 parent_tsf; /* Parent TSF */ + u8 variable[0]; /* Optional Subelements */ +} STRUCT_PACKED; + +/* IEEE Std 802.11-2016, Table 9-112 - Beacon report Subelement IDs */ +#define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY 1 +#define WLAN_BEACON_REPORT_SUBELEM_VENDOR 221 + /* IEEE Std 802.11ad-2012 - Multi-band element */ struct multi_band_ie { u8 eid; /* WLAN_EID_MULTI_BAND */ @@ -1660,4 +1999,55 @@ enum nr_chan_width { NR_CHAN_WIDTH_80P80 = 4, }; +struct ieee80211_he_capabilities { + u8 he_mac_capab_info[5]; + u8 he_phy_capab_info[9]; + u8 he_txrx_mcs_support[12]; /* TODO: 4, 8, or 12 octets */ + /* PPE Thresholds (optional) */ +} STRUCT_PACKED; + +struct ieee80211_he_operation { + u32 he_oper_params; + u8 he_mcs_nss_set[2]; + u8 vht_op_info_chwidth; + u8 vht_op_info_chan_center_freq_seg0_idx; + u8 vht_op_info_chan_center_freq_seg1_idx; + /* Followed by conditional MaxBSSID Indicator subfield (u8) */ +} STRUCT_PACKED; + +/* HE Capabilities Information defines */ +#define HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX 3 +#define HE_PHYCAP_SU_BEAMFORMER_CAPAB ((u8) BIT(7)) +#define HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX 4 +#define HE_PHYCAP_SU_BEAMFORMEE_CAPAB ((u8) BIT(0)) +#define HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX 4 +#define HE_PHYCAP_MU_BEAMFORMER_CAPAB ((u8) BIT(1)) + +/* HE Operation defines */ +#define HE_OPERATION_BSS_COLOR_MASK ((u32) (BIT(0) | BIT(1) | \ + BIT(2) | BIT(3) | \ + BIT(4) | BIT(5))) +#define HE_OPERATION_DFLT_PE_DURATION_MASK ((u32) (BIT(6) | BIT(7) | \ + BIT(8))) +#define HE_OPERATION_DFLT_PE_DURATION_OFFSET 6 +#define HE_OPERATION_TWT_REQUIRED ((u32) BIT(9)) +#define HE_OPERATION_RTS_THRESHOLD_MASK ((u32) (BIT(10) | BIT(11) | \ + BIT(12) | BIT(13) | \ + BIT(14) | BIT(15) | \ + BIT(16) | BIT(17) | \ + BIT(18) | BIT(19))) +#define HE_OPERATION_RTS_THRESHOLD_OFFSET 10 +#define HE_OPERATION_PARTIAL_BSS_COLOR ((u32) BIT(20)) +#define HE_OPERATION_MAX_BSSID_INDICATOR_MASK ((u32) (BIT(21) | BIT(22) | \ + BIT(23) | BIT(24) | \ + BIT(25) | BIT(26) | \ + BIT(27) | BIT(28))) +#define HE_OPERATION_MAX_BSSID_INDICATOR_OFFSET 21 +#define HE_OPERATION_TX_BSSID_INDICATOR ((u32) BIT(29)) +#define HE_OPERATION_BSS_COLOR_DISABLED ((u32) BIT(30)) +#define HE_OPERATION_BSS_DUAL_BEACON ((u32) BIT(31)) + +/* DPP Public Action frame identifiers - OUI_WFA */ +#define DPP_OUI_TYPE 0x1A + #endif /* IEEE802_11_DEFS_H */ diff --git a/src/common/ieee802_1x_defs.h b/src/common/ieee802_1x_defs.h index a0c1d1bfafc4c..e7acff108eb36 100644 --- a/src/common/ieee802_1x_defs.h +++ b/src/common/ieee802_1x_defs.h @@ -12,6 +12,8 @@ #define CS_ID_LEN 8 #define CS_ID_GCM_AES_128 0x0080020001000001ULL #define CS_NAME_GCM_AES_128 "GCM-AES-128" +#define CS_ID_GCM_AES_256 0x0080c20001000002ULL +#define CS_NAME_GCM_AES_256 "GCM-AES-256" enum macsec_policy { /** @@ -25,6 +27,12 @@ enum macsec_policy { * Disabled MACsec - do not secure sessions. */ DO_NOT_SECURE, + + /** + * Should secure sessions, and try to use encryption. + * Like @SHOULD_SECURE, this follows the key server's decision. + */ + SHOULD_ENCRYPT, }; diff --git a/src/common/privsep_commands.h b/src/common/privsep_commands.h index 8dff30382b605..b85c6c347a797 100644 --- a/src/common/privsep_commands.h +++ b/src/common/privsep_commands.h @@ -9,6 +9,7 @@ #ifndef PRIVSEP_COMMANDS_H #define PRIVSEP_COMMANDS_H +#include "drivers/driver.h" #include "common/ieee802_11_defs.h" enum privsep_cmd { @@ -29,8 +30,17 @@ enum privsep_cmd { PRIVSEP_CMD_AUTHENTICATE, }; -struct privsep_cmd_authenticate -{ +#define PRIVSEP_MAX_SCAN_FREQS 50 + +struct privsep_cmd_scan { + unsigned int num_ssids; + u8 ssids[WPAS_MAX_SCAN_SSIDS][32]; + u8 ssid_lens[WPAS_MAX_SCAN_SSIDS]; + unsigned int num_freqs; + u16 freqs[PRIVSEP_MAX_SCAN_FREQS]; +}; + +struct privsep_cmd_authenticate { int freq; u8 bssid[ETH_ALEN]; u8 ssid[SSID_MAX_LEN]; @@ -42,13 +52,12 @@ struct privsep_cmd_authenticate int wep_tx_keyidx; int local_state_change; int p2p; - size_t sae_data_len; + size_t auth_data_len; /* followed by ie_len bytes of ie */ - /* followed by sae_data_len bytes of sae_data */ + /* followed by auth_data_len bytes of auth_data */ }; -struct privsep_cmd_associate -{ +struct privsep_cmd_associate { u8 bssid[ETH_ALEN]; u8 ssid[SSID_MAX_LEN]; size_t ssid_len; @@ -64,8 +73,7 @@ struct privsep_cmd_associate /* followed by wpa_ie_len bytes of wpa_ie */ }; -struct privsep_cmd_set_key -{ +struct privsep_cmd_set_key { int alg; u8 addr[ETH_ALEN]; int key_idx; @@ -84,7 +92,6 @@ enum privsep_event { PRIVSEP_EVENT_MICHAEL_MIC_FAILURE, PRIVSEP_EVENT_INTERFACE_STATUS, PRIVSEP_EVENT_PMKID_CANDIDATE, - PRIVSEP_EVENT_STKSTART, PRIVSEP_EVENT_FT_RESPONSE, PRIVSEP_EVENT_RX_EAPOL, PRIVSEP_EVENT_SCAN_STARTED, diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h index adaec890b58de..7c75d0804553d 100644 --- a/src/common/qca-vendor.h +++ b/src/common/qca-vendor.h @@ -1,6 +1,7 @@ /* * Qualcomm Atheros OUI and vendor specific assignments - * Copyright (c) 2014-2015, Qualcomm Atheros, Inc. + * Copyright (c) 2014-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. @@ -49,7 +50,10 @@ enum qca_radiotap_vendor_ids { * * @QCA_NL80211_VENDOR_SUBCMD_NAN: NAN command/event which is used to pass * NAN Request/Response and NAN Indication messages. These messages are - * interpreted between the framework and the firmware component. + * interpreted between the framework and the firmware component. While + * sending the command from userspace to the driver, payload is not + * encapsulated inside any attribute. Attribute QCA_WLAN_VENDOR_ATTR_NAN + * is used when receiving vendor events in userspace from the driver. * * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY: Set key operation that can be * used to configure PMK to the driver even when not connected. This can @@ -90,6 +94,75 @@ enum qca_radiotap_vendor_ids { * which supports DFS offloading, to indicate a radar pattern has been * detected. The channel is now unusable. * + * @QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET: Get the feature bitmap + * based on enum wifi_logger_supported_features. Attributes defined in + * enum qca_wlan_vendor_attr_get_logger_features. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_RING_DATA: Get the ring data from a particular + * logger ring, QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID is passed as the + * attribute for this command. Attributes defined in + * enum qca_wlan_vendor_attr_wifi_logger_start. + * + * @QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES: Get the supported TDLS + * capabilities of the driver, parameters includes the attributes defined + * in enum qca_wlan_vendor_attr_tdls_get_capabilities. + * + * @QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS: Vendor command used to offload + * sending of certain periodic IP packet to firmware, attributes defined in + * enum qca_wlan_vendor_attr_offloaded_packets. + * + * @QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI: Command used to configure RSSI + * monitoring, defines min and max RSSI which are configured for RSSI + * monitoring. Also used to notify the RSSI breach and provides the BSSID + * and RSSI value that was breached. Attributes defined in + * enum qca_wlan_vendor_attr_rssi_monitoring. + * + * @QCA_NL80211_VENDOR_SUBCMD_NDP: Command used for performing various NAN + * Data Path (NDP) related operations, attributes defined in + * enum qca_wlan_vendor_attr_ndp_params. + * + * @QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD: Command used to enable/disable + * Neighbour Discovery offload, attributes defined in + * enum qca_wlan_vendor_attr_nd_offload. + * + * @QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER: Used to set/get the various + * configuration parameter for BPF packet filter, attributes defined in + * enum qca_wlan_vendor_attr_packet_filter. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE: Gets the driver-firmware + * maximum supported size, attributes defined in + * enum qca_wlan_vendor_drv_info. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS: Command to get various + * data about wake reasons and datapath IP statistics, attributes defined + * in enum qca_wlan_vendor_attr_wake_stats. + * + * @QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG: Command used to set configuration + * for IEEE 802.11 communicating outside the context of a basic service + * set, called OCB command. Uses the attributes defines in + * enum qca_wlan_vendor_attr_ocb_set_config. + * + * @QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME: Command used to set OCB + * UTC time. Use the attributes defines in + * enum qca_wlan_vendor_attr_ocb_set_utc_time. + * + * @QCA_NL80211_VENDOR_SUBCMD_OCB_START_TIMING_ADVERT: Command used to start + * sending OCB timing advert frames. Uses the attributes defines in + * enum qca_wlan_vendor_attr_ocb_start_timing_advert. + * + * @QCA_NL80211_VENDOR_SUBCMD_OCB_STOP_TIMING_ADVERT: Command used to stop + * OCB timing advert. Uses the attributes defines in + * enum qca_wlan_vendor_attr_ocb_stop_timing_advert. + * + * @QCA_NL80211_VENDOR_SUBCMD_OCB_GET_TSF_TIMER: Command used to get TSF + * timer value. Uses the attributes defines in + * enum qca_wlan_vendor_attr_ocb_get_tsf_resp. + * + * @QCA_NL80211_VENDOR_SUBCMD_LINK_PROPERTIES: Command/event to update the + * link properties of the respective interface. As an event, is used + * to notify the connected station's status. The attributes for this + * command are defined in enum qca_wlan_vendor_attr_link_properties. + * * @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START: Command used to * start the P2P Listen offload function in device and pass the listen * channel, period, interval, count, device types, and vendor specific @@ -164,8 +237,11 @@ enum qca_radiotap_vendor_ids { * * @QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS: Perform a standalone AOA (angle of * arrival) measurement with a single peer. Specify peer MAC address in - * QCA_WLAN_VENDOR_ATTR_MAC_ADDR and measurement type in - * QCA_WLAN_VENDOR_ATTR_AOA_TYPE. Measurement result is reported in + * QCA_WLAN_VENDOR_ATTR_MAC_ADDR and optionally frequency (MHz) in + * QCA_WLAN_VENDOR_ATTR_FREQ (if not specified, locate peer in kernel + * scan results cache and use the frequency from there). + * Also specify measurement type in QCA_WLAN_VENDOR_ATTR_AOA_TYPE. + * Measurement result is reported in * QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT event. * * @QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS: Abort an AOA measurement. Specify @@ -185,6 +261,244 @@ enum qca_radiotap_vendor_ids { * * @QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI: Get antenna RSSI value for a * specific chain. + * + * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG: Get low level + * configuration for a DMG RF sector. Specify sector index in + * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX, sector type in + * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and RF modules + * to return sector information for in + * QCA_WLAN_VENDOR_ATTR_DMG_RF_MODULE_MASK. Returns sector configuration + * in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG. Also return the + * exact time where information was captured in + * QCA_WLAN_VENDOR_ATTR_TSF. + * + * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG: Set low level + * configuration for a DMG RF sector. Specify sector index in + * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX, sector type in + * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and sector configuration + * for one or more DMG RF modules in + * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG. + * + * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR: Get selected + * DMG RF sector for a station. This is the sector that the HW + * will use to communicate with the station. Specify the MAC address + * of associated station/AP/PCP in QCA_WLAN_VENDOR_ATTR_MAC_ADDR (not + * needed for unassociated station). Specify sector type to return in + * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE. Returns the selected + * sector index in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX. + * Also return the exact time where the information was captured + * in QCA_WLAN_VENDOR_ATTR_TSF. + * + * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR: Set the + * selected DMG RF sector for a station. This is the sector that + * the HW will use to communicate with the station. + * Specify the MAC address of associated station/AP/PCP in + * QCA_WLAN_VENDOR_ATTR_MAC_ADDR, the sector type to select in + * QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and the sector index + * in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX. + * The selected sector will be locked such that it will not be + * modified like it normally does (for example when station + * moves around). To unlock the selected sector for a station + * pass the special value 0xFFFF in the sector index. To unlock + * all connected stations also pass a broadcast MAC address. + * + * @QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS: Configure the TDLS behavior + * in the host driver. The different TDLS configurations are defined + * by the attributes in enum qca_wlan_vendor_attr_tdls_configuration. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES: Query device IEEE 802.11ax HE + * capabilities. The response uses the attributes defined in + * enum qca_wlan_vendor_attr_get_he_capabilities. + * + * @QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN: Abort an ongoing vendor scan that was + * started with QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN. This command + * carries the scan cookie of the corresponding scan request. The scan + * cookie is represented by QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE. + * + * @QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS: Set the Specific + * Absorption Rate (SAR) power limits. A critical regulation for + * FCC compliance, OEMs require methods to set SAR limits on TX + * power of WLAN/WWAN. enum qca_vendor_attr_sar_limits + * attributes are used with this command. + * + * @QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS: This command/event is used by the + * host driver for offloading the implementation of Auto Channel Selection + * (ACS) to an external user space entity. This interface is used as the + * event from the host driver to the user space entity and also as the + * request from the user space entity to the host driver. The event from + * the host driver is used by the user space entity as an indication to + * start the ACS functionality. The attributes used by this event are + * represented by the enum qca_wlan_vendor_attr_external_acs_event. + * User space entity uses the same interface to inform the host driver with + * selected channels after the ACS operation using the attributes defined + * by enum qca_wlan_vendor_attr_external_acs_channels. + * + * @QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE: Vendor event carrying the + * requisite information leading to a power save failure. The information + * carried as part of this event is represented by the + * enum qca_attr_chip_power_save_failure attributes. + * + * @QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET: Start/Stop the NUD statistics + * collection. Uses attributes defined in enum qca_attr_nud_stats_set. + * + * @QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET: Get the NUD statistics. These + * statistics are represented by the enum qca_attr_nud_stats_get + * attributes. + * + * @QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS: Sub-command to fetch + * the BSS transition status, whether accept or reject, for a list of + * candidate BSSIDs provided by the userspace. This uses the vendor + * attributes QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON and + * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO. The userspace shall specify + * the attributes QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON and an + * array of QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID nested in + * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO in the request. In the response + * the driver shall specify array of + * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID and + * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS pairs nested in + * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO. + * + * @QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL: Set the trace level for a + * specific QCA module. The trace levels are represented by + * enum qca_attr_trace_level attributes. + * + * @QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT: Set the Beam Refinement + * Protocol antenna limit in different modes. See enum + * qca_wlan_vendor_attr_brp_ant_limit_mode. + * + * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START: Start spectral scan. The scan + * parameters are specified by enum qca_wlan_vendor_attr_spectral_scan. + * This returns a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE) + * identifying the operation in success case. + * + * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP: Stop spectral scan. This uses + * a cookie (%QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE) from + * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START to identify the scan to + * be stopped. + * + * @QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS: Set the active Type Of Service on the + * specific interface. This can be used to modify some of the low level + * scan parameters (off channel dwell time, home channel time) in the + * driver/firmware. These parameters are maintained within the host driver. + * This command is valid only when the interface is in the connected state. + * These scan parameters shall be reset by the driver/firmware once + * disconnected. The attributes used with this command are defined in + * enum qca_wlan_vendor_attr_active_tos. + * + * @QCA_NL80211_VENDOR_SUBCMD_HANG: Event indicating to the user space that the + * driver has detected an internal failure. This event carries the + * information indicating the reason that triggered this detection. The + * attributes for this command are defined in + * enum qca_wlan_vendor_attr_hang. + * + * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CONFIG: Get the current values + * of spectral parameters used. The spectral scan parameters are specified + * by enum qca_wlan_vendor_attr_spectral_scan. + * + * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS: Get the debug stats + * for spectral scan functionality. The debug stats are specified by + * enum qca_wlan_vendor_attr_spectral_diag_stats. + * + * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO: Get spectral + * scan system capabilities. The capabilities are specified + * by enum qca_wlan_vendor_attr_spectral_cap. + * + * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS: Get the current + * status of spectral scan. The status values are specified + * by enum qca_wlan_vendor_attr_spectral_scan_status. + * + * @QCA_NL80211_VENDOR_SUBCMD_PEER_FLUSH_PENDING: Sub-command to flush + * peer pending packets. Specify the peer MAC address in + * QCA_WLAN_VENDOR_ATTR_PEER_ADDR and the access category of the packets + * in QCA_WLAN_VENDOR_ATTR_AC. The attributes are listed + * in enum qca_wlan_vendor_attr_flush_pending. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO: Get vendor specific Representative + * RF Operating Parameter (RROP) information. The attributes for this + * information are defined in enum qca_wlan_vendor_attr_rrop_info. This is + * intended for use by external Auto Channel Selection applications. + * + * @QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS: Get the Specific Absorption Rate + * (SAR) power limits. This is a companion to the command + * @QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS and is used to retrieve the + * settings currently in use. The attributes returned by this command are + * defined by enum qca_vendor_attr_sar_limits. + * + * @QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO: Provides the current behavior of + * the WLAN hardware MAC. Also, provides the WLAN netdev interface + * information attached to the respective MAC. + * This works both as a query (user space asks the current mode) or event + * interface (driver advertising the current mode to the user space). + * Driver does not trigger this event for temporary hardware mode changes. + * Mode changes w.r.t Wi-Fi connection update (VIZ creation / deletion, + * channel change, etc.) are updated with this event. Attributes for this + * interface are defined in enum qca_wlan_vendor_attr_mac. + * + * @QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH: Set MSDU queue depth threshold + * per peer per TID. Attributes for this command are define in + * enum qca_wlan_set_qdepth_thresh_attr. + * @QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD: Provides the thermal shutdown action + * guide for WLAN driver. Request to suspend of driver and FW if the + * temperature is higher than the suspend threshold; resume action is + * requested to driver if the temperature is lower than the resume + * threshold. In user poll mode, request temperature data by user. For test + * purpose, getting thermal shutdown configuration parameters is needed. + * Attributes for this interface are defined in + * enum qca_wlan_vendor_attr_thermal_cmd. + * @QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT: Thermal events reported from + * driver. Thermal temperature and indication of resume completion are + * reported as thermal events. The attributes for this command are defined + * in enum qca_wlan_vendor_attr_thermal_event. + * + * @QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION: Sub command to set WiFi + * test configuration. Attributes for this command are defined in + * enum qca_wlan_vendor_attr_wifi_test_config. + * + * @QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER: This command is used to configure an + * RX filter to receive frames from stations that are active on the + * operating channel, but not associated with the local device (e.g., STAs + * associated with other APs). Filtering is done based on a list of BSSIDs + * and STA MAC addresses added by the user. This command is also used to + * fetch the statistics of unassociated stations. The attributes used with + * this command are defined in enum qca_wlan_vendor_attr_bss_filter. + * + * @QCA_NL80211_VENDOR_SUBCMD_NAN_EXT: An extendable version of NAN vendor + * command. The earlier command for NAN, QCA_NL80211_VENDOR_SUBCMD_NAN, + * carried a payload which was a binary blob of data. The command was not + * extendable to send more information. The newer version carries the + * legacy blob encapsulated within an attribute and can be extended with + * additional vendor attributes that can enhance the NAN command interface. + * @QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT: Event to indicate scan triggered + * or stopped within driver/firmware in order to initiate roaming. The + * attributes used with this event are defined in enum + * qca_wlan_vendor_attr_roam_scan. Some drivers may not send these events + * in few cases, e.g., if the host processor is sleeping when this event + * is generated in firmware. + * + * @QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG: This command is used to + * configure parameters per peer to capture Channel Frequency Response + * (CFR) and enable Periodic CFR capture. The attributes for this command + * are defined in enum qca_wlan_vendor_peer_cfr_capture_attr. + * + * @QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT: Event to indicate changes + * in throughput dynamically. The driver estimates the throughput based on + * number of packets being transmitted/received per second and indicates + * the changes in throughput to user space. Userspace tools can use this + * information to configure kernel's TCP parameters in order to achieve + * peak throughput. Optionally, the driver will also send guidance on + * modifications to kernel's TCP parameters which can be referred by + * userspace tools. The attributes used with this event are defined in enum + * qca_wlan_vendor_attr_throughput_change. + * + * @QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG: This command is used to set + * priorities among different types of traffic during coex scenarios. + * Current supported prioritization is among WLAN/BT/ZIGBEE with different + * profiles mentioned in enum qca_coex_config_profiles. The associated + * attributes used with this command are defined in enum + * qca_vendor_attr_coex_config. + * + * Based on the config provided, FW will boost the weight and prioritize + * the traffic for that subsystem (WLAN/BT/Zigbee). */ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0, @@ -194,7 +508,7 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10, QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY = 11, QCA_NL80211_VENDOR_SUBCMD_NAN = 12, - QCA_NL80211_VENDOR_SUBMCD_STATS_EXT = 13, + QCA_NL80211_VENDOR_SUBCMD_STATS_EXT = 13, QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET = 14, QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET = 15, QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR = 16, @@ -236,11 +550,33 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED = 58, QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED = 59, QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED = 60, - /* 61-73 - reserved for QCA */ + QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO = 61, + QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START = 62, + QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP = 63, + QCA_NL80211_VENDOR_SUBCMD_ROAM = 64, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SSID_HOTLIST = 65, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_SSID_HOTLIST = 66, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_SSID_FOUND = 67, + QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_SSID_LOST = 68, + QCA_NL80211_VENDOR_SUBCMD_PNO_SET_LIST = 69, + QCA_NL80211_VENDOR_SUBCMD_PNO_SET_PASSPOINT_LIST = 70, + QCA_NL80211_VENDOR_SUBCMD_PNO_RESET_PASSPOINT_LIST = 71, + QCA_NL80211_VENDOR_SUBCMD_PNO_NETWORK_FOUND = 72, + QCA_NL80211_VENDOR_SUBCMD_PNO_PASSPOINT_NETWORK_FOUND = 73, /* Wi-Fi configuration subcommands */ QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION = 74, QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION = 75, - /* 76-90 - reserved for QCA */ + QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET = 76, + QCA_NL80211_VENDOR_SUBCMD_GET_RING_DATA = 77, + QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES = 78, + QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS = 79, + QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI = 80, + QCA_NL80211_VENDOR_SUBCMD_NDP = 81, + QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD = 82, + QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER = 83, + QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE = 84, + QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS = 85, + /* 86-90 - reserved for QCA */ QCA_NL80211_VENDOR_SUBCMD_DATA_OFFLOAD = 91, QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG = 92, QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME = 93, @@ -285,21 +621,66 @@ enum qca_nl80211_vendor_subcmds { QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT = 136, QCA_NL80211_VENDOR_SUBCMD_ENCRYPTION_TEST = 137, QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI = 138, + /* DMG low level RF sector operations */ + QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG = 139, + QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG = 140, + QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR = 141, + QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR = 142, + QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS = 143, + QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES = 144, + QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN = 145, + QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS = 146, + QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS = 147, + QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE = 148, + QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET = 149, + QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET = 150, + QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS = 151, + QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL = 152, + QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT = 153, + QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START = 154, + QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP = 155, + QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS = 156, + QCA_NL80211_VENDOR_SUBCMD_HANG = 157, + QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CONFIG = 158, + QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS = 159, + QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO = 160, + QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS = 161, + /* Flush peer pending data */ + QCA_NL80211_VENDOR_SUBCMD_PEER_FLUSH_PENDING = 162, + QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO = 163, + QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS = 164, + QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO = 165, + QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH = 166, + /* Thermal shutdown commands to protect wifi chip */ + QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD = 167, + QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT = 168, + /* Wi-Fi test configuration subcommand */ + QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION = 169, + /* Frame filter operations for other BSSs/unassociated STAs */ + QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER = 170, + QCA_NL80211_VENDOR_SUBCMD_NAN_EXT = 171, + QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT = 172, + QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG = 173, + QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT = 174, + QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG = 175, }; - enum qca_wlan_vendor_attr { QCA_WLAN_VENDOR_ATTR_INVALID = 0, /* used by QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY */ QCA_WLAN_VENDOR_ATTR_DFS = 1, - /* used by QCA_NL80211_VENDOR_SUBCMD_NAN */ + /* Used only when driver sends vendor events to the userspace under the + * command QCA_NL80211_VENDOR_SUBCMD_NAN. Not used when userspace sends + * commands to the driver. + */ QCA_WLAN_VENDOR_ATTR_NAN = 2, /* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */ QCA_WLAN_VENDOR_ATTR_STATS_EXT = 3, /* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */ QCA_WLAN_VENDOR_ATTR_IFINDEX = 4, /* used by QCA_NL80211_VENDOR_SUBCMD_ROAMING, u32 with values defined - * by enum qca_roaming_policy. */ + * by enum qca_roaming_policy. + */ QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY = 5, QCA_WLAN_VENDOR_ATTR_MAC_ADDR = 6, /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */ @@ -387,17 +768,87 @@ enum qca_wlan_vendor_attr { QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT = 25, /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command * to specify the chain number (unsigned 32 bit value) to inquire - * the corresponding antenna RSSI value */ + * the corresponding antenna RSSI value + */ QCA_WLAN_VENDOR_ATTR_CHAIN_INDEX = 26, /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command - * to report the specific antenna RSSI value (unsigned 32 bit value) */ + * to report the specific antenna RSSI value (unsigned 32 bit value) + */ QCA_WLAN_VENDOR_ATTR_CHAIN_RSSI = 27, + /* Frequency in MHz, various uses. Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_FREQ = 28, + /* TSF timer value, unsigned 64 bit value. + * May be returned by various commands. + */ + QCA_WLAN_VENDOR_ATTR_TSF = 29, + /* DMG RF sector index, unsigned 16 bit number. Valid values are + * 0..127 for sector indices or 65535 as special value used to + * unlock sector selection in + * QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR. + */ + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX = 30, + /* DMG RF sector type, unsigned 8 bit value. One of the values + * in enum qca_wlan_vendor_attr_dmg_rf_sector_type. + */ + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE = 31, + /* Bitmask of DMG RF modules for which information is requested. Each + * bit corresponds to an RF module with the same index as the bit + * number. Unsigned 32 bit number but only low 8 bits can be set since + * all DMG chips currently have up to 8 RF modules. + */ + QCA_WLAN_VENDOR_ATTR_DMG_RF_MODULE_MASK = 32, + /* Array of nested attributes where each entry is DMG RF sector + * configuration for a single RF module. + * Attributes for each entry are taken from enum + * qca_wlan_vendor_attr_dmg_rf_sector_cfg. + * Specified in QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG + * and returned by QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG. + */ + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG = 33, + /* Used in QCA_NL80211_VENDOR_SUBCMD_STATS_EXT command + * to report frame aggregation statistics to userspace. + */ + QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_NUM = 34, + QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_INFO = 35, + /* Unsigned 8-bit value representing MBO transition reason code as + * provided by the AP used by subcommand + * QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS. This is + * specified by the userspace in the request to the driver. + */ + QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON = 36, + /* Array of nested attributes, BSSID and status code, used by subcommand + * QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS, where each + * entry is taken from enum qca_wlan_vendor_attr_btm_candidate_info. + * The userspace space specifies the list/array of candidate BSSIDs in + * the order of preference in the request. The driver specifies the + * status code, for each BSSID in the list, in the response. The + * acceptable candidates are listed in the order preferred by the + * driver. + */ + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO = 37, + /* Used in QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT command + * See enum qca_wlan_vendor_attr_brp_ant_limit_mode. + */ + QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE = 38, + /* Used in QCA_NL80211_VENDOR_SUBCMD_BRP_SET_ANT_LIMIT command + * to define the number of antennas to use for BRP. + * different purpose in each ANT_LIMIT_MODE: + * DISABLE - ignored + * EFFECTIVE - upper limit to number of antennas to be used + * FORCE - exact number of antennas to be used + * unsigned 8 bit value + */ + QCA_WLAN_VENDOR_ATTR_BRP_ANT_NUM_LIMIT = 39, + /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command + * to report the corresponding antenna index to the chain RSSI value + */ + QCA_WLAN_VENDOR_ATTR_ANTENNA_INFO = 40, + /* keep last */ QCA_WLAN_VENDOR_ATTR_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_MAX = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1, }; - enum qca_roaming_policy { QCA_ROAMING_NOT_ALLOWED, QCA_ROAMING_ALLOWED_WITHIN_ESS, @@ -413,6 +864,38 @@ enum qca_wlan_vendor_attr_roam_auth { QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS, + /* Indicates the status of re-association requested by user space for + * the BSSID specified by QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID. + * Type u16. + * Represents the status code from AP. Use + * %WLAN_STATUS_UNSPECIFIED_FAILURE if the device cannot give you the + * real status code for failures. + */ + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_STATUS, + /* This attribute indicates that the old association was maintained when + * a re-association is requested by user space and that re-association + * attempt fails (i.e., cannot connect to the requested BSS, but can + * remain associated with the BSS with which the association was in + * place when being requested to roam). Used along with + * WLAN_VENDOR_ATTR_ROAM_AUTH_STATUS to indicate the current + * re-association status. Type flag. + * This attribute is applicable only for re-association failure cases. + */ + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RETAIN_CONNECTION, + /* This attribute specifies the PMK if one was newly generated during + * FILS roaming. This is added to the PMKSA cache and is used in + * subsequent connections with PMKSA caching. + */ + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK = 11, + /* This attribute specifies the PMKID used/generated for the current + * FILS roam. This is used in subsequent connections with PMKSA caching. + */ + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID = 12, + /* A 16-bit unsigned value specifying the next sequence number to use + * in ERP message in the currently associated realm. This is used in + * doing subsequent ERP based connections in the same realm. + */ + QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM = 13, /* keep last */ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX = @@ -493,13 +976,24 @@ enum qca_wlan_vendor_acs_hw_mode { * @QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY: Device supports automatic * band selection based on channel selection results. * @QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS: Device supports - * simultaneous off-channel operations. + * simultaneous off-channel operations. * @QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD: Device supports P2P * Listen offload; a mechanism where the station's firmware takes care of * responding to incoming Probe Request frames received from other P2P * Devices whilst in Listen state, rather than having the user space * wpa_supplicant do it. Information from received P2P requests are * forwarded from firmware to host whenever the host processor wakes up. + * @QCA_WLAN_VENDOR_FEATURE_OCE_STA: Device supports all OCE non-AP STA + * specific features. + * @QCA_WLAN_VENDOR_FEATURE_OCE_AP: Device supports all OCE AP specific + * features. + * @QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON: Device supports OCE STA-CFON + * specific features only. If a Device sets this bit but not the + * %QCA_WLAN_VENDOR_FEATURE_OCE_AP, the userspace shall assume that + * this Device may not support all OCE AP functionalities but can support + * only OCE STA-CFON functionalities. + * @QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY: Device supports self + * managed regulatory. * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits */ enum qca_wlan_vendor_features { @@ -507,6 +1001,10 @@ enum qca_wlan_vendor_features { QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY = 1, QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS = 2, QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD = 3, + QCA_WLAN_VENDOR_FEATURE_OCE_STA = 4, + QCA_WLAN_VENDOR_FEATURE_OCE_AP = 5, + QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON = 6, + QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY = 7, NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */ }; @@ -532,6 +1030,112 @@ enum qca_wlan_vendor_attr_data_offload_ind { QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST - 1 }; +/** + * enum qca_wlan_vendor_attr_ocb_set_config - Vendor subcmd attributes to set + * OCB config + * + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT: Number of channels in the + * configuration + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE: Size of the schedule + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY: Array of channels + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY: Array of channels to be + * scheduled + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY: Array of NDL channel + * information + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY: Array of NDL + * active state configuration + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS: Configuration flags such as + * OCB_CONFIG_FLAG_80211_FRAME_MODE + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_DEF_TX_PARAM: Default TX parameters to + * use in the case that a packet is sent without a TX control header + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_TA_MAX_DURATION: Max duration after the + * last TA received that the local time set by TA is synchronous to other + * communicating OCB STAs. + */ +enum qca_wlan_vendor_attr_ocb_set_config { + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_COUNT = 1, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_SIZE = 2, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_CHANNEL_ARRAY = 3, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_SCHEDULE_ARRAY = 4, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_CHANNEL_ARRAY = 5, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_NDL_ACTIVE_STATE_ARRAY = 6, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_FLAGS = 7, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_DEF_TX_PARAM = 8, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_TA_MAX_DURATION = 9, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_MAX = + QCA_WLAN_VENDOR_ATTR_OCB_SET_CONFIG_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_ocb_set_utc_time - Vendor subcmd attributes to set + * UTC time + * + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE: The UTC time as an array of + * 10 bytes + * @QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR: The time error as an array of + * 5 bytes + */ +enum qca_wlan_vendor_attr_ocb_set_utc_time { + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_VALUE = 1, + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_ERROR = 2, + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_MAX = + QCA_WLAN_VENDOR_ATTR_OCB_SET_UTC_TIME_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_ocb_start_timing_advert - Vendor subcmd attributes + * to start sending timing advert frames + * + * @QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ: Cannel frequency + * on which to send the frames + * @QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE: Number of times + * the frame is sent in 5 seconds + */ +enum qca_wlan_vendor_attr_ocb_start_timing_advert { + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_CHANNEL_FREQ = 1, + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_REPEAT_RATE = 2, + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_MAX = + QCA_WLAN_VENDOR_ATTR_OCB_START_TIMING_ADVERT_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_ocb_stop_timing_advert - Vendor subcmd attributes + * to stop timing advert + * + * @QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ: The channel + * frequency on which to stop the timing advert + */ +enum qca_wlan_vendor_attr_ocb_stop_timing_advert { + QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_CHANNEL_FREQ = 1, + QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_MAX = + QCA_WLAN_VENDOR_ATTR_OCB_STOP_TIMING_ADVERT_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_ocb_get_tsf_response - Vendor subcmd attributes to + * get TSF timer value + * + * @QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH: Higher 32 bits of the + * timer + * @QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW: Lower 32 bits of the timer + */ +enum qca_wlan_vendor_attr_ocb_get_tsf_resp { + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_HIGH = 1, + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_TIMER_LOW = 2, + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_MAX = + QCA_WLAN_VENDOR_ATTR_OCB_GET_TSF_RESP_AFTER_LAST - 1 +}; + enum qca_vendor_attr_get_preferred_freq_list { QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_INVALID, /* A 32-unsigned value; the interface type/mode for which the preferred @@ -544,6 +1148,12 @@ enum qca_vendor_attr_get_preferred_freq_list { * from kernel space to user space. */ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST, + /* An array of nested values as per enum qca_wlan_vendor_attr_pcl + * attribute. Each element contains frequency (MHz), weight, and flag + * bit mask indicating how the frequency should be used in P2P + * negotiation; sent from kernel space to user space. + */ + QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_WEIGHED_PCL, /* keep last */ QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_GET_PREFERRED_FREQ_LIST_MAX = @@ -679,11 +1289,39 @@ enum qca_vendor_attr_wisa_cmd { * vendor specific element is defined by the latest P802.11ax draft. * Please note that the draft is still work in progress and this element * payload is subject to change. + * + * @QCA_VENDOR_ELEM_RAPS: RAPS element (OFDMA-based Random Access Parameter Set + * element). + * This element can be used for pre-standard publication testing of HE + * before P802.11ax draft assigns the element ID extension. The payload of + * this vendor specific element is defined by the latest P802.11ax draft + * (not including the Element ID Extension field). Please note that the + * draft is still work in progress and this element payload is subject to + * change. + * + * @QCA_VENDOR_ELEM_MU_EDCA_PARAMS: MU EDCA Parameter Set element. + * This element can be used for pre-standard publication testing of HE + * before P802.11ax draft assigns the element ID extension. The payload of + * this vendor specific element is defined by the latest P802.11ax draft + * (not including the Element ID Extension field). Please note that the + * draft is still work in progress and this element payload is subject to + * change. + * + * @QCA_VENDOR_ELEM_BSS_COLOR_CHANGE: BSS Color Change Announcement element. + * This element can be used for pre-standard publication testing of HE + * before P802.11ax draft assigns the element ID extension. The payload of + * this vendor specific element is defined by the latest P802.11ax draft + * (not including the Element ID Extension field). Please note that the + * draft is still work in progress and this element payload is subject to + * change. */ enum qca_vendor_element_id { QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0, QCA_VENDOR_ELEM_HE_CAPAB = 1, QCA_VENDOR_ELEM_HE_OPER = 2, + QCA_VENDOR_ELEM_RAPS = 3, + QCA_VENDOR_ELEM_MU_EDCA_PARAMS = 4, + QCA_VENDOR_ELEM_BSS_COLOR_CHANGE = 5, }; /** @@ -696,29 +1334,32 @@ enum qca_vendor_element_id { * @QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES: Nested array attribute of supported * rates to be included * @QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE: flag used to send probe requests - * at non CCK rate in 2GHz band + * at non CCK rate in 2GHz band * @QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS: Unsigned 32-bit scan flags * @QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE: Unsigned 64-bit cookie provided by the - * driver for the specific scan request + * driver for the specific scan request * @QCA_WLAN_VENDOR_ATTR_SCAN_STATUS: Unsigned 8-bit status of the scan - * request decoded as in enum scan_status + * request decoded as in enum scan_status * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC: 6-byte MAC address to use when randomisation - * scan flag is set + * scan flag is set * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK: 6-byte MAC address mask to be used with - * randomisation + * randomisation + * @QCA_WLAN_VENDOR_ATTR_SCAN_BSSID: 6-byte MAC address representing the + * specific BSSID to scan for. */ enum qca_wlan_vendor_attr_scan { QCA_WLAN_VENDOR_ATTR_SCAN_INVALID_PARAM = 0, - QCA_WLAN_VENDOR_ATTR_SCAN_IE, - QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES, - QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS, - QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES, - QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE, - QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS, - QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE, - QCA_WLAN_VENDOR_ATTR_SCAN_STATUS, - QCA_WLAN_VENDOR_ATTR_SCAN_MAC, - QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK, + QCA_WLAN_VENDOR_ATTR_SCAN_IE = 1, + QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES = 2, + QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS = 3, + QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES = 4, + QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE = 5, + QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS = 6, + QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE = 7, + QCA_WLAN_VENDOR_ATTR_SCAN_STATUS = 8, + QCA_WLAN_VENDOR_ATTR_SCAN_MAC = 9, + QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK = 10, + QCA_WLAN_VENDOR_ATTR_SCAN_BSSID = 11, QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_SCAN_MAX = QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST - 1 @@ -726,10 +1367,10 @@ enum qca_wlan_vendor_attr_scan { /** * enum scan_status - Specifies the valid values the vendor scan attribute - * QCA_WLAN_VENDOR_ATTR_SCAN_STATUS can take + * QCA_WLAN_VENDOR_ATTR_SCAN_STATUS can take * * @VENDOR_SCAN_STATUS_NEW_RESULTS: implies the vendor scan is successful with - * new scan results + * new scan results * @VENDOR_SCAN_STATUS_ABORTED: implies the vendor scan was aborted in-between */ enum scan_status { @@ -776,7 +1417,8 @@ enum qca_vendor_attr_txpower_scale { enum qca_vendor_attr_txpower_decr_db { QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_INVALID, /* 8-bit unsigned value to indicate the reduction of TX power in dB for - * a virtual interface. */ + * a virtual interface. + */ QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB, /* keep last */ QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST, @@ -826,29 +1468,37 @@ enum qca_wlan_vendor_attr_config { */ QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND = 7, /* 8-bit unsigned value to configure the maximum TX MPDU for - * aggregation. */ + * aggregation. + */ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION = 8, /* 8-bit unsigned value to configure the maximum RX MPDU for - * aggregation. */ + * aggregation. + */ QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION = 9, /* 8-bit unsigned value to configure the Non aggregrate/11g sw - * retry threshold (0 disable, 31 max). */ + * retry threshold (0 disable, 31 max). + */ QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY = 10, /* 8-bit unsigned value to configure the aggregrate sw - * retry threshold (0 disable, 31 max). */ + * retry threshold (0 disable, 31 max). + */ QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY = 11, /* 8-bit unsigned value to configure the MGMT frame - * retry threshold (0 disable, 31 max). */ + * retry threshold (0 disable, 31 max). + */ QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY = 12, /* 8-bit unsigned value to configure the CTRL frame - * retry threshold (0 disable, 31 max). */ + * retry threshold (0 disable, 31 max). + */ QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY = 13, /* 8-bit unsigned value to configure the propagation delay for - * 2G/5G band (0~63, units in us) */ + * 2G/5G band (0~63, units in us) + */ QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY = 14, /* Unsigned 32-bit value to configure the number of unicast TX fail * packet count. The peer is disconnected once this threshold is - * reached. */ + * reached. + */ QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT = 15, /* Attribute used to set scan default IEs to the driver. * @@ -859,7 +1509,8 @@ enum qca_wlan_vendor_attr_config { * merged with the IEs received along with scan request coming to the * driver. If a particular IE is present in the scan default IEs but not * present in the scan request, then that IE should be added to the IEs - * sent in the Probe Request frames for that scan request. */ + * sent in the Probe Request frames for that scan request. + */ QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES = 16, /* Unsigned 32-bit attribute for generic commands */ QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_COMMAND = 17, @@ -868,42 +1519,183 @@ enum qca_wlan_vendor_attr_config { /* Unsigned 32-bit data attribute for generic command response */ QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA = 19, /* Unsigned 32-bit length attribute for - * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA */ + * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA + */ QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_LENGTH = 20, /* Unsigned 32-bit flags attribute for - * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA */ + * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA + */ QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_FLAGS = 21, /* Unsigned 32-bit, defining the access policy. * See enum qca_access_policy. Used with - * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST. */ + * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST. + */ QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY = 22, /* Sets the list of full set of IEs for which a specific access policy * has to be applied. Used along with * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY to control the access. - * Zero length payload can be used to clear this access constraint. */ + * Zero length payload can be used to clear this access constraint. + */ QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST = 23, /* Unsigned 32-bit, specifies the interface index (netdev) for which the * corresponding configurations are applied. If the interface index is * not specified, the configurations are attributed to the respective - * wiphy. */ + * wiphy. + */ QCA_WLAN_VENDOR_ATTR_CONFIG_IFINDEX = 24, /* 8-bit unsigned value to trigger QPower: 1-Enable, 0-Disable */ QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER = 25, /* 8-bit unsigned value to configure the driver and below layers to * ignore the assoc disallowed set by APs while connecting - * 1-Ignore, 0-Don't ignore */ + * 1-Ignore, 0-Don't ignore + */ QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED = 26, /* 32-bit unsigned value to trigger antenna diversity features: - * 1-Enable, 0-Disable */ + * 1-Enable, 0-Disable + */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA = 27, /* 32-bit unsigned value to configure specific chain antenna */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN = 28, /* 32-bit unsigned value to trigger cycle selftest - * 1-Enable, 0-Disable */ + * 1-Enable, 0-Disable + */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST = 29, /* 32-bit unsigned to configure the cycle time of selftest - * the unit is micro-second */ + * the unit is micro-second + */ QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL = 30, + /* 32-bit unsigned value to set reorder timeout for AC_VO */ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE = 31, + /* 32-bit unsigned value to set reorder timeout for AC_VI */ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VIDEO = 32, + /* 32-bit unsigned value to set reorder timeout for AC_BE */ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BESTEFFORT = 33, + /* 32-bit unsigned value to set reorder timeout for AC_BK */ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BACKGROUND = 34, + /* 6-byte MAC address to point out the specific peer */ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC = 35, + /* 32-bit unsigned value to set window size for specific peer */ + QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT = 36, + /* 8-bit unsigned value to set the beacon miss threshold in 2.4 GHz */ + QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_24 = 37, + /* 8-bit unsigned value to set the beacon miss threshold in 5 GHz */ + QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_5 = 38, + /* 32-bit unsigned value to configure 5 or 10 MHz channel width for + * station device while in disconnect state. The attribute use the + * value of enum nl80211_chan_width: NL80211_CHAN_WIDTH_5 means 5 MHz, + * NL80211_CHAN_WIDTH_10 means 10 MHz. If set, the device work in 5 or + * 10 MHz channel width, the station will not connect to a BSS using 20 + * MHz or higher bandwidth. Set to NL80211_CHAN_WIDTH_20_NOHT to + * clear this constraint. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_SUB20_CHAN_WIDTH = 39, + /* 32-bit unsigned value to configure the propagation absolute delay + * for 2G/5G band (units in us) + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_ABS_DELAY = 40, + /* 32-bit unsigned value to set probe period */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_PERIOD = 41, + /* 32-bit unsigned value to set stay period */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_STAY_PERIOD = 42, + /* 32-bit unsigned value to set snr diff */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SNR_DIFF = 43, + /* 32-bit unsigned value to set probe dwell time */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_PROBE_DWELL_TIME = 44, + /* 32-bit unsigned value to set mgmt snr weight */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_MGMT_SNR_WEIGHT = 45, + /* 32-bit unsigned value to set data snr weight */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_DATA_SNR_WEIGHT = 46, + /* 32-bit unsigned value to set ack snr weight */ + QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ACK_SNR_WEIGHT = 47, + /* 32-bit unsigned value to configure the listen interval. + * This is in units of beacon intervals. This configuration alters + * the negotiated listen interval with the AP during the connection. + * It is highly recommended to configure a value less than or equal to + * the one negotiated during the association. Configuring any greater + * value can have adverse effects (frame loss, AP disassociating STA, + * etc.). + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_LISTEN_INTERVAL = 48, + /* + * 8 bit unsigned value that is set on an AP/GO virtual interface to + * disable operations that would cause the AP/GO to leave its operating + * channel. + * + * This will restrict the scans to the AP/GO operating channel and the + * channels of the other band, if DBS is supported.A STA/CLI interface + * brought up after this setting is enabled, will be restricted to + * connecting to devices only on the AP/GO interface's operating channel + * or on the other band in DBS case. P2P supported channel list is + * modified, to only include AP interface's operating-channel and the + * channels of the other band if DBS is supported. + * + * These restrictions are only applicable as long as the AP/GO interface + * is alive. If the AP/GO interface is brought down then this + * setting/restriction is forgotten. + * + * If this variable is set on an AP/GO interface while a multi-channel + * concurrent session is active, it has no effect on the operation of + * the current interfaces, other than restricting the scan to the AP/GO + * operating channel and the other band channels if DBS is supported. + * However, if the STA is brought down and restarted then the new STA + * connection will either be formed on the AP/GO channel or on the + * other band in a DBS case. This is because of the scan being + * restricted on these channels as mentioned above. + * + * 1-Restrict / 0-Don't restrict offchannel operations. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_RESTRICT_OFFCHANNEL = 49, + /* + * 8 bit unsigned value to enable/disable LRO (Large Receive Offload) + * on an interface. + * 1 - Enable, 0 - Disable. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_LRO = 50, + + /* + * 8 bit unsigned value to globally enable/disable scan + * 1 - Enable, 0 - Disable. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_ENABLE = 51, + + /* 8-bit unsigned value to set the total beacon miss count + * This parameter will set the total beacon miss count. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_TOTAL_BEACON_MISS_COUNT = 52, + + /* Unsigned 32-bit value to configure the number of continuous + * Beacon Miss which shall be used by the firmware to penalize + * the RSSI for BTC. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_PENALIZE_AFTER_NCONS_BEACON_MISS_BTC = 53, + + /* 8-bit unsigned value to configure the driver and below layers to + * enable/disable all FILS features. + * 0-enable, 1-disable + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS = 54, + + /* 16-bit unsigned value to configure the level of WLAN latency + * module. See enum qca_wlan_vendor_attr_config_latency_level. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL = 55, + + /* 8-bit unsigned value indicating the driver to use the RSNE as-is from + * the connect interface. Exclusively used for the scenarios where the + * device is used as a test bed device with special functionality and + * not recommended for production. This helps driver to not validate the + * RSNE passed from user space and thus allow arbitrary IE data to be + * used for testing purposes. + * 1-enable, 0-disable. + * Applications set/reset this configuration. If not reset, this + * parameter remains in use until the driver is unloaded. + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_RSN_IE = 56, + + /* 8-bit unsigned value to trigger green Tx power saving. + * 1-Enable, 0-Disable + */ + QCA_WLAN_VENDOR_ATTR_CONFIG_GTX = 57, /* keep last */ QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST, @@ -937,7 +1729,8 @@ enum qca_wlan_vendor_attr_sap_config { enum qca_wlan_vendor_attr_sap_conditional_chan_switch { QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_INVALID = 0, /* Priority based frequency list (an array of u32 values in host byte - * order) */ + * order) + */ QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST = 1, /* Status of the conditional switch (u32). * 0: Success, Non-zero: Failure @@ -972,6 +1765,36 @@ enum qca_wlan_gpio_attr { }; /** + * qca_wlan_set_qdepth_thresh_attr - Parameters for setting + * MSDUQ depth threshold per peer per tid in the target + * + * Associated Vendor Command: + * QCA_NL80211_VENDOR_SUBCMD_SET_QDEPTH_THRESH + */ +enum qca_wlan_set_qdepth_thresh_attr { + QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_INVALID = 0, + /* 6-byte MAC address */ + QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_MAC_ADDR, + /* Unsigned 32-bit attribute for holding the TID */ + QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_TID, + /* Unsigned 32-bit attribute for holding the update mask + * bit 0 - Update high priority msdu qdepth threshold + * bit 1 - Update low priority msdu qdepth threshold + * bit 2 - Update UDP msdu qdepth threshold + * bit 3 - Update Non UDP msdu qdepth threshold + * rest of bits are reserved + */ + QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_UPDATE_MASK, + /* Unsigned 32-bit attribute for holding the threshold value */ + QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_VALUE, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_LAST, + QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_MAX = + QCA_WLAN_VENDOR_ATTR_QDEPTH_THRESH_LAST - 1, +}; + +/** * enum qca_wlan_vendor_attr_get_hw_capability - Wi-Fi hardware capability */ enum qca_wlan_vendor_attr_get_hw_capability { @@ -1143,6 +1966,12 @@ enum qca_wlan_vendor_attr_get_hw_capability { * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF: per antenna NF value * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON: RSSI of beacon * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON: SNR of beacon + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_REPORT_TIME: u64 + * Absolute timestamp from 1970/1/1, unit in ms. After receiving the + * message, user layer APP could call gettimeofday to get another + * timestamp and calculate transfer delay for the message. + * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MEASUREMENT_TIME: u32 + * Real period for this measurement, unit in us. */ enum qca_wlan_vendor_attr_ll_stats_ext { QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_INVALID = 0, @@ -1236,6 +2065,9 @@ enum qca_wlan_vendor_attr_ll_stats_ext { QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_REPORT_TIME, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MEASUREMENT_TIME, + QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST, QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX = QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST - 1 @@ -1289,7 +2121,7 @@ enum qca_wlan_vendor_attr_loc_capa { * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_INITIATOR: Set if driver * can run FTM sessions. QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION * will be supported if set. -* @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP: Set if FTM responder + * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP: Set if FTM responder * supports immediate (ASAP) response. * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA: Set if driver supports standalone * AOA measurement using QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS. @@ -1320,6 +2152,10 @@ enum qca_wlan_vendor_attr_loc_capa_flags { * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD: Request AOA * measurement every <value> bursts. If 0 or not specified, * AOA measurements will be disabled for this peer. + * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ: Frequency in MHz where + * the measurement frames are exchanged. Optional; if not + * specified, try to locate the peer in the kernel scan + * results cache and use frequency from there. */ enum qca_wlan_vendor_attr_ftm_peer_info { QCA_WLAN_VENDOR_ATTR_FTM_PEER_INVALID, @@ -1328,6 +2164,7 @@ enum qca_wlan_vendor_attr_ftm_peer_info { QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS, QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID, QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD, + QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ, /* keep last */ QCA_WLAN_VENDOR_ATTR_FTM_PEER_AFTER_LAST, QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX = @@ -1587,4 +2424,3824 @@ enum qca_wlan_vendor_attr_encryption_test { QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_AFTER_LAST - 1 }; +/** + * enum qca_wlan_vendor_attr_dmg_rf_sector_type - Type of + * sector for DMG RF sector operations. + * + * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_RX: RX sector + * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_TX: TX sector + */ +enum qca_wlan_vendor_attr_dmg_rf_sector_type { + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_RX, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_TX, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_MAX +}; + +/** + * BRP antenna limit mode + * + * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE: Disable BRP force + * antenna limit, BRP will be performed as usual. + * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_EFFECTIVE: Define maximal + * antennas limit. the hardware may use less antennas than the + * maximum limit. + * @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_FORCE: The hardware will + * use exactly the specified number of antennas for BRP. + */ +enum qca_wlan_vendor_attr_brp_ant_limit_mode { + QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE, + QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_EFFECTIVE, + QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_FORCE, + QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_MAX +}; + +/** + * enum qca_wlan_vendor_attr_dmg_rf_sector_cfg - Attributes for + * DMG RF sector configuration for a single RF module. + * The values are defined in a compact way which closely matches + * the way it is stored in HW registers. + * The configuration provides values for 32 antennas and 8 distribution + * amplifiers, and together describes the characteristics of the RF + * sector - such as a beam in some direction with some gain. + * + * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX: Index + * of RF module for this configuration. + * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE0: Bit 0 of edge + * amplifier gain index. Unsigned 32 bit number containing + * bits for all 32 antennas. + * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE1: Bit 1 of edge + * amplifier gain index. Unsigned 32 bit number containing + * bits for all 32 antennas. + * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE2: Bit 2 of edge + * amplifier gain index. Unsigned 32 bit number containing + * bits for all 32 antennas. + * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_HI: Phase values + * for first 16 antennas, 2 bits per antenna. + * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_LO: Phase values + * for last 16 antennas, 2 bits per antenna. + * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16: Contains + * DTYPE values (3 bits) for each distribution amplifier, followed + * by X16 switch bits for each distribution amplifier. There are + * total of 8 distribution amplifiers. + */ +enum qca_wlan_vendor_attr_dmg_rf_sector_cfg { + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX = 1, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE0 = 2, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE1 = 3, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE2 = 4, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_HI = 5, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_LO = 6, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16 = 7, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MAX = + QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST - 1 +}; + +enum qca_wlan_vendor_attr_ll_stats_set { + QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_INVALID = 0, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD = 1, + QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING = 2, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX = + QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_AFTER_LAST - 1, +}; + +enum qca_wlan_vendor_attr_ll_stats_clr { + QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_INVALID = 0, + /* Unsigned 32bit bitmap for clearing statistics + * All radio statistics 0x00000001 + * cca_busy_time (within radio statistics) 0x00000002 + * All channel stats (within radio statistics) 0x00000004 + * All scan statistics (within radio statistics) 0x00000008 + * All interface statistics 0x00000010 + * All tx rate statistics (within interface statistics) 0x00000020 + * All ac statistics (with in interface statistics) 0x00000040 + * All contention (min, max, avg) statistics (within ac statisctics) + * 0x00000080. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK = 1, + /* Unsigned 8 bit value: Request to stop statistics collection */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ = 2, + + /* Unsigned 32 bit bitmap: Response from the driver + * for the cleared statistics + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK = 3, + /* Unsigned 8 bit value: Response from driver/firmware + * for the stop request + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP = 4, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX = + QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_AFTER_LAST - 1, +}; + +enum qca_wlan_vendor_attr_ll_stats_get { + QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_INVALID = 0, + /* Unsigned 32 bit value provided by the caller issuing the GET stats + * command. When reporting the stats results, the driver uses the same + * value to indicate which GET request the results correspond to. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID = 1, + /* Unsigned 32 bit value - bit mask to identify what statistics are + * requested for retrieval. + * Radio Statistics 0x00000001 + * Interface Statistics 0x00000020 + * All Peer Statistics 0x00000040 + * Peer Statistics 0x00000080 + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK = 2, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX = + QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_AFTER_LAST - 1, +}; + +enum qca_wlan_vendor_attr_ll_stats_results { + QCA_WLAN_VENDOR_ATTR_LL_STATS_INVALID = 0, + /* Unsigned 32bit value. Used by the driver; must match the request id + * provided with the QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET command. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_REQ_ID = 1, + + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_BEACON_RX = 2, + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_RX = 3, + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_RX = 4, + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_TX = 5, + /* Signed 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_MGMT = 6, + /* Signed 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_DATA = 7, + /* Signed 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_ACK = 8, + + /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_* are + * nested within the interface stats. + */ + + /* Interface mode, e.g., STA, SOFTAP, IBSS, etc. + * Type = enum wifi_interface_mode. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MODE = 9, + /* Interface MAC address. An array of 6 Unsigned int8 */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MAC_ADDR = 10, + /* Type = enum wifi_connection_state, e.g., DISCONNECTED, + * AUTHENTICATING, etc. valid for STA, CLI only. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_STATE = 11, + /* Type = enum wifi_roam_state. Roaming state, e.g., IDLE or ACTIVE + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_ROAMING = 12, + /* Unsigned 32 bit value. WIFI_CAPABILITY_XXX */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_CAPABILITIES = 13, + /* NULL terminated SSID. An array of 33 Unsigned 8bit values */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_SSID = 14, + /* BSSID. An array of 6 unsigned 8 bit values */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_BSSID = 15, + /* Country string advertised by AP. An array of 3 unsigned 8 bit + * values. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_AP_COUNTRY_STR = 16, + /* Country string for this association. An array of 3 unsigned 8 bit + * values. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_COUNTRY_STR = 17, + + /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_* could + * be nested within the interface stats. + */ + + /* Type = enum wifi_traffic_ac, e.g., V0, VI, BE and BK */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_AC = 18, + /* Unsigned int 32 value corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MPDU = 19, + /* Unsigned int 32 value corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MPDU = 20, + /* Unsigned int 32 value corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MCAST = 21, + /* Unsigned int 32 value corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MCAST = 22, + /* Unsigned int 32 value corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_AMPDU = 23, + /* Unsigned int 32 value corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_AMPDU = 24, + /* Unsigned int 32 value corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_MPDU_LOST = 25, + /* Unsigned int 32 value corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES = 26, + /* Unsigned int 32 value corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_SHORT = 27, + /* Unsigned int 32 values corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_LONG = 28, + /* Unsigned int 32 values corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MIN = 29, + /* Unsigned int 32 values corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MAX = 30, + /* Unsigned int 32 values corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_AVG = 31, + /* Unsigned int 32 values corresponding to respective AC */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_NUM_SAMPLES = 32, + /* Unsigned 32 bit value. Number of peers */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS = 33, + + /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_* are + * nested within the interface stats. + */ + + /* Type = enum wifi_peer_type. Peer type, e.g., STA, AP, P2P GO etc. */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_TYPE = 34, + /* MAC addr corresponding to respective peer. An array of 6 unsigned + * 8 bit values. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_MAC_ADDRESS = 35, + /* Unsigned int 32 bit value representing capabilities corresponding + * to respective peer. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_CAPABILITIES = 36, + /* Unsigned 32 bit value. Number of rates */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES = 37, + + /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_* + * are nested within the rate stat. + */ + + /* Wi-Fi Rate - separate attributes defined for individual fields */ + + /* Unsigned int 8 bit value; 0: OFDM, 1:CCK, 2:HT 3:VHT 4..7 reserved */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_PREAMBLE = 38, + /* Unsigned int 8 bit value; 0:1x1, 1:2x2, 3:3x3, 4:4x4 */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_NSS = 39, + /* Unsigned int 8 bit value; 0:20 MHz, 1:40 MHz, 2:80 MHz, 3:160 MHz */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BW = 40, + /* Unsigned int 8 bit value; OFDM/CCK rate code would be as per IEEE Std + * in the units of 0.5 Mbps HT/VHT it would be MCS index + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MCS_INDEX = 41, + + /* Unsigned 32 bit value. Bit rate in units of 100 kbps */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BIT_RATE = 42, + + /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_STAT_* could be + * nested within the peer info stats. + */ + + /* Unsigned int 32 bit value. Number of successfully transmitted data + * packets, i.e., with ACK received corresponding to the respective + * rate. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_TX_MPDU = 43, + /* Unsigned int 32 bit value. Number of received data packets + * corresponding to the respective rate. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RX_MPDU = 44, + /* Unsigned int 32 bit value. Number of data packet losses, i.e., no ACK + * received corresponding to the respective rate. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MPDU_LOST = 45, + /* Unsigned int 32 bit value. Total number of data packet retries for + * the respective rate. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES = 46, + /* Unsigned int 32 bit value. Total number of short data packet retries + * for the respective rate. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_SHORT = 47, + /* Unsigned int 32 bit value. Total number of long data packet retries + * for the respective rate. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_LONG = 48, + + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID = 49, + /* Unsigned 32 bit value. Total number of msecs the radio is awake + * accruing over time. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME = 50, + /* Unsigned 32 bit value. Total number of msecs the radio is + * transmitting accruing over time. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME = 51, + /* Unsigned 32 bit value. Total number of msecs the radio is in active + * receive accruing over time. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME = 52, + /* Unsigned 32 bit value. Total number of msecs the radio is awake due + * to all scan accruing over time. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN = 53, + /* Unsigned 32 bit value. Total number of msecs the radio is awake due + * to NAN accruing over time. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD = 54, + /* Unsigned 32 bit value. Total number of msecs the radio is awake due + * to GSCAN accruing over time. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_GSCAN = 55, + /* Unsigned 32 bit value. Total number of msecs the radio is awake due + * to roam scan accruing over time. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN = 56, + /* Unsigned 32 bit value. Total number of msecs the radio is awake due + * to PNO scan accruing over time. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN = 57, + /* Unsigned 32 bit value. Total number of msecs the radio is awake due + * to Hotspot 2.0 scans and GAS exchange accruing over time. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20 = 58, + /* Unsigned 32 bit value. Number of channels. */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS = 59, + + /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_* could + * be nested within the channel stats. + */ + + /* Type = enum wifi_channel_width. Channel width, e.g., 20, 40, 80 */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH = 60, + /* Unsigned 32 bit value. Primary 20 MHz channel. */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ = 61, + /* Unsigned 32 bit value. Center frequency (MHz) first segment. */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0 = 62, + /* Unsigned 32 bit value. Center frequency (MHz) second segment. */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1 = 63, + + /* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_* could be + * nested within the radio stats. + */ + + /* Unsigned int 32 bit value representing total number of msecs the + * radio is awake on that channel accruing over time, corresponding to + * the respective channel. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME = 64, + /* Unsigned int 32 bit value representing total number of msecs the CCA + * register is busy accruing over time corresponding to the respective + * channel. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME = 65, + + QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS = 66, + + /* Signifies the nested list of channel attributes + * QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_* + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO = 67, + + /* Signifies the nested list of peer info attributes + * QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_* + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO = 68, + + /* Signifies the nested list of rate info attributes + * QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_* + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_RATE_INFO = 69, + + /* Signifies the nested list of wmm info attributes + * QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_* + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_INFO = 70, + + /* Unsigned 8 bit value. Used by the driver; if set to 1, it indicates + * that more stats, e.g., peers or radio, are to follow in the next + * QCA_NL80211_VENDOR_SUBCMD_LL_STATS_*_RESULTS event. + * Otherwise, it is set to 0. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_MORE_DATA = 71, + + /* Unsigned 64 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_AVERAGE_TSF_OFFSET = 72, + + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_DETECTED = 73, + + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_AVG_NUM_FRAMES_LEAKED = 74, + + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_GUARD_TIME = 75, + + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE = 76, + + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_TX_LEVELS = 77, + + /* Number of msecs the radio spent in transmitting for each power level + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL = 78, + + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_SUCC_CNT = 79, + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_FAIL_CNT = 80, + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_SUCC_CNT = 81, + /* Unsigned 32 bit value */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_FAIL_CNT = 82, + + /* Unsigned int 32 value. + * Pending MSDUs corresponding to respective AC. + */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_PENDING_MSDU = 83, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX = + QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST - 1, +}; + +enum qca_wlan_vendor_attr_ll_stats_type { + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_INVALID = 0, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_RADIO = 1, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_IFACE = 2, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_PEERS = 3, + + /* keep last */ + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_AFTER_LAST, + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_MAX = + QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_tdls_configuration - Attributes for + * TDLS configuration to the host driver. + * + * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE: Configure the TDLS trigger + * mode in the host driver. enum qca_wlan_vendor_tdls_trigger_mode + * represents the different TDLS trigger modes. + * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD: Duration (u32) within + * which QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD number + * of packets shall meet the criteria for implicit TDLS setup. + * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD: Number (u32) of Tx/Rx packets + * within a duration QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD + * to initiate a TDLS setup. + * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_DISCOVERY_PERIOD: Time (u32) to initiate + * a TDLS Discovery to the peer. + * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX_DISCOVERY_ATTEMPT: Max number (u32) of + * discovery attempts to know the TDLS capability of the peer. A peer is + * marked as TDLS not capable if there is no response for all the attempts. + * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT: Represents a duration (u32) + * within which QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD + * number of TX / RX frames meet the criteria for TDLS teardown. + * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD: Minimum number (u32) + * of Tx/Rx packets within a duration + * QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT to tear down a TDLS link. + * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_SETUP_RSSI_THRESHOLD: Threshold + * corresponding to the RSSI of the peer below which a TDLS setup is + * triggered. + * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TEARDOWN_RSSI_THRESHOLD: Threshold + * corresponding to the RSSI of the peer above which a TDLS teardown is + * triggered. + */ +enum qca_wlan_vendor_attr_tdls_configuration { + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE = 1, + + /* Attributes configuring the TDLS Implicit Trigger */ + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD = 2, + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD = 3, + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_DISCOVERY_PERIOD = 4, + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX_DISCOVERY_ATTEMPT = 5, + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT = 6, + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD = 7, + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_SETUP_RSSI_THRESHOLD = 8, + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TEARDOWN_RSSI_THRESHOLD = 9, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX = + QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_tdls_trigger_mode: Represents the TDLS trigger mode in + * the driver + * + * The following are the different values for + * QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE. + * + * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT: The trigger to initiate/teardown + * the TDLS connection to a respective peer comes from the user space. + * wpa_supplicant provides the commands TDLS_SETUP, TDLS_TEARDOWN, + * TDLS_DISCOVER to do this. + * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT: Host driver triggers this TDLS + * setup/teardown to the eligible peer once the configured criteria + * (such as TX/RX threshold, RSSI) is met. The attributes + * in QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IMPLICIT_PARAMS correspond to + * the different configuration criteria for the TDLS trigger from the + * host driver. + * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL: Enables the driver to trigger + * the TDLS setup / teardown through the implicit mode only to the + * configured MAC addresses (wpa_supplicant, with tdls_external_control=1, + * configures the MAC address through TDLS_SETUP / TDLS_TEARDOWN commands). + * External mode works on top of the implicit mode. Thus the host driver + * is expected to configure in TDLS Implicit mode too to operate in + * External mode. + * Configuring External mode alone without Implicit mode is invalid. + * + * All the above implementations work as expected only when the host driver + * advertises the capability WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP - representing + * that the TDLS message exchange is not internal to the host driver, but + * depends on wpa_supplicant to do the message exchange. + */ +enum qca_wlan_vendor_tdls_trigger_mode { + QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT = 1 << 0, + QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT = 1 << 1, + QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL = 1 << 2, +}; + +/** + * enum qca_vendor_attr_sar_limits_selections - Source of SAR power limits + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0: Select SAR profile #0 + * that is hard-coded in the Board Data File (BDF). + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1: Select SAR profile #1 + * that is hard-coded in the Board Data File (BDF). + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2: Select SAR profile #2 + * that is hard-coded in the Board Data File (BDF). + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3: Select SAR profile #3 + * that is hard-coded in the Board Data File (BDF). + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4: Select SAR profile #4 + * that is hard-coded in the Board Data File (BDF). + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE: Do not select any + * source of SAR power limits, thereby disabling the SAR power + * limit feature. + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER: Select the SAR power + * limits configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR. + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0: Select the SAR power + * limits version 2.0 configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR. + * + * This enumerates the valid set of values that may be supplied for + * attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT in an instance of + * the %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor command or in + * the response to an instance of the + * %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS vendor command. + */ +enum qca_vendor_attr_sar_limits_selections { + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0 = 0, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1 = 1, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2 = 2, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3 = 3, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4 = 4, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE = 5, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER = 6, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0 = 7, +}; + +/** + * enum qca_vendor_attr_sar_limits_spec_modulations - + * SAR limits specification modulation + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK - + * CCK modulation + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM - + * OFDM modulation + * + * This enumerates the valid set of values that may be supplied for + * attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION in an + * instance of attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC in an + * instance of the %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor + * command or in the response to an instance of the + * %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS vendor command. + */ +enum qca_vendor_attr_sar_limits_spec_modulations { + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK = 0, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM = 1, +}; + +/** + * enum qca_vendor_attr_sar_limits - Attributes for SAR power limits + * + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE: Optional (u32) value to + * select which SAR power limit table should be used. Valid + * values are enumerated in enum + * %qca_vendor_attr_sar_limits_selections. The existing SAR + * power limit selection is unchanged if this attribute is not + * present. + * + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS: Optional (u32) value + * which specifies the number of SAR power limit specifications + * which will follow. + * + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC: Nested array of SAR power + * limit specifications. The number of specifications is + * specified by @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS. Each + * specification contains a set of + * QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_* attributes. A + * specification is uniquely identified by the attributes + * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND, + * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN, and + * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION and always + * contains as a payload the attribute + * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT, + * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX. + * Either %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT or + * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX is + * needed based upon the value of + * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE. + * + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND: Optional (u32) value to + * indicate for which band this specification applies. Valid + * values are enumerated in enum %nl80211_band (although not all + * bands may be supported by a given device). If the attribute is + * not supplied then the specification will be applied to all + * supported bands. + * + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN: Optional (u32) value + * to indicate for which antenna chain this specification + * applies, i.e. 1 for chain 1, 2 for chain 2, etc. If the + * attribute is not supplied then the specification will be + * applied to all chains. + * + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION: Optional (u32) + * value to indicate for which modulation scheme this + * specification applies. Valid values are enumerated in enum + * %qca_vendor_attr_sar_limits_spec_modulations. If the attribute + * is not supplied then the specification will be applied to all + * modulation schemes. + * + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT: Required (u32) + * value to specify the actual power limit value in units of 0.5 + * dBm (i.e., a value of 11 represents 5.5 dBm). + * This is required, when %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT is + * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER. + * + * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX: Required (u32) + * value to indicate SAR V2 indices (0 - 11) to select SAR V2 profiles. + * This is required, when %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT is + * %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_V2_0. + * + * These attributes are used with %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS + * and %QCA_NL80211_VENDOR_SUBCMD_GET_SAR_LIMITS. + */ +enum qca_vendor_attr_sar_limits { + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE = 1, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS = 2, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC = 3, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND = 4, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN = 5, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION = 6, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT = 7, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT_INDEX = 8, + + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_MAX = + QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_get_wifi_info: Attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO sub command. + */ +enum qca_wlan_vendor_attr_get_wifi_info { + QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION = 1, + QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION = 2, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX = + QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_AFTER_LAST - 1, +}; + +/* + * enum qca_wlan_vendor_attr_wifi_logger_start: Attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START sub command. + */ +enum qca_wlan_vendor_attr_wifi_logger_start { + QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID = 1, + QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL = 2, + QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS = 3, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_GET_MAX = + QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_AFTER_LAST - 1, +}; + +enum qca_wlan_vendor_attr_logger_results { + QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_INVALID = 0, + + /* Unsigned 32-bit value; must match the request Id supplied by + * Wi-Fi HAL in the corresponding subcmd NL msg. + */ + QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_REQUEST_ID = 1, + + /* Unsigned 32-bit value; used to indicate the size of memory + * dump to be allocated. + */ + QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_MEMDUMP_SIZE = 2, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_MAX = + QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_AFTER_LAST - 1, +}; + +enum qca_wlan_vendor_attr_roaming_config_params { + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_INVALID = 0, + + QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD = 1, + QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID = 2, + + /* Attributes for wifi_set_ssid_white_list */ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS = 3, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST = 4, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID = 5, + + /* Attributes for set_roam_params */ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD = 6, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD = 7, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR = 8, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR = 9, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST = 10, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS = 11, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER = 12, + + /* Attribute for set_lazy_roam */ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE = 13, + + /* Attribute for set_lazy_roam with preferences */ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS = 14, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID = 15, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID = 16, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER = 17, + + /* Attribute for set_blacklist bssid params */ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS = 18, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID = 19, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID = 20, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX = + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST - 1, +}; + +/* + * enum qca_wlan_vendor_attr_roam_subcmd: Attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_ROAM sub command. + */ +enum qca_wlan_vendor_attr_roam_subcmd { + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SSID_WHITE_LIST = 1, + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_GSCAN_ROAM_PARAMS = 2, + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_LAZY_ROAM = 3, + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BSSID_PREFS = 4, + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BSSID_PARAMS = 5, + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID = 6, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_MAX = + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_AFTER_LAST - 1, +}; + +enum qca_wlan_vendor_attr_gscan_config_params { + QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_INVALID = 0, + + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID = 1, + + /* Attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_VALID_CHANNELS sub command. + */ + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND + = 2, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS + = 3, + + /* Attributes for input params used by + * QCA_NL80211_VENDOR_SUBCMD_GSCAN_START sub command. + */ + + /* Unsigned 32-bit value; channel frequency */ + QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_CHANNEL = 4, + /* Unsigned 32-bit value; dwell time in ms. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_DWELL_TIME = 5, + /* Unsigned 8-bit value; 0: active; 1: passive; N/A for DFS */ + QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_PASSIVE = 6, + /* Unsigned 8-bit value; channel class */ + QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_CLASS = 7, + + /* Unsigned 8-bit value; bucket index, 0 based */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_INDEX = 8, + /* Unsigned 8-bit value; band. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_BAND = 9, + /* Unsigned 32-bit value; desired period, in ms. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_PERIOD = 10, + /* Unsigned 8-bit value; report events semantics. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_REPORT_EVENTS = 11, + /* Unsigned 32-bit value. Followed by a nested array of + * GSCAN_CHANNEL_SPEC_* attributes. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS = 12, + + /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_* attributes. + * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC = 13, + + /* Unsigned 32-bit value; base timer period in ms. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_BASE_PERIOD = 14, + /* Unsigned 32-bit value; number of APs to store in each scan in the + * BSSID/RSSI history buffer (keep the highest RSSI APs). + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_MAX_AP_PER_SCAN = 15, + /* Unsigned 8-bit value; in %, when scan buffer is this much full, wake + * up AP. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_PERCENT + = 16, + + /* Unsigned 8-bit value; number of scan bucket specs; followed by a + * nested array of_GSCAN_BUCKET_SPEC_* attributes and values. The size + * of the array is determined by NUM_BUCKETS. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS = 17, + + /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_* attributes. + * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC = 18, + + /* Unsigned 8-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH + = 19, + /* Unsigned 32-bit value; maximum number of results to be returned. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_MAX + = 20, + + /* An array of 6 x unsigned 8-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_BSSID = 21, + /* Signed 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_RSSI_LOW = 22, + /* Signed 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH = 23, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_CHANNEL = 24, + + /* Number of hotlist APs as unsigned 32-bit value, followed by a nested + * array of AP_THRESHOLD_PARAM attributes and values. The size of the + * array is determined by NUM_AP. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BSSID_HOTLIST_PARAMS_NUM_AP = 25, + + /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_* attributes. + * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM = 26, + + /* Unsigned 32-bit value; number of samples for averaging RSSI. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_RSSI_SAMPLE_SIZE + = 27, + /* Unsigned 32-bit value; number of samples to confirm AP loss. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_LOST_AP_SAMPLE_SIZE + = 28, + /* Unsigned 32-bit value; number of APs breaching threshold. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_MIN_BREACHING = 29, + /* Unsigned 32-bit value; number of APs. Followed by an array of + * AP_THRESHOLD_PARAM attributes. Size of the array is NUM_AP. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_NUM_AP = 30, + /* Unsigned 32-bit value; number of samples to confirm AP loss. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE + = 31, + /* Unsigned 32-bit value. If max_period is non zero or different than + * period, then this bucket is an exponential backoff bucket. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_MAX_PERIOD = 32, + /* Unsigned 32-bit value. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_BASE = 33, + /* Unsigned 32-bit value. For exponential back off bucket, number of + * scans to perform for a given period. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_STEP_COUNT = 34, + /* Unsigned 8-bit value; in number of scans, wake up AP after these + * many scans. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_NUM_SCANS + = 35, + + /* Attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SSID_HOTLIST sub command. + */ + /* Unsigned 3-2bit value; number of samples to confirm SSID loss. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_LOST_SSID_SAMPLE_SIZE + = 36, + /* Number of hotlist SSIDs as unsigned 32-bit value, followed by a + * nested array of SSID_THRESHOLD_PARAM_* attributes and values. The + * size of the array is determined by NUM_SSID. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_NUM_SSID = 37, + /* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_* + * attributes. + * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_NUM_SSID + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM = 38, + + /* An array of 33 x unsigned 8-bit value; NULL terminated SSID */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_SSID = 39, + /* Unsigned 8-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_BAND = 40, + /* Signed 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_RSSI_LOW = 41, + /* Signed 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_RSSI_HIGH = 42, + /* Unsigned 32-bit value; a bitmask with additional gscan config flag. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_CONFIGURATION_FLAGS = 43, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_MAX = + QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_AFTER_LAST - 1, +}; + +enum qca_wlan_vendor_attr_gscan_results { + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_INVALID = 0, + + /* Unsigned 32-bit value; must match the request Id supplied by + * Wi-Fi HAL in the corresponding subcmd NL msg. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_REQUEST_ID = 1, + + /* Unsigned 32-bit value; used to indicate the status response from + * firmware/driver for the vendor sub-command. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_STATUS = 2, + + /* GSCAN Valid Channels attributes */ + /* Unsigned 32bit value; followed by a nested array of CHANNELS. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_CHANNELS = 3, + /* An array of NUM_CHANNELS x unsigned 32-bit value integers + * representing channel numbers. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CHANNELS = 4, + + /* GSCAN Capabilities attributes */ + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE = 5, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS = 6, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN + = 7, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE + = 8, + /* Signed 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD + = 9, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS = 10, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS + = 11, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES + = 12, + + /* GSCAN Attributes used with + * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_RESULTS_AVAILABLE sub-command. + */ + + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE = 13, + + /* GSCAN attributes used with + * QCA_NL80211_VENDOR_SUBCMD_GSCAN_FULL_SCAN_RESULT sub-command. + */ + + /* An array of NUM_RESULTS_AVAILABLE x + * QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_* + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST = 14, + + /* Unsigned 64-bit value; age of sample at the time of retrieval */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_TIME_STAMP = 15, + /* 33 x unsigned 8-bit value; NULL terminated SSID */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_SSID = 16, + /* An array of 6 x unsigned 8-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_BSSID = 17, + /* Unsigned 32-bit value; channel frequency in MHz */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_CHANNEL = 18, + /* Signed 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RSSI = 19, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RTT = 20, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RTT_SD = 21, + /* Unsigned 16-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_BEACON_PERIOD = 22, + /* Unsigned 16-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_CAPABILITY = 23, + /* Unsigned 32-bit value; size of the IE DATA blob */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_IE_LENGTH = 24, + /* An array of IE_LENGTH x unsigned 8-bit value; blob of all the + * information elements found in the beacon; this data should be a + * packed list of wifi_information_element objects, one after the + * other. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_IE_DATA = 25, + + /* Unsigned 8-bit value; set by driver to indicate more scan results are + * available. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_MORE_DATA = 26, + + /* GSCAN attributes for + * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_EVENT sub-command. + */ + /* Unsigned 8-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_EVENT_TYPE = 27, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_EVENT_STATUS = 28, + + /* GSCAN attributes for + * QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_FOUND sub-command. + */ + /* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE + * to indicate number of results. + * Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the + * list of results. + */ + + /* GSCAN attributes for + * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SIGNIFICANT_CHANGE sub-command. + */ + /* An array of 6 x unsigned 8-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_BSSID = 29, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_CHANNEL + = 30, + /* Unsigned 32-bit value. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_NUM_RSSI + = 31, + /* A nested array of signed 32-bit RSSI values. Size of the array is + * determined by (NUM_RSSI of SIGNIFICANT_CHANGE_RESULT_NUM_RSSI. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_RSSI_LIST + = 32, + + /* GSCAN attributes used with + * QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CACHED_RESULTS sub-command. + */ + /* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE + * to indicate number of gscan cached results returned. + * Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_LIST to indicate + * the list of gscan cached results. + */ + + /* An array of NUM_RESULTS_AVAILABLE x + * QCA_NL80211_VENDOR_ATTR_GSCAN_CACHED_RESULTS_* + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_LIST = 33, + /* Unsigned 32-bit value; a unique identifier for the scan unit. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_SCAN_ID = 34, + /* Unsigned 32-bit value; a bitmask w/additional information about scan. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_FLAGS = 35, + /* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE + * to indicate number of wifi scan results/bssids retrieved by the scan. + * Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the + * list of wifi scan results returned for each cached result block. + */ + + /* GSCAN attributes for + * QCA_NL80211_VENDOR_SUBCMD_PNO_NETWORK_FOUND sub-command. + */ + /* Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE for + * number of results. + * Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the nested + * list of wifi scan results returned for each + * wifi_passpoint_match_result block. + * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE. + */ + + /* GSCAN attributes for + * QCA_NL80211_VENDOR_SUBCMD_PNO_PASSPOINT_NETWORK_FOUND sub-command. + */ + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_NETWORK_FOUND_NUM_MATCHES + = 36, + /* A nested array of + * QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_* + * attributes. Array size = + * *_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_NETWORK_FOUND_NUM_MATCHES. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_RESULT_LIST = 37, + + /* Unsigned 32-bit value; network block id for the matched network */ + QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ID = 38, + /* Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the nested + * list of wifi scan results returned for each + * wifi_passpoint_match_result block. + */ + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP_LEN = 39, + /* An array size of PASSPOINT_MATCH_ANQP_LEN of unsigned 8-bit values; + * ANQP data in the information_element format. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP = 40, + + /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS = 41, + /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS = 42, + /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID + = 43, + /* Unsigned 32-bit value; a GSCAN Capabilities attribute. */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID + = 44, + + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_BUCKETS_SCANNED = 45, + + /* Unsigned 32-bit value; a GSCAN Capabilities attribute. + * This is used to limit the maximum number of BSSIDs while sending + * the vendor command QCA_NL80211_VENDOR_SUBCMD_ROAM with attributes + * QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID and + * QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID. + */ + QCA_WLAN_VENDOR_ATTR_GSCAN_MAX_NUM_BLACKLISTED_BSSID = 46, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_MAX = + QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_AFTER_LAST - 1, +}; + +enum qca_wlan_vendor_attr_pno_config_params { + QCA_WLAN_VENDOR_ATTR_PNO_INVALID = 0, + /* Attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_PNO_SET_PASSPOINT_LIST sub command. + */ + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM = 1, + /* Array of nested QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_* + * attributes. Array size = + * QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM. + */ + QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NETWORK_ARRAY = 2, + + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ID = 3, + /* An array of 256 x unsigned 8-bit value; NULL terminated UTF-8 encoded + * realm, 0 if unspecified. + */ + QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_REALM = 4, + /* An array of 16 x unsigned 32-bit value; roaming consortium ids to + * match, 0 if unspecified. + */ + QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_CNSRTM_ID = 5, + /* An array of 6 x unsigned 8-bit value; MCC/MNC combination, 0s if + * unspecified. + */ + QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_PLMN = 6, + + /* Attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_PNO_SET_LIST sub command. + */ + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS = 7, + /* Array of nested + * QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_* + * attributes. Array size = + * QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS. + */ + QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORKS_LIST = 8, + /* An array of 33 x unsigned 8-bit value; NULL terminated SSID */ + QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID = 9, + /* Signed 8-bit value; threshold for considering this SSID as found, + * required granularity for this threshold is 4 dBm to 8 dBm. + */ + QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_RSSI_THRESHOLD + = 10, + /* Unsigned 8-bit value; WIFI_PNO_FLAG_XXX */ + QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS = 11, + /* Unsigned 8-bit value; auth bit field for matching WPA IE */ + QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT = 12, + /* Unsigned 8-bit to indicate ePNO type; + * It takes values from qca_wlan_epno_type + */ + QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_TYPE = 13, + + /* Nested attribute to send the channel list */ + QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_CHANNEL_LIST = 14, + + /* Unsigned 32-bit value; indicates the interval between PNO scan + * cycles in msec. + */ + QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_SCAN_INTERVAL = 15, + QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI = 16, + QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI = 17, + QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX = 18, + QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS = 19, + QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS = 20, + QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS = 21, + QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS = 22, + /* Unsigned 32-bit value, representing the PNO Request ID */ + QCA_WLAN_VENDOR_ATTR_PNO_CONFIG_REQUEST_ID = 23, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_PNO_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_PNO_MAX = + QCA_WLAN_VENDOR_ATTR_PNO_AFTER_LAST - 1, +}; + +/** + * qca_wlan_vendor_acs_select_reason: This represents the different reasons why + * the ACS has to be triggered. These values are used by + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON and + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON + */ +enum qca_wlan_vendor_acs_select_reason { + /* Represents the reason that the ACS triggered during the AP start */ + QCA_WLAN_VENDOR_ACS_SELECT_REASON_INIT, + /* Represents the reason that DFS found with the current channel */ + QCA_WLAN_VENDOR_ACS_SELECT_REASON_DFS, + /* Represents the reason that LTE co-exist in the current band. */ + QCA_WLAN_VENDOR_ACS_SELECT_REASON_LTE_COEX, +}; + +/** + * qca_wlan_vendor_attr_external_acs_policy: Attribute values for + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY to the vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This represents the + * external ACS policies to select the channels w.r.t. the PCL weights. + * (QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL represents the channels and + * their PCL weights.) + * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_MANDATORY: Mandatory to + * select a channel with non-zero PCL weight. + * @QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_PREFERRED: Prefer a + * channel with non-zero PCL weight. + * + */ +enum qca_wlan_vendor_attr_external_acs_policy { + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_PREFERRED, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_POLICY_PCL_MANDATORY, +}; + +/** + * qca_wlan_vendor_channel_prop_flags: This represent the flags for a channel. + * This is used by QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS. + */ +enum qca_wlan_vendor_channel_prop_flags { + /* Bits 0, 1, 2, and 3 are reserved */ + + /* Turbo channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_TURBO = 1 << 4, + /* CCK channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_CCK = 1 << 5, + /* OFDM channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_OFDM = 1 << 6, + /* 2.4 GHz spectrum channel. */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_2GHZ = 1 << 7, + /* 5 GHz spectrum channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_5GHZ = 1 << 8, + /* Only passive scan allowed */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_PASSIVE = 1 << 9, + /* Dynamic CCK-OFDM channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_DYN = 1 << 10, + /* GFSK channel (FHSS PHY) */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_GFSK = 1 << 11, + /* Radar found on channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_RADAR = 1 << 12, + /* 11a static turbo channel only */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_STURBO = 1 << 13, + /* Half rate channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HALF = 1 << 14, + /* Quarter rate channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_QUARTER = 1 << 15, + /* HT 20 channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT20 = 1 << 16, + /* HT 40 with extension channel above */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40PLUS = 1 << 17, + /* HT 40 with extension channel below */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40MINUS = 1 << 18, + /* HT 40 intolerant */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40INTOL = 1 << 19, + /* VHT 20 channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT20 = 1 << 20, + /* VHT 40 with extension channel above */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT40PLUS = 1 << 21, + /* VHT 40 with extension channel below */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT40MINUS = 1 << 22, + /* VHT 80 channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT80 = 1 << 23, + /* HT 40 intolerant mark bit for ACS use */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40INTOLMARK = 1 << 24, + /* Channel temporarily blocked due to noise */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_BLOCKED = 1 << 25, + /* VHT 160 channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT160 = 1 << 26, + /* VHT 80+80 channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT80_80 = 1 << 27, + /* HE 20 channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE20 = 1 << 28, + /* HE 40 with extension channel above */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40PLUS = 1 << 29, + /* HE 40 with extension channel below */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40MINUS = 1 << 30, + /* HE 40 intolerant */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOL = 1 << 31, +}; + +/** + * qca_wlan_vendor_channel_prop_flags_2: This represents the flags for a + * channel, and is a continuation of qca_wlan_vendor_channel_prop_flags. This is + * used by QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2. + */ +enum qca_wlan_vendor_channel_prop_flags_2 { + /* HE 40 intolerant mark bit for ACS use */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOLMARK = 1 << 0, + /* HE 80 channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE80 = 1 << 1, + /* HE 160 channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE160 = 1 << 2, + /* HE 80+80 channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE80_80 = 1 << 3, +}; + +/** + * qca_wlan_vendor_channel_prop_flags_ext: This represent the extended flags for + * each channel. This is used by + * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAG_EXT. + */ +enum qca_wlan_vendor_channel_prop_flags_ext { + /* Radar found on channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_RADAR_FOUND = 1 << 0, + /* DFS required on channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS = 1 << 1, + /* DFS required on channel for 2nd band of 80+80 */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS_CFREQ2 = 1 << 2, + /* If channel has been checked for DFS */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS_CLEAR = 1 << 3, + /* Excluded in 802.11d */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_11D_EXCLUDED = 1 << 4, + /* Channel Switch Announcement received on this channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_CSA_RECEIVED = 1 << 5, + /* Ad-hoc is not allowed */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_ADHOC = 1 << 6, + /* Station only channel */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_HOSTAP = 1 << 7, + /* DFS radar history for slave device (STA mode) */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_HISTORY_RADAR = 1 << 8, + /* DFS CAC valid for slave device (STA mode) */ + QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_CAC_VALID = 1 << 9, +}; + +/** + * qca_wlan_vendor_external_acs_event_chan_info_attr: Represents per channel + * information. These attributes are sent as part of + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO. Each set of the following + * attributes correspond to a single channel. + */ +enum qca_wlan_vendor_external_acs_event_chan_info_attr { + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_INVALID = 0, + + /* A bitmask (u32) with flags specified in + * enum qca_wlan_vendor_channel_prop_flags. + */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS = 1, + /* A bitmask (u32) with flags specified in + * enum qca_wlan_vendor_channel_prop_flags_ext. + */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAG_EXT = 2, + /* frequency in MHz (u32) */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ = 3, + /* maximum regulatory transmission power (u32) */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_REG_POWER = 4, + /* maximum transmission power (u32) */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_POWER = 5, + /* minimum transmission power (u32) */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MIN_POWER = 6, + /* regulatory class id (u8) */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_REG_CLASS_ID = 7, + /* maximum antenna gain in (u8) */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_ANTENNA_GAIN = 8, + /* VHT segment 0 (u8) */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0 = 9, + /* VHT segment 1 (u8) */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1 = 10, + /* A bitmask (u32) with flags specified in + * enum qca_wlan_vendor_channel_prop_flags_2. + */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS_2 = 11, + + /* keep last */ + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST, + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX = + QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST - 1, +}; + +/** + * qca_wlan_vendor_attr_pcl: Represents attributes for + * preferred channel list (PCL). These attributes are sent as part of + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL and + * QCA_NL80211_VENDOR_SUBCMD_GET_PREFERRED_FREQ_LIST. + */ +enum qca_wlan_vendor_attr_pcl { + QCA_WLAN_VENDOR_ATTR_PCL_INVALID = 0, + + /* Channel number (u8) */ + QCA_WLAN_VENDOR_ATTR_PCL_CHANNEL = 1, + /* Channel weightage (u8) */ + QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT = 2, + /* Channel frequency (u32) in MHz */ + QCA_WLAN_VENDOR_ATTR_PCL_FREQ = 3, + /* Channel flags (u32) + * bit 0 set: channel to be used for GO role, + * bit 1 set: channel to be used on CLI role, + * bit 2 set: channel must be considered for operating channel + * selection & peer chosen operating channel should be + * one of the channels with this flag set, + * bit 3 set: channel should be excluded in GO negotiation + */ + QCA_WLAN_VENDOR_ATTR_PCL_FLAG = 4, +}; + +/** + * qca_wlan_vendor_attr_external_acs_event: Attribute to vendor sub-command + * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This attribute will be sent by + * host driver. + */ +enum qca_wlan_vendor_attr_external_acs_event { + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_INVALID = 0, + + /* This reason (u8) refers to enum qca_wlan_vendor_acs_select_reason. + * This helps ACS module to understand why ACS needs to be started. + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON = 1, + /* Flag attribute to indicate if driver supports spectral scanning */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_SPECTRAL_SUPPORTED = 2, + /* Flag attribute to indicate if 11ac is offloaded to firmware */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_OFFLOAD_ENABLED = 3, + /* Flag attribute to indicate if driver provides additional channel + * capability as part of scan operation + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_ADD_CHAN_STATS_SUPPORT = 4, + /* Flag attribute to indicate interface status is UP */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_AP_UP = 5, + /* Operating mode (u8) of interface. Takes one of enum nl80211_iftype + * values. + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_SAP_MODE = 6, + /* Channel width (u8). It takes one of enum nl80211_chan_width values. + * This is the upper bound of channel width. ACS logic should try to get + * a channel with the specified width and if not found, look for lower + * values. + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_WIDTH = 7, + /* This (u8) will hold values of one of enum nl80211_bands */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_BAND = 8, + /* PHY/HW mode (u8). Takes one of enum qca_wlan_vendor_acs_hw_mode + * values + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PHY_MODE = 9, + /* Array of (u32) supported frequency list among which ACS should choose + * best frequency. + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_FREQ_LIST = 10, + /* Preferred channel list by the driver which will have array of nested + * values as per enum qca_wlan_vendor_attr_pcl attribute. + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL = 11, + /* Array of nested attribute for each channel. It takes attr as defined + * in enum qca_wlan_vendor_external_acs_event_chan_info_attr. + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO = 12, + /* External ACS policy such as PCL mandatory, PCL preferred, etc. + * It uses values defined in enum + * qca_wlan_vendor_attr_external_acs_policy. + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_POLICY = 13, + /* Reference RF Operating Parameter (RROP) availability information + * (u16). It uses values defined in enum + * qca_wlan_vendor_attr_rropavail_info. + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_RROPAVAIL_INFO = 14, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_MAX = + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST - 1, +}; + +/** + * qca_wlan_vendor_attr_external_acs_channels: Attributes to vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This carries a list of channels + * in priority order as decided after ACS operation in userspace. + */ +enum qca_wlan_vendor_attr_external_acs_channels { + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_INVALID = 0, + + /* One of reason code (u8) from enum qca_wlan_vendor_acs_select_reason + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON = 1, + + /* Array of nested values for each channel with following attributes: + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_BAND, + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY, + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY, + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0, + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1, + * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH + */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST = 2, + /* This (u8) will hold values of one of enum nl80211_bands */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_BAND = 3, + /* Primary channel (u8) */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY = 4, + /* Secondary channel (u8) used for HT 40 MHz channels */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY = 5, + /* VHT seg0 channel (u8) */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0 = 6, + /* VHT seg1 channel (u8) */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1 = 7, + /* Channel width (u8). Takes one of enum nl80211_chan_width values. */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH = 8, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST, + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_MAX = + QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST - 1 +}; + +enum qca_chip_power_save_failure_reason { + /* Indicates if the reason for the failure is due to a protocol + * layer/module. + */ + QCA_CHIP_POWER_SAVE_FAILURE_REASON_PROTOCOL = 0, + /* Indicates if the reason for the failure is due to a hardware issue. + */ + QCA_CHIP_POWER_SAVE_FAILURE_REASON_HARDWARE = 1, +}; + +/** + * qca_attr_chip_power_save_failure: Attributes to vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE. This carries the requisite + * information leading to the power save failure. + */ +enum qca_attr_chip_power_save_failure { + QCA_ATTR_CHIP_POWER_SAVE_FAILURE_INVALID = 0, + /* Reason to cause the power save failure. + * These reasons are represented by + * enum qca_chip_power_save_failure_reason. + */ + QCA_ATTR_CHIP_POWER_SAVE_FAILURE_REASON = 1, + + /* keep last */ + QCA_ATTR_CHIP_POWER_SAVE_FAILURE_LAST, + QCA_ATTR_CHIP_POWER_SAVE_FAILURE_MAX = + QCA_ATTR_CHIP_POWER_SAVE_FAILURE_LAST - 1, +}; + +/** + * qca_wlan_vendor_nud_stats_data_pkt_flags: Flag representing the various + * data types for which the stats have to get collected. + */ +enum qca_wlan_vendor_nud_stats_data_pkt_flags { + QCA_WLAN_VENDOR_NUD_STATS_DATA_ARP = 1 << 0, + QCA_WLAN_VENDOR_NUD_STATS_DATA_DNS = 1 << 1, + QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_HANDSHAKE = 1 << 2, + QCA_WLAN_VENDOR_NUD_STATS_DATA_ICMPV4 = 1 << 3, + QCA_WLAN_VENDOR_NUD_STATS_DATA_ICMPV6 = 1 << 4, + /* Used by QCA_ATTR_NUD_STATS_PKT_TYPE only in nud stats get + * to represent the stats of respective data type. + */ + QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_SYN = 1 << 5, + QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_SYN_ACK = 1 << 6, + QCA_WLAN_VENDOR_NUD_STATS_DATA_TCP_ACK = 1 << 7, +}; + +enum qca_wlan_vendor_nud_stats_set_data_pkt_info { + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_INVALID = 0, + /* Represents the data packet type to be monitored (u32). + * Host driver tracks the stats corresponding to each data frame + * represented by these flags. + * These data packets are represented by + * enum qca_wlan_vendor_nud_stats_data_pkt_flags + */ + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_TYPE = 1, + /* Name corresponding to the DNS frame for which the respective DNS + * stats have to get monitored (string). Max string length 255. + */ + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DNS_DOMAIN_NAME = 2, + /* source port on which the respective proto stats have to get + * collected (u32). + */ + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_SRC_PORT = 3, + /* destination port on which the respective proto stats have to get + * collected (u32). + */ + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_PORT = 4, + /* IPv4 address for which the destined data packets have to be + * monitored. (in network byte order), u32. + */ + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_IPV4 = 5, + /* IPv6 address for which the destined data packets have to be + * monitored. (in network byte order), 16 bytes array. + */ + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_DEST_IPV6 = 6, + + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_LAST, + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_MAX = + QCA_ATTR_NUD_STATS_DATA_PKT_INFO_LAST - 1, +}; + +/** + * qca_wlan_vendor_attr_nud_stats_set: Attributes to vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET. This carries the requisite + * information to start/stop the NUD statistics collection. + */ +enum qca_attr_nud_stats_set { + QCA_ATTR_NUD_STATS_SET_INVALID = 0, + + /* Flag to start/stop the NUD statistics collection. + * Start - If included, Stop - If not included + */ + QCA_ATTR_NUD_STATS_SET_START = 1, + /* IPv4 address of the default gateway (in network byte order), u32 */ + QCA_ATTR_NUD_STATS_GW_IPV4 = 2, + /* Represents the list of data packet types to be monitored. + * Host driver tracks the stats corresponding to each data frame + * represented by these flags. + * These data packets are represented by + * enum qca_wlan_vendor_nud_stats_set_data_pkt_info + */ + QCA_ATTR_NUD_STATS_SET_DATA_PKT_INFO = 3, + + /* keep last */ + QCA_ATTR_NUD_STATS_SET_LAST, + QCA_ATTR_NUD_STATS_SET_MAX = + QCA_ATTR_NUD_STATS_SET_LAST - 1, +}; + +enum qca_attr_nud_data_stats { + QCA_ATTR_NUD_DATA_STATS_INVALID = 0, + /* Data packet type for which the stats are collected (u32). + * Represented by enum qca_wlan_vendor_nud_stats_data_pkt_flags + */ + QCA_ATTR_NUD_STATS_PKT_TYPE = 1, + /* Name corresponding to the DNS frame for which the respective DNS + * stats are monitored (string). Max string length 255. + */ + QCA_ATTR_NUD_STATS_PKT_DNS_DOMAIN_NAME = 2, + /* source port on which the respective proto stats are collected (u32). + */ + QCA_ATTR_NUD_STATS_PKT_SRC_PORT = 3, + /* destination port on which the respective proto stats are collected + * (u32). + */ + QCA_ATTR_NUD_STATS_PKT_DEST_PORT = 4, + /* IPv4 address for which the destined data packets have to be + * monitored. (in network byte order), u32. + */ + QCA_ATTR_NUD_STATS_PKT_DEST_IPV4 = 5, + /* IPv6 address for which the destined data packets have to be + * monitored. (in network byte order), 16 bytes array. + */ + QCA_ATTR_NUD_STATS_PKT_DEST_IPV6 = 6, + /* Data packet Request count received from netdev (u32). */ + QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_FROM_NETDEV = 7, + /* Data packet Request count sent to lower MAC from upper MAC (u32). */ + QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TO_LOWER_MAC = 8, + /* Data packet Request count received by lower MAC from upper MAC + * (u32) + */ + QCA_ATTR_NUD_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC = 9, + /* Data packet Request count successfully transmitted by the device + * (u32) + */ + QCA_ATTR_NUD_STATS_PKT_REQ_COUNT_TX_SUCCESS = 10, + /* Data packet Response count received by lower MAC (u32) */ + QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC = 11, + /* Data packet Response count received by upper MAC (u32) */ + QCA_ATTR_NUD_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC = 12, + /* Data packet Response count delivered to netdev (u32) */ + QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_TO_NETDEV = 13, + /* Data Packet Response count that are dropped out of order (u32) */ + QCA_ATTR_NUD_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP = 14, + + /* keep last */ + QCA_ATTR_NUD_DATA_STATS_LAST, + QCA_ATTR_NUD_DATA_STATS_MAX = + QCA_ATTR_NUD_DATA_STATS_LAST - 1, +}; + +/** + * qca_attr_nud_stats_get: Attributes to vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET. This carries the requisite + * NUD statistics collected when queried. + */ +enum qca_attr_nud_stats_get { + QCA_ATTR_NUD_STATS_GET_INVALID = 0, + /* ARP Request count from netdev (u32) */ + QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV = 1, + /* ARP Request count sent to lower MAC from upper MAC (u32) */ + QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC = 2, + /* ARP Request count received by lower MAC from upper MAC (u32) */ + QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC = 3, + /* ARP Request count successfully transmitted by the device (u32) */ + QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS = 4, + /* ARP Response count received by lower MAC (u32) */ + QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC = 5, + /* ARP Response count received by upper MAC (u32) */ + QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC = 6, + /* ARP Response count delivered to netdev (u32) */ + QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV = 7, + /* ARP Response count dropped due to out of order reception (u32) */ + QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP = 8, + /* Flag indicating if the station's link to the AP is active. + * Active Link - If included, Inactive link - If not included + */ + QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE = 9, + /* Flag indicating if there is any duplicate address detected (DAD). + * Yes - If detected, No - If not detected. + */ + QCA_ATTR_NUD_STATS_IS_DAD = 10, + /* List of Data packet types for which the stats are requested. + * This list does not carry ARP stats as they are done by the + * above attributes. Represented by enum qca_attr_nud_data_stats. + */ + QCA_ATTR_NUD_STATS_DATA_PKT_STATS = 11, + + /* keep last */ + QCA_ATTR_NUD_STATS_GET_LAST, + QCA_ATTR_NUD_STATS_GET_MAX = + QCA_ATTR_NUD_STATS_GET_LAST - 1, +}; + +enum qca_wlan_btm_candidate_status { + QCA_STATUS_ACCEPT = 0, + QCA_STATUS_REJECT_EXCESSIVE_FRAME_LOSS_EXPECTED = 1, + QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED = 2, + QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY = 3, + QCA_STATUS_REJECT_LOW_RSSI = 4, + QCA_STATUS_REJECT_HIGH_INTERFERENCE = 5, + QCA_STATUS_REJECT_UNKNOWN = 6, +}; + +enum qca_wlan_vendor_attr_btm_candidate_info { + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_INVALID = 0, + + /* 6-byte MAC address representing the BSSID of transition candidate */ + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID = 1, + /* Unsigned 32-bit value from enum qca_wlan_btm_candidate_status + * returned by the driver. It says whether the BSSID provided in + * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID is acceptable by + * the driver, if not it specifies the reason for rejection. + * Note that the user-space can overwrite the transition reject reason + * codes provided by driver based on more information. + */ + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS = 2, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX = + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_AFTER_LAST - 1, +}; + +enum qca_attr_trace_level { + QCA_ATTR_TRACE_LEVEL_INVALID = 0, + /* + * Nested array of the following attributes: + * QCA_ATTR_TRACE_LEVEL_MODULE, + * QCA_ATTR_TRACE_LEVEL_MASK. + */ + QCA_ATTR_TRACE_LEVEL_PARAM = 1, + /* + * Specific QCA host driver module. Please refer to the QCA host + * driver implementation to get the specific module ID. + */ + QCA_ATTR_TRACE_LEVEL_MODULE = 2, + /* Different trace level masks represented in the QCA host driver. */ + QCA_ATTR_TRACE_LEVEL_MASK = 3, + + /* keep last */ + QCA_ATTR_TRACE_LEVEL_AFTER_LAST, + QCA_ATTR_TRACE_LEVEL_MAX = + QCA_ATTR_TRACE_LEVEL_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_get_he_capabilities - IEEE 802.11ax HE capabilities + */ +enum qca_wlan_vendor_attr_get_he_capabilities { + QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_INVALID = 0, + /* Whether HE capabilities is supported + * (u8 attribute: 0 = not supported, 1 = supported) + */ + QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED = 1, + /* HE PHY capabilities, array of 3 u32 values */ + QCA_WLAN_VENDOR_ATTR_PHY_CAPAB = 2, + /* HE MAC capabilities (u32 attribute) */ + QCA_WLAN_VENDOR_ATTR_MAC_CAPAB = 3, + /* HE MCS map (u32 attribute) */ + QCA_WLAN_VENDOR_ATTR_HE_MCS = 4, + /* Number of SS (u32 attribute) */ + QCA_WLAN_VENDOR_ATTR_NUM_SS = 5, + /* RU count (u32 attribute) */ + QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK = 6, + /* PPE threshold data, array of 8 u32 values */ + QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD = 7, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX = + QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_spectral_scan - Spectral scan config parameters + */ +enum qca_wlan_vendor_attr_spectral_scan { + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_INVALID = 0, + /* Number of times the chip enters spectral scan mode before + * deactivating spectral scans. When set to 0, chip will enter spectral + * scan mode continuously. u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SCAN_COUNT = 1, + /* Spectral scan period. Period increment resolution is 256*Tclk, + * where Tclk = 1/44 MHz (Gmode), 1/40 MHz (Amode). u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SCAN_PERIOD = 2, + /* Spectral scan priority. u32 attribute. */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_PRIORITY = 3, + /* Number of FFT data points to compute. u32 attribute. */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FFT_SIZE = 4, + /* Enable targeted gain change before starting the spectral scan FFT. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_GC_ENA = 5, + /* Restart a queued spectral scan. u32 attribute. */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RESTART_ENA = 6, + /* Noise floor reference number for the calculation of bin power. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_NOISE_FLOOR_REF = 7, + /* Disallow spectral scan triggers after TX/RX packets by setting + * this delay value to roughly SIFS time period or greater. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_INIT_DELAY = 8, + /* Number of strong bins (inclusive) per sub-channel, below + * which a signal is declared a narrow band tone. u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_NB_TONE_THR = 9, + /* Specify the threshold over which a bin is declared strong (for + * scan bandwidth analysis). u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_STR_BIN_THR = 10, + /* Spectral scan report mode. u32 attribute. */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_WB_RPT_MODE = 11, + /* RSSI report mode, if the ADC RSSI is below + * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_THR, + * then FFTs will not trigger, but timestamps and summaries get + * reported. u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_RPT_MODE = 12, + /* ADC RSSI must be greater than or equal to this threshold (signed dB) + * to ensure spectral scan reporting with normal error code. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RSSI_THR = 13, + /* Format of frequency bin magnitude for spectral scan triggered FFTs: + * 0: linear magnitude, 1: log magnitude (20*log10(lin_mag)). + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_PWR_FORMAT = 14, + /* Format of FFT report to software for spectral scan triggered FFTs. + * 0: No FFT report (only spectral scan summary report) + * 1: 2-dword summary of metrics for each completed FFT + spectral scan + * report + * 2: 2-dword summary of metrics for each completed FFT + 1x-oversampled + * bins (in-band) per FFT + spectral scan summary report + * 3: 2-dword summary of metrics for each completed FFT + 2x-oversampled + * bins (all) per FFT + spectral scan summary report + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_RPT_MODE = 15, + /* Number of LSBs to shift out in order to scale the FFT bins. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_BIN_SCALE = 16, + /* Set to 1 (with spectral_scan_pwr_format=1), to report bin magnitudes + * in dBm power. u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DBM_ADJ = 17, + /* Per chain enable mask to select input ADC for search FFT. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_CHN_MASK = 18, + /* An unsigned 64-bit integer provided by host driver to identify the + * spectral scan request. This attribute is included in the scan + * response message for @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START + * and used as an attribute in + * @QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_STOP to identify the + * specific scan to be stopped. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_COOKIE = 19, + /* Skip interval for FFT reports. u32 attribute */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_FFT_PERIOD = 20, + /* Set to report only one set of FFT results. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_SHORT_REPORT = 21, + /* Debug level for spectral module in driver. + * 0 : Verbosity level 0 + * 1 : Verbosity level 1 + * 2 : Verbosity level 2 + * 3 : Matched filterID display + * 4 : One time dump of FFT report + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_DEBUG_LEVEL = 22, + /* Type of spectral scan request. u32 attribute. + * It uses values defined in enum + * qca_wlan_vendor_attr_spectral_scan_request_type. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE = 23, + + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_MAX = + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CONFIG_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_spectral_diag_stats - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_DIAG_STATS. + */ +enum qca_wlan_vendor_attr_spectral_diag_stats { + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_INVALID = 0, + /* Number of spectral TLV signature mismatches. + * u64 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_SIG_MISMATCH = 1, + /* Number of spectral phyerror events with insufficient length when + * parsing for secondary 80 search FFT report. u64 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_SEC80_SFFT_INSUFFLEN = 2, + /* Number of spectral phyerror events without secondary 80 + * search FFT report. u64 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_NOSEC80_SFFT = 3, + /* Number of spectral phyerror events with vht operation segment 1 id + * mismatches in search fft report. u64 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_VHTSEG1ID_MISMATCH = 4, + /* Number of spectral phyerror events with vht operation segment 2 id + * mismatches in search fft report. u64 attribute. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_VHTSEG2ID_MISMATCH = 5, + + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_MAX = + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_DIAG_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_spectral_cap - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO. + */ +enum qca_wlan_vendor_attr_spectral_cap { + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_INVALID = 0, + /* Flag attribute to indicate phydiag capability */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_PHYDIAG = 1, + /* Flag attribute to indicate radar detection capability */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_RADAR = 2, + /* Flag attribute to indicate spectral capability */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_SPECTRAL = 3, + /* Flag attribute to indicate advanced spectral capability */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_ADVANCED_SPECTRAL = 4, + /* Spectral hardware generation. u32 attribute. + * It uses values defined in enum + * qca_wlan_vendor_spectral_scan_cap_hw_gen. + */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HW_GEN = 5, + + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_MAX = + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_spectral_scan_status - used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS. + */ +enum qca_wlan_vendor_attr_spectral_scan_status { + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_INVALID = 0, + /* Flag attribute to indicate whether spectral scan is enabled */ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_IS_ENABLED = 1, + /* Flag attribute to indicate whether spectral scan is in progress*/ + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_IS_ACTIVE = 2, + + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_MAX = + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_STATUS_AFTER_LAST - 1, +}; + +/** + * qca_wlan_vendor_attr_spectral_scan_request_type: Attribute values for + * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE to the vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_START. This represents the + * spectral scan request types. + * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN_AND_CONFIG: Request to + * set the spectral parameters and start scan. + * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN: Request to + * only set the spectral parameters. + * @QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_CONFIG: Request to + * only start the spectral scan. + */ +enum qca_wlan_vendor_attr_spectral_scan_request_type { + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN_AND_CONFIG, + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_SCAN, + QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_REQUEST_TYPE_CONFIG, +}; + +/** + * qca_wlan_vendor_spectral_scan_cap_hw_gen: Attribute values for + * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HW_GEN to the vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO. This represents the + * spectral hardware generation. + * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_1: generation 1 + * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_2: generation 2 + * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_3: generation 3 + */ +enum qca_wlan_vendor_spectral_scan_cap_hw_gen { + QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_1 = 0, + QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_2 = 1, + QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_HW_GEN_3 = 2, +}; + +enum qca_wlan_vendor_tos { + QCA_WLAN_VENDOR_TOS_BK = 0, + QCA_WLAN_VENDOR_TOS_BE = 1, + QCA_WLAN_VENDOR_TOS_VI = 2, + QCA_WLAN_VENDOR_TOS_VO = 3, +}; + +/** + * enum qca_wlan_vendor_attr_active_tos - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_ACTIVE_TOS. + */ +enum qca_wlan_vendor_attr_active_tos { + QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_INVALID = 0, + /* Type Of Service - Represented by qca_wlan_vendor_tos */ + QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS = 1, + /* Flag attribute representing the start (attribute included) or stop + * (attribute not included) of the respective TOS. + */ + QCA_WLAN_VENDOR_ATTR_ACTIVE_TOS_START = 2, +}; + +enum qca_wlan_vendor_hang_reason { + /* Unspecified reason */ + QCA_WLAN_HANG_REASON_UNSPECIFIED = 0, + /* No Map for the MAC entry for the received frame */ + QCA_WLAN_HANG_RX_HASH_NO_ENTRY_FOUND = 1, + /* Peer deletion timeout happened */ + QCA_WLAN_HANG_PEER_DELETION_TIMEDOUT = 2, + /* Peer unmap timeout */ + QCA_WLAN_HANG_PEER_UNMAP_TIMEDOUT = 3, + /* Scan request timed out */ + QCA_WLAN_HANG_SCAN_REQ_EXPIRED = 4, + /* Consecutive Scan attempt failures */ + QCA_WLAN_HANG_SCAN_ATTEMPT_FAILURES = 5, + /* Unable to get the message buffer */ + QCA_WLAN_HANG_GET_MSG_BUFF_FAILURE = 6, + /* Current command processing is timedout */ + QCA_WLAN_HANG_ACTIVE_LIST_TIMEOUT = 7, + /* Timeout for an ACK from FW for suspend request */ + QCA_WLAN_HANG_SUSPEND_TIMEOUT = 8, + /* Timeout for an ACK from FW for resume request */ + QCA_WLAN_HANG_RESUME_TIMEOUT = 9, + /* Transmission timeout for consecutive data frames */ + QCA_WLAN_HANG_TRANSMISSIONS_TIMEOUT = 10, + /* Timeout for the TX completion status of data frame */ + QCA_WLAN_HANG_TX_COMPLETE_TIMEOUT = 11, + /* DXE failure for TX/RX, DXE resource unavailability */ + QCA_WLAN_HANG_DXE_FAILURE = 12, + /* WMI pending commands exceed the maximum count */ + QCA_WLAN_HANG_WMI_EXCEED_MAX_PENDING_CMDS = 13, +}; + +/** + * enum qca_wlan_vendor_attr_hang - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_HANG. + */ +enum qca_wlan_vendor_attr_hang { + QCA_WLAN_VENDOR_ATTR_HANG_INVALID = 0, + /* Reason for the hang - u32 attribute with a value from enum + * qca_wlan_vendor_hang_reason. + */ + QCA_WLAN_VENDOR_ATTR_HANG_REASON = 1, + + QCA_WLAN_VENDOR_ATTR_HANG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_HANG_MAX = + QCA_WLAN_VENDOR_ATTR_HANG_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_flush_pending - Attributes for + * flushing pending traffic in firmware. + * + * @QCA_WLAN_VENDOR_ATTR_PEER_ADDR: Configure peer MAC address. + * @QCA_WLAN_VENDOR_ATTR_AC: Configure access category of the pending + * packets. It is u8 value with bit 0~3 represent AC_BE, AC_BK, + * AC_VI, AC_VO respectively. Set the corresponding bit to 1 to + * flush packets with access category. + */ +enum qca_wlan_vendor_attr_flush_pending { + QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_PEER_ADDR = 1, + QCA_WLAN_VENDOR_ATTR_AC = 2, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_MAX = + QCA_WLAN_VENDOR_ATTR_FLUSH_PENDING_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_rropavail_info - Specifies whether Representative + * RF Operating Parameter (RROP) information is available, and if so, at which + * point in the application-driver interaction sequence it can be retrieved by + * the application from the driver. This point may vary by architecture and + * other factors. This is a u16 value. + */ +enum qca_wlan_vendor_attr_rropavail_info { + /* RROP information is unavailable. */ + QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_UNAVAILABLE, + /* RROP information is available and the application can retrieve the + * information after receiving an QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS + * event from the driver. + */ + QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_EXTERNAL_ACS_START, + /* RROP information is available only after a vendor specific scan + * (requested using QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN) has + * successfully completed. The application can retrieve the information + * after receiving the QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE event from + * the driver. + */ + QCA_WLAN_VENDOR_ATTR_RROPAVAIL_INFO_VSCAN_END, +}; + +/** + * enum qca_wlan_vendor_attr_rrop_info - Specifies vendor specific + * Representative RF Operating Parameter (RROP) information. It is sent for the + * vendor command QCA_NL80211_VENDOR_SUBCMD_GET_RROP_INFO. This information is + * intended for use by external Auto Channel Selection applications. It provides + * guidance values for some RF parameters that are used by the system during + * operation. These values could vary by channel, band, radio, and so on. + */ +enum qca_wlan_vendor_attr_rrop_info { + QCA_WLAN_VENDOR_ATTR_RROP_INFO_INVALID = 0, + + /* Representative Tx Power List (RTPL) which has an array of nested + * values as per attributes in enum qca_wlan_vendor_attr_rtplinst. + */ + QCA_WLAN_VENDOR_ATTR_RROP_INFO_RTPL = 1, + + QCA_WLAN_VENDOR_ATTR_RROP_INFO_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_RROP_INFO_MAX = + QCA_WLAN_VENDOR_ATTR_RROP_INFO_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_rtplinst - Specifies attributes for individual list + * entry instances in the Representative Tx Power List (RTPL). It provides + * simplified power values intended for helping external Auto channel Selection + * applications compare potential Tx power performance between channels, other + * operating conditions remaining identical. These values are not necessarily + * the actual Tx power values that will be used by the system. They are also not + * necessarily the max or average values that will be used. Instead, they are + * relative, summarized keys for algorithmic use computed by the driver or + * underlying firmware considering a number of vendor specific factors. + */ +enum qca_wlan_vendor_attr_rtplinst { + QCA_WLAN_VENDOR_ATTR_RTPLINST_INVALID = 0, + + /* Primary channel number (u8) */ + QCA_WLAN_VENDOR_ATTR_RTPLINST_PRIMARY = 1, + /* Representative Tx power in dBm (s32) with emphasis on throughput. */ + QCA_WLAN_VENDOR_ATTR_RTPLINST_TXPOWER_THROUGHPUT = 2, + /* Representative Tx power in dBm (s32) with emphasis on range. */ + QCA_WLAN_VENDOR_ATTR_RTPLINST_TXPOWER_RANGE = 3, + + QCA_WLAN_VENDOR_ATTR_RTPLINST_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_RTPLINST_MAX = + QCA_WLAN_VENDOR_ATTR_RTPLINST_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_config_latency_level - Level for + * wlan latency module. + * + * There will be various of Wi-Fi functionality like scan/roaming/adaptive + * power saving which would causing data exchange out of service, this + * would be a big impact on latency. For latency sensitive applications over + * Wi-Fi are intolerant to such operations and thus would configure them + * to meet their respective needs. It is well understood by such applications + * that altering the default behavior would degrade the Wi-Fi functionality + * w.r.t the above pointed WLAN operations. + * + * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL: + * Default WLAN operation level which throughput orientated. + * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MODERATE: + * Use moderate level to improve latency by limit scan duration. + * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_LOW: + * Use low latency level to benifit application like concurrent + * downloading or video streaming via constraint scan/adaptive PS. + * @QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_ULTRALOW: + * Use ultra low latency level to benefit for gaming/voice + * application via constraint scan/roaming/adaptive PS. + */ +enum qca_wlan_vendor_attr_config_latency_level { + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_NORMAL = 1, + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MODERATE = 2, + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_LOW = 3, + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_ULTRALOW = 4, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_MAX = + QCA_WLAN_VENDOR_ATTR_CONFIG_LATENCY_LEVEL_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_wlan_mac - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO. + */ +enum qca_wlan_vendor_attr_mac { + QCA_WLAN_VENDOR_ATTR_MAC_INVALID = 0, + + /* MAC mode info list which has an array of nested values as + * per attributes in enum qca_wlan_vendor_attr_mac_mode_info. + */ + QCA_WLAN_VENDOR_ATTR_MAC_INFO = 1, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_MAC_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_MAC_MAX = + QCA_WLAN_VENDOR_ATTR_MAC_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_mac_iface_info - Information of the connected + * Wi-Fi netdev interface on a respective MAC. + * Used by the attribute QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO. + */ +enum qca_wlan_vendor_attr_mac_iface_info { + QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_INVALID = 0, + /* Wi-Fi netdev's interface index (u32) */ + QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_IFINDEX = 1, + /* Associated frequency in MHz of the connected Wi-Fi interface (u32) */ + QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_FREQ = 2, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_MAX = + QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_mac_info - Points to MAC the information. + * Used by the attribute QCA_WLAN_VENDOR_ATTR_MAC_INFO of the + * vendor command QCA_NL80211_VENDOR_SUBCMD_WLAN_MAC_INFO. + */ +enum qca_wlan_vendor_attr_mac_info { + QCA_WLAN_VENDOR_ATTR_MAC_INFO_INVALID = 0, + /* Hardware MAC ID associated for the MAC (u32) */ + QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAC_ID = 1, + /* Band supported by the MAC at a given point. + * This is a u32 bitmask of BIT(NL80211_BAND_*) as described in %enum + * nl80211_band. + */ + QCA_WLAN_VENDOR_ATTR_MAC_INFO_BAND = 2, + /* Refers to list of WLAN netdev interfaces associated with this MAC. + * Represented by enum qca_wlan_vendor_attr_mac_iface_info. + */ + QCA_WLAN_VENDOR_ATTR_MAC_IFACE_INFO = 3, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_MAC_INFO_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_MAC_INFO_MAX = + QCA_WLAN_VENDOR_ATTR_MAC_INFO_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_get_logger_features - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_GET_LOGGER_FEATURE_SET. + */ +enum qca_wlan_vendor_attr_get_logger_features { + QCA_WLAN_VENDOR_ATTR_LOGGER_INVALID = 0, + /* Unsigned 32-bit enum value of wifi_logger_supported_features */ + QCA_WLAN_VENDOR_ATTR_LOGGER_SUPPORTED = 1, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_LOGGER_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_LOGGER_MAX = + QCA_WLAN_VENDOR_ATTR_LOGGER_AFTER_LAST - 1, +}; + +/** + * enum wifi_logger_supported_features - Values for supported logger features + */ +enum wifi_logger_supported_features { + WIFI_LOGGER_MEMORY_DUMP_FEATURE = (1 << (0)), + WIFI_LOGGER_PER_PACKET_TX_RX_STATUS_FEATURE = (1 << (1)), + WIFI_LOGGER_CONNECT_EVENT_FEATURE = (1 << (2)), + WIFI_LOGGER_POWER_EVENT_FEATURE = (1 << (3)), + WIFI_LOGGER_WAKE_LOCK_FEATURE = (1 << (4)), + WIFI_LOGGER_VERBOSE_FEATURE = (1 << (5)), + WIFI_LOGGER_WATCHDOG_TIMER_FEATURE = (1 << (6)), + WIFI_LOGGER_DRIVER_DUMP_FEATURE = (1 << (7)), + WIFI_LOGGER_PACKET_FATE_FEATURE = (1 << (8)), +}; + +/** + * enum qca_wlan_tdls_caps_features_supported - Values for TDLS get + * capabilities features + */ +enum qca_wlan_tdls_caps_features_supported { + WIFI_TDLS_SUPPORT = (1 << (0)), + WIFI_TDLS_EXTERNAL_CONTROL_SUPPORT = (1 << (1)), + WIFI_TDLS_OFFCHANNEL_SUPPORT = (1 << (2)) +}; + +/** + * enum qca_wlan_vendor_attr_tdls_get_capabilities - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_CAPABILITIES. + */ +enum qca_wlan_vendor_attr_tdls_get_capabilities { + QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_INVALID = 0, + /* Indicates the max concurrent sessions */ + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX_CONC_SESSIONS, + /* Indicates the support for features */ + /* Unsigned 32-bit bitmap qca_wlan_tdls_caps_features_supported + */ + QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_FEATURES_SUPPORTED, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_MAX = + QCA_WLAN_VENDOR_ATTR_TDLS_GET_CAPS_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_offloaded_packets_sending_control - Offload packets control + * command used as value for the attribute + * QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL. + */ +enum qca_wlan_offloaded_packets_sending_control { + QCA_WLAN_OFFLOADED_PACKETS_SENDING_CONTROL_INVALID = 0, + QCA_WLAN_OFFLOADED_PACKETS_SENDING_START, + QCA_WLAN_OFFLOADED_PACKETS_SENDING_STOP +}; + +/** + * enum qca_wlan_vendor_attr_offloaded_packets - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_OFFLOADED_PACKETS. + */ +enum qca_wlan_vendor_attr_offloaded_packets { + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_INVALID = 0, + /* Takes valid value from the enum + * qca_wlan_offloaded_packets_sending_control + * Unsigned 32-bit value + */ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SENDING_CONTROL, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_REQUEST_ID, + /* array of u8 len: Max packet size */ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_IP_PACKET_DATA, + /* 6-byte MAC address used to represent source MAC address */ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_SRC_MAC_ADDR, + /* 6-byte MAC address used to represent destination MAC address */ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR, + /* Unsigned 32-bit value, in milli seconds */ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_MAX = + QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_rssi_monitoring_control - RSSI control commands used as values + * by the attribute QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL. + */ +enum qca_wlan_rssi_monitoring_control { + QCA_WLAN_RSSI_MONITORING_CONTROL_INVALID = 0, + QCA_WLAN_RSSI_MONITORING_START, + QCA_WLAN_RSSI_MONITORING_STOP, +}; + +/** + * enum qca_wlan_vendor_attr_rssi_monitoring - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_MONITOR_RSSI. + */ +enum qca_wlan_vendor_attr_rssi_monitoring { + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_INVALID = 0, + /* Takes valid value from the enum + * qca_wlan_rssi_monitoring_control + * Unsigned 32-bit value enum qca_wlan_rssi_monitoring_control + */ + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CONTROL, + /* Unsigned 32-bit value */ + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_REQUEST_ID, + /* Signed 8-bit value in dBm */ + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX_RSSI, + /* Signed 8-bit value in dBm */ + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MIN_RSSI, + /* attributes to be used/received in callback */ + /* 6-byte MAC address used to represent current BSSID MAC address */ + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_BSSID, + /* Signed 8-bit value indicating the current RSSI */ + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_CUR_RSSI, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_MAX = + QCA_WLAN_VENDOR_ATTR_RSSI_MONITORING_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_ndp_params - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_NDP. + */ +enum qca_wlan_vendor_attr_ndp_params { + QCA_WLAN_VENDOR_ATTR_NDP_PARAM_INVALID = 0, + /* Unsigned 32-bit value + * enum of sub commands values in qca_wlan_ndp_sub_cmd + */ + QCA_WLAN_VENDOR_ATTR_NDP_SUBCMD, + /* Unsigned 16-bit value */ + QCA_WLAN_VENDOR_ATTR_NDP_TRANSACTION_ID, + /* NL attributes for data used NDP SUB cmds */ + /* Unsigned 32-bit value indicating a service info */ + QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_INSTANCE_ID, + /* Unsigned 32-bit value; channel frequency in MHz */ + QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL, + /* Interface Discovery MAC address. An array of 6 Unsigned int8 */ + QCA_WLAN_VENDOR_ATTR_NDP_PEER_DISCOVERY_MAC_ADDR, + /* Interface name on which NDP is being created */ + QCA_WLAN_VENDOR_ATTR_NDP_IFACE_STR, + /* Unsigned 32-bit value for security */ + /* CONFIG_SECURITY is deprecated, use NCS_SK_TYPE/PMK/SCID instead */ + QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_SECURITY, + /* Unsigned 32-bit value for QoS */ + QCA_WLAN_VENDOR_ATTR_NDP_CONFIG_QOS, + /* Array of u8: len = QCA_WLAN_VENDOR_ATTR_NAN_DP_APP_INFO_LEN */ + QCA_WLAN_VENDOR_ATTR_NDP_APP_INFO, + /* Unsigned 32-bit value for NDP instance Id */ + QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID, + /* Array of instance Ids */ + QCA_WLAN_VENDOR_ATTR_NDP_INSTANCE_ID_ARRAY, + /* Unsigned 32-bit value for initiator/responder NDP response code + * accept/reject + */ + QCA_WLAN_VENDOR_ATTR_NDP_RESPONSE_CODE, + /* NDI MAC address. An array of 6 Unsigned int8 */ + QCA_WLAN_VENDOR_ATTR_NDP_NDI_MAC_ADDR, + /* Unsigned 32-bit value errors types returned by driver + * The wifi_nan.h in AOSP project platform/hardware/libhardware_legacy + * NanStatusType includes these values. + */ + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RESPONSE_STATUS_TYPE, + /* Unsigned 32-bit value error values returned by driver + * The nan_i.h in AOSP project platform/hardware/qcom/wlan + * NanInternalStatusType includes these values. + */ + QCA_WLAN_VENDOR_ATTR_NDP_DRV_RETURN_VALUE, + /* Unsigned 32-bit value for Channel setup configuration + * The wifi_nan.h in AOSP project platform/hardware/libhardware_legacy + * NanDataPathChannelCfg includes these values. + */ + QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_CONFIG, + /* Unsigned 32-bit value for Cipher Suite Shared Key Type */ + QCA_WLAN_VENDOR_ATTR_NDP_CSID, + /* Array of u8: len = NAN_PMK_INFO_LEN 32 bytes */ + QCA_WLAN_VENDOR_ATTR_NDP_PMK, + /* Security Context Identifier that contains the PMKID + * Array of u8: len = NAN_SCID_BUF_LEN 1024 bytes + */ + QCA_WLAN_VENDOR_ATTR_NDP_SCID, + /* Array of u8: len = NAN_SECURITY_MAX_PASSPHRASE_LEN 63 bytes */ + QCA_WLAN_VENDOR_ATTR_NDP_PASSPHRASE, + /* Array of u8: len = NAN_MAX_SERVICE_NAME_LEN 255 bytes */ + QCA_WLAN_VENDOR_ATTR_NDP_SERVICE_NAME, + /* Unsigned 32-bit bitmap indicating schedule update + * BIT_0: NSS Update + * BIT_1: Channel list update + */ + QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_REASON, + /* Unsigned 32-bit value for NSS */ + QCA_WLAN_VENDOR_ATTR_NDP_NSS, + /* Unsigned 32-bit value for NUMBER NDP CHANNEL */ + QCA_WLAN_VENDOR_ATTR_NDP_NUM_CHANNELS, + /* Unsigned 32-bit value for CHANNEL BANDWIDTH + * 0:20 MHz, 1:40 MHz, 2:80 MHz, 3:160 MHz + */ + QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_WIDTH, + /* Array of channel/band width */ + QCA_WLAN_VENDOR_ATTR_NDP_CHANNEL_INFO, + /* IPv6 address used by NDP (in network byte order), 16 bytes array. + * This attribute is used and optional for ndp request, ndp response, + * ndp indication, and ndp confirm. + */ + QCA_WLAN_VENDOR_ATTR_NDP_IPV6_ADDR = 27, + /* Unsigned 16-bit value indicating transport port used by NDP. + * This attribute is used and optional for ndp response, ndp indication, + * and ndp confirm. + */ + QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PORT = 28, + /* Unsigned 8-bit value indicating protocol used by NDP and assigned by + * the Internet Assigned Numbers Authority (IANA) as per: + * https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml + * This attribute is used and optional for ndp response, ndp indication, + * and ndp confirm. + */ + QCA_WLAN_VENDOR_ATTR_NDP_TRANSPORT_PROTOCOL = 29, + /* Unsigned 8-bit value indicating if NDP remote peer supports NAN NDPE. + * 1:support 0:not support + */ + QCA_WLAN_VENDOR_ATTR_PEER_NDPE_SUPPORT = 30, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_MAX = + QCA_WLAN_VENDOR_ATTR_NDP_PARAMS_AFTER_LAST - 1, +}; + +enum qca_wlan_ndp_sub_cmd { + QCA_WLAN_VENDOR_ATTR_NDP_INVALID = 0, + /* Command to create a NAN data path interface */ + QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_CREATE = 1, + /* Command to delete a NAN data path interface */ + QCA_WLAN_VENDOR_ATTR_NDP_INTERFACE_DELETE = 2, + /* Command to initiate a NAN data path session */ + QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_REQUEST = 3, + /* Command to notify if the NAN data path session was sent */ + QCA_WLAN_VENDOR_ATTR_NDP_INITIATOR_RESPONSE = 4, + /* Command to respond to NAN data path session */ + QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_REQUEST = 5, + /* Command to notify on the responder about the response */ + QCA_WLAN_VENDOR_ATTR_NDP_RESPONDER_RESPONSE = 6, + /* Command to initiate a NAN data path end */ + QCA_WLAN_VENDOR_ATTR_NDP_END_REQUEST = 7, + /* Command to notify the if end request was sent */ + QCA_WLAN_VENDOR_ATTR_NDP_END_RESPONSE = 8, + /* Command to notify the peer about the end request */ + QCA_WLAN_VENDOR_ATTR_NDP_REQUEST_IND = 9, + /* Command to confirm the NAN data path session is complete */ + QCA_WLAN_VENDOR_ATTR_NDP_CONFIRM_IND = 10, + /* Command to indicate the peer about the end request being received */ + QCA_WLAN_VENDOR_ATTR_NDP_END_IND = 11, + /* Command to indicate the peer of schedule update */ + QCA_WLAN_VENDOR_ATTR_NDP_SCHEDULE_UPDATE_IND = 12 +}; + +/** + * enum qca_wlan_vendor_attr_nd_offload - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_ND_OFFLOAD. + */ +enum qca_wlan_vendor_attr_nd_offload { + QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_INVALID = 0, + /* Flag to set Neighbour Discovery offload */ + QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_FLAG, + /* Keep last */ + QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_MAX = + QCA_WLAN_VENDOR_ATTR_ND_OFFLOAD_AFTER_LAST - 1, +}; + +/** + * enum packet_filter_sub_cmd - Packet filter sub commands + */ +enum packet_filter_sub_cmd { + /** + * Write packet filter program and/or data. The driver/firmware should + * disable APF before writing into local buffer and re-enable APF after + * writing is done. + */ + QCA_WLAN_SET_PACKET_FILTER = 1, + /* Get packet filter feature capabilities from driver */ + QCA_WLAN_GET_PACKET_FILTER = 2, + /** + * Write packet filter program and/or data. User space will send the + * %QCA_WLAN_DISABLE_PACKET_FILTER command before issuing this command + * and will send the %QCA_WLAN_ENABLE_PACKET_FILTER afterwards. The key + * difference from that %QCA_WLAN_SET_PACKET_FILTER is the control over + * enable/disable is given to user space with this command. Also, + * user space sends the length of program portion in the buffer within + * %QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH. + */ + QCA_WLAN_WRITE_PACKET_FILTER = 3, + /* Read packet filter program and/or data */ + QCA_WLAN_READ_PACKET_FILTER = 4, + /* Enable APF feature */ + QCA_WLAN_ENABLE_PACKET_FILTER = 5, + /* Disable APF feature */ + QCA_WLAN_DISABLE_PACKET_FILTER = 6, +}; + +/** + * enum qca_wlan_vendor_attr_packet_filter - BPF control commands used by + * vendor QCA_NL80211_VENDOR_SUBCMD_PACKET_FILTER. + */ +enum qca_wlan_vendor_attr_packet_filter { + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_INVALID = 0, + /* Unsigned 32-bit enum passed using packet_filter_sub_cmd */ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SUB_CMD, + /* Unsigned 32-bit value indicating the packet filter version */ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_VERSION, + /* Unsigned 32-bit value indicating the packet filter id */ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_ID, + /** + * Unsigned 32-bit value indicating the packet filter size including + * program + data. + */ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_SIZE, + /* Unsigned 32-bit value indicating the packet filter current offset */ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_CURRENT_OFFSET, + /* Program and/or data in bytes */ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROGRAM, + /* Unsigned 32-bit value of the length of the program section in packet + * filter buffer. + */ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_PROG_LENGTH = 7, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_MAX = + QCA_WLAN_VENDOR_ATTR_PACKET_FILTER_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_drv_info - WLAN driver info used by vendor command + * QCA_NL80211_VENDOR_SUBCMD_GET_BUS_SIZE. + */ +enum qca_wlan_vendor_drv_info { + QCA_WLAN_VENDOR_ATTR_DRV_INFO_INVALID = 0, + /* Maximum Message size info between firmware & HOST + * Unsigned 32-bit value + */ + QCA_WLAN_VENDOR_ATTR_DRV_INFO_BUS_SIZE, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_DRV_INFO_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_DRV_INFO_MAX = + QCA_WLAN_VENDOR_ATTR_DRV_INFO_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_wake_stats - Wake lock stats used by vendor + * command QCA_NL80211_VENDOR_SUBCMD_GET_WAKE_REASON_STATS. + */ +enum qca_wlan_vendor_attr_wake_stats { + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_INVALID = 0, + /* Unsigned 32-bit value indicating the total count of wake event */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_CMD_EVENT_WAKE, + /* Array of individual wake count, each index representing wake reason + */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_CMD_EVENT_WAKE_CNT_PTR, + /* Unsigned 32-bit value representing wake count array */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_CMD_EVENT_WAKE_CNT_SZ, + /* Unsigned 32-bit total wake count value of driver/fw */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_DRIVER_FW_LOCAL_WAKE, + /* Array of wake stats of driver/fw */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_PTR, + /* Unsigned 32-bit total wake count value of driver/fw */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_DRIVER_FW_LOCAL_WAKE_CNT_SZ, + /* Unsigned 32-bit total wake count value of packets received */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_TOTAL_RX_DATA_WAKE, + /* Unsigned 32-bit wake count value unicast packets received */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_UNICAST_CNT, + /* Unsigned 32-bit wake count value multicast packets received */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_MULTICAST_CNT, + /* Unsigned 32-bit wake count value broadcast packets received */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RX_BROADCAST_CNT, + /* Unsigned 32-bit wake count value of ICMP packets */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP_PKT, + /* Unsigned 32-bit wake count value of ICMP6 packets */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_PKT, + /* Unsigned 32-bit value ICMP6 router advertisement */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_RA, + /* Unsigned 32-bit value ICMP6 neighbor advertisement */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_NA, + /* Unsigned 32-bit value ICMP6 neighbor solicitation */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_NS, + /* Unsigned 32-bit wake count value of receive side ICMP4 multicast */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP4_RX_MULTICAST_CNT, + /* Unsigned 32-bit wake count value of receive side ICMP6 multicast */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_ICMP6_RX_MULTICAST_CNT, + /* Unsigned 32-bit wake count value of receive side multicast */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_OTHER_RX_MULTICAST_CNT, + /* Unsigned 32-bit wake count value of a given RSSI breach */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_RSSI_BREACH_CNT, + /* Unsigned 32-bit wake count value of low RSSI */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_LOW_RSSI_CNT, + /* Unsigned 32-bit value GSCAN count */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_GSCAN_CNT, + /* Unsigned 32-bit value PNO complete count */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_PNO_COMPLETE_CNT, + /* Unsigned 32-bit value PNO match count */ + QCA_WLAN_VENDOR_ATTR_WAKE_STATS_PNO_MATCH_CNT, + /* keep last */ + QCA_WLAN_VENDOR_GET_WAKE_STATS_AFTER_LAST, + QCA_WLAN_VENDOR_GET_WAKE_STATS_MAX = + QCA_WLAN_VENDOR_GET_WAKE_STATS_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_thermal_cmd - Vendor subcmd attributes to set + * cmd value. Used for NL attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command. + */ +enum qca_wlan_vendor_attr_thermal_cmd { + QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_INVALID = 0, + /* The value of command, driver will implement different operations + * according to this value. It uses values defined in + * enum qca_wlan_vendor_attr_thermal_cmd_type. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE = 1, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_MAX = + QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_AFTER_LAST - 1 +}; + +/** + * qca_wlan_vendor_attr_thermal_cmd_type: Attribute values for + * QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_VALUE to the vendor subcmd + * QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD. This represents the + * thermal command types sent to driver. + * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS: Request to + * get thermal shutdown configuration parameters for display. Parameters + * responded from driver are defined in + * enum qca_wlan_vendor_attr_get_thermal_params_rsp. + * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE: Request to + * get temperature. Host should respond with a temperature data. It is defined + * in enum qca_wlan_vendor_attr_thermal_get_temperature. + * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SUSPEND: Request to execute thermal + * suspend action. + * @QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_RESUME: Request to execute thermal + * resume action. + */ +enum qca_wlan_vendor_attr_thermal_cmd_type { + QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS, + QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE, + QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_SUSPEND, + QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_RESUME, +}; + +/** + * enum qca_wlan_vendor_attr_thermal_get_temperature - vendor subcmd attributes + * to get chip temperature by user. + * enum values are used for NL attributes for data used by + * QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_TEMPERATURE command for data used + * by QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command. + */ +enum qca_wlan_vendor_attr_thermal_get_temperature { + QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_INVALID = 0, + /* Temperature value (degree Celsius) from driver. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_DATA, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_MAX = + QCA_WLAN_VENDOR_ATTR_THERMAL_GET_TEMPERATURE_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_get_thermal_params_rsp - vendor subcmd attributes + * to get configuration parameters of thermal shutdown feature. Enum values are + * used by QCA_WLAN_VENDOR_ATTR_THERMAL_CMD_TYPE_GET_PARAMS command for data + * used by QCA_NL80211_VENDOR_SUBCMD_THERMAL_CMD sub command. + */ +enum qca_wlan_vendor_attr_get_thermal_params_rsp { + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_INVALID = 0, + /* Indicate if the thermal shutdown feature is enabled. + * NLA_FLAG attribute. + */ + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SHUTDOWN_EN, + /* Indicate if the auto mode is enabled. + * Enable: Driver triggers the suspend/resume action. + * Disable: User space triggers the suspend/resume action. + * NLA_FLAG attribute. + */ + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SHUTDOWN_AUTO_EN, + /* Thermal resume threshold (degree Celsius). Issue the resume command + * if the temperature value is lower than this threshold. + * u16 attribute. + */ + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_RESUME_THRESH, + /* Thermal warning threshold (degree Celsius). FW reports temperature + * to driver if it's higher than this threshold. + * u16 attribute. + */ + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_WARNING_THRESH, + /* Thermal suspend threshold (degree Celsius). Issue the suspend command + * if the temperature value is higher than this threshold. + * u16 attribute. + */ + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SUSPEND_THRESH, + /* FW reports temperature data periodically at this interval (ms). + * u16 attribute. + */ + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_SAMPLE_RATE, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_MAX = + QCA_WLAN_VENDOR_ATTR_GET_THERMAL_PARAMS_RSP_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_thermal_event - vendor subcmd attributes to + * report thermal events from driver to user space. + * enum values are used for NL attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_THERMAL_EVENT sub command. + */ +enum qca_wlan_vendor_attr_thermal_event { + QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_INVALID = 0, + /* Temperature value (degree Celsius) from driver. + * u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_TEMPERATURE, + /* Indication of resume completion from power save mode. + * NLA_FLAG attribute. + */ + QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_RESUME_COMPLETE, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_MAX = + QCA_WLAN_VENDOR_ATTR_THERMAL_EVENT_AFTER_LAST - 1, +}; + +/** + * enum he_fragmentation_val - HE fragmentation support values + * Indicates level of dynamic fragmentation that is supported by + * a STA as a recipient. + * HE fragmentation values are defined in IEEE P802.11ax/D2.0, 9.4.2.237.2 + * (HE MAC Capabilities Information field) and are used in HE Capabilities + * element to advertise the support. These values are validated in the driver + * to check the device capability and advertised in the HE Capabilities + * element. These values are used to configure testbed device to allow the + * advertised hardware capabilities to be downgraded for testing purposes. + * + * @HE_FRAG_DISABLE: no support for dynamic fragmentation + * @HE_FRAG_LEVEL1: support for dynamic fragments that are + * contained within an MPDU or S-MPDU, no support for dynamic fragments + * within an A-MPDU that is not an S-MPDU. + * @HE_FRAG_LEVEL2: support for dynamic fragments that are + * contained within an MPDU or S-MPDU and support for up to one dynamic + * fragment for each MSDU, each A-MSDU if supported by the recipient, and + * each MMPDU within an A-MPDU or multi-TID A-MPDU that is not an + * MPDU or S-MPDU. + * @HE_FRAG_LEVEL3: support for dynamic fragments that are + * contained within an MPDU or S-MPDU and support for multiple dynamic + * fragments for each MSDU and for each A-MSDU if supported by the + * recipient within an A-MPDU or multi-TID AMPDU and up to one dynamic + * fragment for each MMPDU in a multi-TID A-MPDU that is not an S-MPDU. + */ +enum he_fragmentation_val { + HE_FRAG_DISABLE, + HE_FRAG_LEVEL1, + HE_FRAG_LEVEL2, + HE_FRAG_LEVEL3, +}; + +/** + * enum he_mcs_config - HE MCS support configuration + * + * Configures the HE Tx/Rx MCS map in HE capability IE for given bandwidth. + * These values are used in driver to configure the HE MCS map to advertise + * Tx/Rx MCS map in HE capability and these values are applied for all the + * streams supported by the device. To configure MCS for different bandwidths, + * vendor command needs to be sent using this attribute with appropriate value. + * For example, to configure HE_80_MCS_0_7, send vendor command using HE MCS + * attribute with HE_80_MCS0_7. And to configure HE MCS for HE_160_MCS0_11 + * send this command using HE MCS config attribute with value HE_160_MCS0_11. + * These values are used to configure testbed device to allow the advertised + * hardware capabilities to be downgraded for testing purposes. The enum values + * are defined such that BIT[1:0] indicates the MCS map value. Values 3,7 and + * 11 are not used as BIT[1:0] value is 3 which is used to disable MCS map. + * These values are validated in the driver before setting the MCS map and + * driver returns error if the input is other than these enum values. + * + * @HE_80_MCS0_7: support for HE 80/40/20 MHz MCS 0 to 7 + * @HE_80_MCS0_9: support for HE 80/40/20 MHz MCS 0 to 9 + * @HE_80_MCS0_11: support for HE 80/40/20 MHz MCS 0 to 11 + * @HE_160_MCS0_7: support for HE 160 MHz MCS 0 to 7 + * @HE_160_MCS0_9: support for HE 160 MHz MCS 0 to 9 + * @HE_160_MCS0_11: support for HE 160 MHz MCS 0 to 11 + * @HE_80P80_MCS0_7: support for HE 80p80 MHz MCS 0 to 7 + * @HE_80P80_MCS0_9: support for HE 80p80 MHz MCS 0 to 9 + * @HE_80P80_MCS0_11: support for HE 80p80 MHz MCS 0 to 11 + */ +enum he_mcs_config { + HE_80_MCS0_7 = 0, + HE_80_MCS0_9 = 1, + HE_80_MCS0_11 = 2, + HE_160_MCS0_7 = 4, + HE_160_MCS0_9 = 5, + HE_160_MCS0_11 = 6, + HE_80P80_MCS0_7 = 8, + HE_80P80_MCS0_9 = 9, + HE_80P80_MCS0_11 = 10, +}; + +/** + * enum qca_wlan_ba_session_config - BA session configuration + * + * Indicates the configuration values for BA session configuration attribute. + * + * @QCA_WLAN_ADD_BA: Establish a new BA session with given configuration. + * @QCA_WLAN_DELETE_BA: Delete the existing BA session for given TID. + */ +enum qca_wlan_ba_session_config { + QCA_WLAN_ADD_BA = 1, + QCA_WLAN_DELETE_BA = 2, +}; + +/** + * enum qca_wlan_ac_type - Access category type + * + * Indicates the access category type value. + * + * @QCA_WLAN_AC_BE: BE access category + * @QCA_WLAN_AC_BK: BK access category + * @QCA_WLAN_AC_VI: VI access category + * @QCA_WLAN_AC_VO: VO access category + * @QCA_WLAN_AC_ALL: All ACs + */ +enum qca_wlan_ac_type { + QCA_WLAN_AC_BE = 0, + QCA_WLAN_AC_BK = 1, + QCA_WLAN_AC_VI = 2, + QCA_WLAN_AC_VO = 3, + QCA_WLAN_AC_ALL = 4, +}; + +/** + * enum qca_wlan_he_ltf_cfg - HE LTF configuration + * + * Indicates the HE LTF configuration value. + * + * @QCA_WLAN_HE_LTF_AUTO: HE-LTF is automatically set to the mandatory HE-LTF, + * based on the GI setting + * @QCA_WLAN_HE_LTF_1X: 1X HE LTF is 3.2us LTF + * @QCA_WLAN_HE_LTF_2X: 2X HE LTF is 6.4us LTF + * @QCA_WLAN_HE_LTF_4X: 4X HE LTF is 12.8us LTF + */ +enum qca_wlan_he_ltf_cfg { + QCA_WLAN_HE_LTF_AUTO = 0, + QCA_WLAN_HE_LTF_1X = 1, + QCA_WLAN_HE_LTF_2X = 2, + QCA_WLAN_HE_LTF_4X = 3, +}; + +/** + * enum qca_wlan_he_mac_padding_dur - HE trigger frame MAC padding duration + * + * Indicates the HE trigger frame MAC padding duration value. + * + * @QCA_WLAN_HE_NO_ADDITIONAL_PROCESS_TIME: no additional time required to + * process the trigger frame. + * @QCA_WLAN_HE_8US_OF_PROCESS_TIME: indicates the 8us of processing time for + * trigger frame. + * @QCA_WLAN_HE_16US_OF_PROCESS_TIME: indicates the 16us of processing time for + * trigger frame. + */ +enum qca_wlan_he_mac_padding_dur { + QCA_WLAN_HE_NO_ADDITIONAL_PROCESS_TIME = 0, + QCA_WLAN_HE_8US_OF_PROCESS_TIME = 1, + QCA_WLAN_HE_16US_OF_PROCESS_TIME = 2, +}; + +/** + * enum qca_wlan_he_om_ctrl_ch_bw - HE OM control field BW configuration + * + * Indicates the HE Operating mode control channel width setting value. + * + * @QCA_WLAN_HE_OM_CTRL_BW_20M: Primary 20 MHz + * @QCA_WLAN_HE_OM_CTRL_BW_40M: Primary 40 MHz + * @QCA_WLAN_HE_OM_CTRL_BW_80M: Primary 80 MHz + * @QCA_WLAN_HE_OM_CTRL_BW_160M: 160 MHz and 80+80 MHz + */ +enum qca_wlan_he_om_ctrl_ch_bw { + QCA_WLAN_HE_OM_CTRL_BW_20M = 0, + QCA_WLAN_HE_OM_CTRL_BW_40M = 1, + QCA_WLAN_HE_OM_CTRL_BW_80M = 2, + QCA_WLAN_HE_OM_CTRL_BW_160M = 3, +}; + +/* Attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION + */ +enum qca_wlan_vendor_attr_wifi_test_config { + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_INVALID = 0, + /* 8-bit unsigned value to configure the driver to enable/disable + * WMM feature. This attribute is used to configure testbed device. + * 1-enable, 0-disable + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WMM_ENABLE = 1, + + /* 8-bit unsigned value to configure the driver to accept/reject + * the addba request from peer. This attribute is used to configure + * the testbed device. + * 1-accept addba, 0-reject addba + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ACCEPT_ADDBA_REQ = 2, + + /* 8-bit unsigned value to configure the driver to send or not to + * send the addba request to peer. + * This attribute is used to configure the testbed device. + * 1-send addba, 0-do not send addba + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SEND_ADDBA_REQ = 3, + + /* 8-bit unsigned value to indicate the HE fragmentation support. + * Uses enum he_fragmentation_val values. + * This attribute is used to configure the testbed device to + * allow the advertised hardware capabilities to be downgraded + * for testing purposes. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_FRAGMENTATION = 4, + + /* 8-bit unsigned value to indicate the HE MCS support. + * Uses enum he_mcs_config values. + * This attribute is used to configure the testbed device to + * allow the advertised hardware capabilities to be downgraded + * for testing purposes. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MCS = 5, + + /* 8-bit unsigned value to configure the driver to allow or not to + * allow the connection with WEP/TKIP in HT/VHT/HE modes. + * This attribute is used to configure the testbed device. + * 1-allow WEP/TKIP in HT/VHT/HE, 0-do not allow WEP/TKIP. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_WEP_TKIP_IN_HE = 6, + + /* 8-bit unsigned value to configure the driver to add a + * new BA session or delete the existing BA session for + * given TID. ADDBA command uses the buffer size and TID + * configuration if user specifies the values else default + * value for buffer size is used for all TIDs if the TID + * also not specified. For DEL_BA command TID value is + * required to process the command. + * Uses enum qca_wlan_ba_session_config values. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADD_DEL_BA_SESSION = 7, + + /* 16-bit unsigned value to configure the buffer size in addba + * request and response frames. + * This attribute is used to configure the testbed device. + * The range of the value is 0 to 256. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ADDBA_BUFF_SIZE = 8, + + /* 8-bit unsigned value to configure the buffer size in addba + * request and response frames. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BA_TID = 9, + + /* 8-bit unsigned value to configure the no ack policy. + * To configure no ack policy, access category value is + * required to process the command. + * This attribute is used to configure the testbed device. + * 1 - enable no ack, 0 - disable no ack. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_NO_ACK = 10, + + /* 8-bit unsigned value to configure the AC for no ack policy + * This attribute is used to configure the testbed device. + * Uses the enum qca_wlan_ac_type values. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_NO_ACK_AC = 11, + + /* 8-bit unsigned value to configure the HE LTF + * This attribute is used to configure the testbed device. + * Uses the enum qca_wlan_he_ltf_cfg values. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_LTF = 12, + + /* 8-bit unsigned value to configure the tx beamformee. + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_TX_BEAMFORMEE = 13, + + /* 8-bit unsigned value to configure the tx beamformee number + * of space-time streams. + * This attribute is used to configure the testbed device. + * The range of the value is 0 to 8. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_BEAMFORMEE_NSTS = 14, + + /* 8-bit unsigned value to configure the MU EDCA params for given AC + * This attribute is used to configure the testbed device. + * Uses the enum qca_wlan_ac_type values. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_AC = 15, + + /* 8-bit unsigned value to configure the MU EDCA AIFSN for given AC + * To configure MU EDCA AIFSN value, MU EDCA access category value + * is required to process the command. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_AIFSN = 16, + + /* 8-bit unsigned value to configure the MU EDCA ECW min value for + * given AC. + * To configure MU EDCA ECW min value, MU EDCA access category value + * is required to process the command. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_ECWMIN = 17, + + /* 8-bit unsigned value to configure the MU EDCA ECW max value for + * given AC. + * To configure MU EDCA ECW max value, MU EDCA access category value + * is required to process the command. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_ECWMAX = 18, + + /* 8-bit unsigned value to configure the MU EDCA timer for given AC + * To configure MU EDCA timer value, MU EDCA access category value + * is required to process the command. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MU_EDCA_TIMER = 19, + + /* 8-bit unsigned value to configure the HE trigger frame MAC padding + * duration. + * This attribute is used to configure the testbed device. + * Uses the enum qca_wlan_he_mac_padding_dur values. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_MAC_PADDING_DUR = 20, + + /* 8-bit unsigned value to override the MU EDCA params to defaults + * regardless of the AP beacon MU EDCA params. If it is enabled use + * the default values else use the MU EDCA params from AP beacon. + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_OVERRIDE_MU_EDCA = 21, + + /* 8-bit unsigned value to configure the support for receiving + * an MPDU that contains an operating mode control subfield. + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_SUPP = 22, + + /* Nested attribute values required to setup the TWT session. + * enum qca_wlan_vendor_attr_twt_setup provides the necessary + * information to set up the session. It contains broadcast flags, + * set_up flags, trigger value, flow type, flow ID, wake interval + * exponent, protection, target wake time, wake duration, wake interval + * mantissa. These nested attributes are used to setup a host triggered + * TWT session. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP = 23, + + /* This nested attribute is used to terminate the current TWT session. + * It does not currently carry any attributes. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_TERMINATE = 24, + + /* This nested attribute is used to suspend the current TWT session. + * It does not currently carry any attributes. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SUSPEND = 25, + + /* Nested attribute values to indicate the request for resume. + * This attribute is used to resume the TWT session. + * enum qca_wlan_vendor_attr_twt_resume provides the necessary + * parameters required to resume the TWT session. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_RESUME = 26, + + /* 8-bit unsigned value to set the HE operating mode control + * (OM CTRL) Channel Width subfield. + * The Channel Width subfield indicates the operating channel width + * supported by the STA for both reception and transmission. + * Uses the enum qca_wlan_he_om_ctrl_ch_bw values. + * This setting is cleared with the + * QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG + * flag attribute to reset defaults. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_BW = 27, + + /* 8-bit unsigned value to configure the number of spatial + * streams in HE operating mode control field. + * This setting is cleared with the + * QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG + * flag attribute to reset defaults. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_NSS = 28, + + /* Flag attribute to configure the UL MU disable bit in + * HE operating mode control field. + * This setting is cleared with the + * QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG + * flag attribute to reset defaults. + * This attribute is used to configure the testbed device. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OM_CTRL_UL_MU_DISABLE = 29, + + /* Flag attribute to clear the previously set HE operating mode + * control field configuration. + * This attribute is used to configure the testbed device to reset + * defaults to clear any previously set HE operating mode control + * field configuration. + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_CLEAR_HE_OM_CTRL_CONFIG = 30, + + /* 8-bit unsigned value to configure HE single user PPDU + * transmission. By default this setting is disabled and it + * is disabled in the reset defaults of the device configuration. + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TX_SUPPDU = 31, + + /* 8-bit unsigned value to configure action frame transmission + * in HE trigger based PPDU transmission. + * By default this setting is disabled and it is disabled in + * the reset defaults of the device configuration. + * This attribute is used to configure the testbed device. + * 1-enable, 0-disable + */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_ACTION_TX_TB_PPDU = 32, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX = + QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_bss_filter - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER. + * The user can add/delete the filter by specifying the BSSID/STA MAC address in + * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR, filter type in + * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE, add/delete action in + * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION in the request. The user can get the + * statistics of an unassociated station by specifying the MAC address in + * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR, station type in + * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE, GET action in + * QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION in the request. The user also can get + * the statistics of all unassociated stations by specifying the Broadcast MAC + * address (ff:ff:ff:ff:ff:ff) in QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR with + * above procedure. In the response, driver shall specify statistics + * information nested in QCA_WLAN_VENDOR_ATTR_BSS_FILTER_STA_STATS. + */ +enum qca_wlan_vendor_attr_bss_filter { + QCA_WLAN_VENDOR_ATTR_BSS_FILTER_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAC_ADDR = 1, + /* Other BSS filter type, unsigned 8 bit value. One of the values + * in enum qca_wlan_vendor_bss_filter_type. + */ + QCA_WLAN_VENDOR_ATTR_BSS_FILTER_TYPE = 2, + /* Other BSS filter action, unsigned 8 bit value. One of the values + * in enum qca_wlan_vendor_bss_filter_action. + */ + QCA_WLAN_VENDOR_ATTR_BSS_FILTER_ACTION = 3, + /* Array of nested attributes where each entry is the statistics + * information of the specified station that belong to another BSS. + * Attributes for each entry are taken from enum + * qca_wlan_vendor_bss_filter_sta_stats. + * Other BSS station configured in + * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER with filter type + * QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA. + * Statistics returned by QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER + * with filter action QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET. + */ + QCA_WLAN_VENDOR_ATTR_BSS_FILTER_STA_STATS = 4, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_BSS_FILTER_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_BSS_FILTER_MAX = + QCA_WLAN_VENDOR_ATTR_BSS_FILTER_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_bss_filter_type - Type of + * filter used in other BSS filter operations. Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER. + * + * @QCA_WLAN_VENDOR_BSS_FILTER_TYPE_BSSID: BSSID filter + * @QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA: Station MAC address filter + */ +enum qca_wlan_vendor_bss_filter_type { + QCA_WLAN_VENDOR_BSS_FILTER_TYPE_BSSID, + QCA_WLAN_VENDOR_BSS_FILTER_TYPE_STA, +}; + +/** + * enum qca_wlan_vendor_bss_filter_action - Type of + * action in other BSS filter operations. Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER. + * + * @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_ADD: Add filter + * @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_DEL: Delete filter + * @QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET: Get the statistics + */ +enum qca_wlan_vendor_bss_filter_action { + QCA_WLAN_VENDOR_BSS_FILTER_ACTION_ADD, + QCA_WLAN_VENDOR_BSS_FILTER_ACTION_DEL, + QCA_WLAN_VENDOR_BSS_FILTER_ACTION_GET, +}; + +/** + * enum qca_wlan_vendor_bss_filter_sta_stats - Attributes for + * the statistics of a specific unassociated station belonging to another BSS. + * The statistics provides information of the unassociated station + * filtered by other BSS operation - such as MAC, signal value. + * Used by the vendor command QCA_NL80211_VENDOR_SUBCMD_BSS_FILTER. + * + * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAC: MAC address of the station. + * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI: Last received signal strength + * of the station. Unsigned 8 bit number containing RSSI. + * @QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI_TS: Time stamp of the host + * driver for the last received RSSI. Unsigned 64 bit number containing + * nanoseconds from the boottime. + */ +enum qca_wlan_vendor_bss_filter_sta_stats { + QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_INVALID = 0, + QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAC = 1, + QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI = 2, + QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_RSSI_TS = 3, + + /* keep last */ + QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_AFTER_LAST, + QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_MAX = + QCA_WLAN_VENDOR_BSS_FILTER_STA_STATS_AFTER_LAST - 1 +}; + +/* enum qca_wlan_nan_subcmd_type - Type of NAN command used by attribute + * QCA_WLAN_VENDOR_ATTR_NAN_SUBCMD_TYPE as a part of vendor command + * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT. + */ +enum qca_wlan_nan_ext_subcmd_type { + /* Subcmd of type NAN Enable Request */ + QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ = 1, + /* Subcmd of type NAN Disable Request */ + QCA_WLAN_NAN_EXT_SUBCMD_TYPE_DISABLE_REQ = 2, +}; + +/** + * enum qca_wlan_vendor_attr_nan_params - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT. + */ +enum qca_wlan_vendor_attr_nan_params { + QCA_WLAN_VENDOR_ATTR_NAN_INVALID = 0, + /* Carries NAN command for firmware component. Every vendor command + * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT must contain this attribute with a + * payload containing the NAN command. NLA_BINARY attribute. + */ + QCA_WLAN_VENDOR_ATTR_NAN_CMD_DATA = 1, + /* Indicates the type of NAN command sent with + * QCA_NL80211_VENDOR_SUBCMD_NAN_EXT. enum qca_wlan_nan_ext_subcmd_type + * describes the possible range of values. This attribute is mandatory + * if the command being issued is either + * QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ or + * QCA_WLAN_NAN_EXT_SUBCMD_TYPE_DISABLE_REQ. NLA_U32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_NAN_SUBCMD_TYPE = 2, + /* Frequency (in MHz) of primary NAN discovery social channel in 2.4 GHz + * band. This attribute is mandatory when command type is + * QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ. NLA_U32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_NAN_DISC_24GHZ_BAND_FREQ = 3, + /* Frequency (in MHz) of secondary NAN discovery social channel in 5 GHz + * band. This attribute is optional and should be included when command + * type is QCA_WLAN_NAN_EXT_SUBCMD_TYPE_ENABLE_REQ and NAN discovery + * has to be started on 5GHz along with 2.4GHz. NLA_U32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_NAN_DISC_5GHZ_BAND_FREQ = 4, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_MAX = + QCA_WLAN_VENDOR_ATTR_NAN_PARAMS_AFTER_LAST - 1 +}; + +/** + * enum qca_wlan_vendor_attr_twt_setup: Represents attributes for + * TWT (Target Wake Time) setup request. These attributes are sent as part of + * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_SETUP and + * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST: Flag attribute. + * Disable (flag attribute not present) - Individual TWT + * Enable (flag attribute present) - Broadcast TWT. + * Individual means the session is between the STA and the AP. + * This session is established using a separate negotiation between + * STA and AP. + * Broadcast means the session is across multiple STAs and an AP. The + * configuration parameters are announced in Beacon frames by the AP. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE: Required (u8). + * Unsigned 8-bit qca_wlan_vendor_twt_setup_req_type to + * specify the TWT request type + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER: Flag attribute + * Enable (flag attribute present) - TWT with trigger support. + * Disable (flag attribute not present) - TWT without trigger support. + * Trigger means the AP will send the trigger frame to allow STA to send data. + * Without trigger, the STA will wait for the MU EDCA timer before + * transmitting the data. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE: Required (u8) + * 0 - Announced TWT - In this mode, STA may skip few service periods to + * save more power. If STA wants to wake up, it will send a PS-POLL/QoS + * NULL frame to AP. + * 1 - Unannounced TWT - The STA will wakeup during every SP. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID: Optional (u8) + * Flow ID is the unique identifier for each TWT session. + * Currently this is not required and dialog ID will be set to zero. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP: Required (u8) + * This attribute (exp) is used along with the mantissa to derive the + * wake interval using the following formula: + * pow(2,exp) = wake_intvl_us/wake_intvl_mantis + * Wake interval is the interval between 2 successive SP. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION: Flag attribute + * Enable (flag attribute present) - Protection required. + * Disable (flag attribute not present) - Protection not required. + * If protection is enabled, then the AP will use protection + * mechanism using RTS/CTS to self to reserve the airtime. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME: Optional (u32) + * This attribute is used as the SP offset which is the offset from + * TSF after which the wake happens. The units are in microseconds. If + * this attribute is not provided, then the value will be set to zero. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION: Required (u32) + * This is the duration of the service period. The units are in TU. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA: Required (u32) + * This attribute is used to configure wake interval mantissa. + * The units are in TU. + */ +enum qca_wlan_vendor_attr_twt_setup { + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_BCAST = 1, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_REQ_TYPE = 2, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_TRIGGER = 3, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_TYPE = 4, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_FLOW_ID = 5, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_EXP = 6, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_PROTECTION = 7, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_TIME = 8, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_DURATION = 9, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_WAKE_INTVL_MANTISSA = 10, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_MAX = + QCA_WLAN_VENDOR_ATTR_TWT_SETUP_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_twt_resume: Represents attributes for + * TWT (Target Wake Time) resume request. These attributes are sent as part of + * %QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_TWT_RESUME and + * %QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT: Optional (u8) + * This attribute is used as the SP offset which is the offset from + * TSF after which the wake happens. The units are in microseconds. + * If this attribute is not provided, then the value will be set to + * zero. + * + * @QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE: Required (u32) + * This attribute represents the next TWT subfield size. + */ +enum qca_wlan_vendor_attr_twt_resume { + QCA_WLAN_VENDOR_ATTR_TWT_RESUME_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT = 1, + QCA_WLAN_VENDOR_ATTR_TWT_RESUME_NEXT_TWT_SIZE = 2, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_TWT_RESUME_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_TWT_RESUME_MAX = + QCA_WLAN_VENDOR_ATTR_TWT_RESUME_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_twt_setup_req_type - Required (u8) + * Represents the setup type being requested for TWT. + * @QCA_WLAN_VENDOR_TWT_SETUP_REQUEST: STA is not specifying all the TWT + * parameters but relying on AP to fill the parameters during the negotiation. + * @QCA_WLAN_VENDOR_TWT_SETUP_SUGGEST: STA will provide all the suggested + * values which the AP may accept or AP may provide alternative parameters + * which the STA may accept. + * @QCA_WLAN_VENDOR_TWT_SETUP_DEMAND: STA is not willing to accept any + * alternate parameters than the requested ones. + */ +enum qca_wlan_vendor_twt_setup_req_type { + QCA_WLAN_VENDOR_TWT_SETUP_REQUEST = 1, + QCA_WLAN_VENDOR_TWT_SETUP_SUGGEST = 2, + QCA_WLAN_VENDOR_TWT_SETUP_DEMAND = 3, +}; + +/** + * enum qca_wlan_roam_scan_event_type - Type of roam scan event + * + * Indicates the type of roam scan event sent by firmware/driver. + * + * @QCA_WLAN_ROAM_SCAN_TRIGGER_EVENT: Roam scan trigger event type. + * @QCA_WLAN_ROAM_SCAN_STOP_EVENT: Roam scan stopped event type. + */ +enum qca_wlan_roam_scan_event_type { + QCA_WLAN_ROAM_SCAN_TRIGGER_EVENT = 0, + QCA_WLAN_ROAM_SCAN_STOP_EVENT = 1, +}; + +/** + * enum qca_wlan_roam_scan_trigger_reason - Roam scan trigger reason + * + * Indicates the reason for triggering roam scan by firmware/driver. + * + * @QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_LOW_RSSI: Due to low RSSI of current AP. + * @QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_HIGH_PER: Due to high packet error rate. + */ +enum qca_wlan_roam_scan_trigger_reason { + QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_LOW_RSSI = 0, + QCA_WLAN_ROAM_SCAN_TRIGGER_REASON_HIGH_PER = 1, +}; + +/** + * enum qca_wlan_vendor_attr_roam_scan - Vendor subcmd attributes to report + * roam scan related details from driver/firmware to user space. enum values + * are used for NL attributes sent with + * %QCA_NL80211_VENDOR_SUBCMD_ROAM_SCAN_EVENT sub command. + */ +enum qca_wlan_vendor_attr_roam_scan { + QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_INVALID = 0, + /* Encapsulates type of roam scan event being reported. enum + * qca_wlan_roam_scan_event_type describes the possible range of + * values. u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_EVENT_TYPE = 1, + /* Encapsulates reason for triggering roam scan. enum + * qca_wlan_roam_scan_trigger_reason describes the possible range of + * values. u32 attribute. + */ + QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_TRIGGER_REASON = 2, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_MAX = + QCA_WLAN_VENDOR_ATTR_ROAM_SCAN_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_cfr_method - QCA vendor CFR methods used by + * attribute QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD as part of vendor + * command QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG. + */ +enum qca_wlan_vendor_cfr_method { + /* CFR method using QOS Null frame */ + QCA_WLAN_VENDOR_CFR_METHOD_QOS_NULL = 0, +}; + +/** + * enum qca_wlan_vendor_peer_cfr_capture_attr - Used by the vendor command + * QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG to configure peer + * Channel Frequency Response capture parameters and enable periodic CFR + * capture. + */ +enum qca_wlan_vendor_peer_cfr_capture_attr { + QCA_WLAN_VENDOR_ATTR_PEER_CFR_CAPTURE_INVALID = 0, + /* 6-byte MAC address of the peer. + * This attribute is mandatory. + */ + QCA_WLAN_VENDOR_ATTR_CFR_PEER_MAC_ADDR = 1, + /* Enable peer CFR Capture, flag attribute. + * This attribute is mandatory to enable peer CFR capture. + * If this attribute is not present, peer CFR capture is disabled. + */ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE = 2, + /* BW of measurement, attribute uses the values in enum nl80211_chan_width + * Supported values: 20, 40, 80, 80+80, 160. + * Note that all targets may not support all bandwidths. + * u8 attribute. This attribute is mandatory if attribute + * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used. + */ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_BANDWIDTH = 3, + /* Periodicity of CFR measurement in msec. + * Periodicity should be a multiple of Base timer. + * Current Base timer value supported is 10 msecs (default). + * 0 for one shot capture. u32 attribute. + * This attribute is mandatory if attribute + * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used. + */ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_PERIODICITY = 4, + /* Method used to capture Channel Frequency Response. + * Attribute uses the values defined in enum qca_wlan_vendor_cfr_method. + * u8 attribute. This attribute is mandatory if attribute + * QCA_WLAN_VENDOR_ATTR_PEER_CFR_ENABLE is used. + */ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_METHOD = 5, + /* Enable periodic CFR capture, flag attribute. + * This attribute is mandatory to enable Periodic CFR capture. + * If this attribute is not present, periodic CFR capture is disabled. + */ + QCA_WLAN_VENDOR_ATTR_PERIODIC_CFR_CAPTURE_ENABLE = 6, + + /* Keep last */ + QCA_WLAN_VENDOR_ATTR_PEER_CFR_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_PEER_CFR_MAX = + QCA_WLAN_VENDOR_ATTR_PEER_CFR_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_throughput_level - Current throughput level + * + * Indicates the current level of throughput calculated by the driver. The + * driver may choose different thresholds to decide whether the throughput level + * is low or medium or high based on variety of parameters like physical link + * capacity of the current connection, the number of packets being dispatched + * per second, etc. The throughput level events might not be consistent with the + * actual current throughput value being observed. + * + * @QCA_WLAN_THROUGHPUT_LEVEL_LOW: Low level of throughput + * @QCA_WLAN_THROUGHPUT_LEVEL_MEDIUM: Medium level of throughput + * @QCA_WLAN_THROUGHPUT_LEVEL_HIGH: High level of throughput + */ +enum qca_wlan_throughput_level { + QCA_WLAN_THROUGHPUT_LEVEL_LOW = 0, + QCA_WLAN_THROUGHPUT_LEVEL_MEDIUM = 1, + QCA_WLAN_THROUGHPUT_LEVEL_HIGH = 2, +}; + +/** + * enum qca_wlan_vendor_attr_throughput_change - Vendor subcmd attributes to + * report throughput changes from the driver to user space. enum values are used + * for netlink attributes sent with + * %QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT sub command. + */ +enum qca_wlan_vendor_attr_throughput_change { + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_INVALID = 0, + /* Indicates the direction of throughput in which the change is being + * reported. u8 attribute. Value is 0 for TX and 1 for RX. + */ + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_DIRECTION = 1, + /* Indicates the newly observed throughput level. enum + * qca_wlan_throughput_level describes the possible range of values. + * u8 attribute. + */ + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_THROUGHPUT_LEVEL = 2, + /* Indicates the driver's guidance on the new value to be set to + * kernel's TCP parameter tcp_limit_output_bytes. u32 attribute. The + * driver may optionally include this attribute. + */ + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_LIMIT_OUTPUT_BYTES = 3, + /* Indicates the driver's guidance on the new value to be set to + * kernel's TCP parameter tcp_adv_win_scale. s8 attribute. Possible + * values are from -31 to 31. The driver may optionally include this + * attribute. + */ + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_ADV_WIN_SCALE = 4, + /* Indicates the driver's guidance on the new value to be set to + * kernel's TCP parameter tcp_delack_seg. u32 attribute. The driver may + * optionally include this attribute. + */ + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_TCP_DELACK_SEG = 5, + + /* keep last */ + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_MAX = + QCA_WLAN_VENDOR_ATTR_THROUGHPUT_CHANGE_AFTER_LAST - 1, +}; + +/** + * enum qca_coex_config_profiles - This enum defines different types of + * traffic streams that can be prioritized one over the other during coex + * scenarios. + * The types defined in this enum are categorized in the below manner. + * 0 - 31 values corresponds to WLAN + * 32 - 63 values corresponds to BT + * 64 - 95 values corresponds to Zigbee + * @QCA_WIFI_STA_DISCOVERY: Prioritize discovery frames for WLAN STA + * @QCA_WIFI_STA_CONNECTION: Prioritize connection frames for WLAN STA + * @QCA_WIFI_STA_CLASS_3_MGMT: Prioritize class 3 mgmt frames for WLAN STA + * @QCA_WIFI_STA_DATA : Prioritize data frames for WLAN STA + * @QCA_WIFI_STA_ALL: Priritize all frames for WLAN STA + * @QCA_WIFI_SAP_DISCOVERY: Prioritize discovery frames for WLAN SAP + * @QCA_WIFI_SAP_CONNECTION: Prioritize connection frames for WLAN SAP + * @QCA_WIFI_SAP_CLASS_3_MGMT: Prioritize class 3 mgmt frames for WLAN SAP + * @QCA_WIFI_SAP_DATA: Prioritize data frames for WLAN SAP + * @QCA_WIFI_SAP_ALL: Prioritize all frames for WLAN SAP + * @QCA_BT_A2DP: Prioritize BT A2DP + * @QCA_BT_BLE: Prioritize BT BLE + * @QCA_BT_SCO: Prioritize BT SCO + * @QCA_ZB_LOW: Prioritize Zigbee Low + * @QCA_ZB_HIGH: Prioritize Zigbee High + */ +enum qca_coex_config_profiles { + /* 0 - 31 corresponds to WLAN */ + QCA_WIFI_STA_DISCOVERY = 0, + QCA_WIFI_STA_CONNECTION = 1, + QCA_WIFI_STA_CLASS_3_MGMT = 2, + QCA_WIFI_STA_DATA = 3, + QCA_WIFI_STA_ALL = 4, + QCA_WIFI_SAP_DISCOVERY = 5, + QCA_WIFI_SAP_CONNECTION = 6, + QCA_WIFI_SAP_CLASS_3_MGMT = 7, + QCA_WIFI_SAP_DATA = 8, + QCA_WIFI_SAP_ALL = 9, + /* 32 - 63 corresponds to BT */ + QCA_BT_A2DP = 32, + QCA_BT_BLE = 33, + QCA_BT_SCO = 34, + /* 64 - 95 corresponds to Zigbee */ + QCA_ZB_LOW = 64, + QCA_ZB_HIGH = 65 +}; + +/** + * enum qca_vendor_attr_coex_config - Specifies vendor coex config attributes + * + * @QCA_VENDOR_ATTR_COEX_CONFIG_PROFILES: This attribute contains variable + * length array of 8-bit values from enum qca_coex_config_profiles. + * FW will prioritize the profiles in the order given in the array encapsulated + * in this attribute. + * For example: + * ----------------------------------------------------------------------- + * | 1 | 34 | 32 | 65 | + * ----------------------------------------------------------------------- + * If the attribute contains the values defined in above array then it means + * 1) Wifi STA connection has priority over BT_SCO, BT_A2DP and ZIGBEE HIGH. + * 2) BT_SCO has priority over BT_A2DP. + * 3) BT_A2DP has priority over ZIGBEE HIGH. + * Profiles which are not listed in this array shall not be preferred over the + * profiles which are listed in the array as a part of this attribute. + */ +enum qca_vendor_attr_coex_config { + QCA_VENDOR_ATTR_COEX_CONFIG_INVALID = 0, + QCA_VENDOR_ATTR_COEX_CONFIG_PROFILES = 1, + + /* Keep last */ + QCA_VENDOR_ATTR_COEX_CONFIG_AFTER_LAST, + QCA_VENDOR_ATTR_COEX_CONFIG_MAX = + QCA_VENDOR_ATTR_COEX_CONFIG_AFTER_LAST - 1, +}; + +/** + * enum qca_wlan_vendor_attr_link_properties - Represent the link properties. + * + * @QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR: MAC address of the peer + * (STA/AP) for the connected link. + * @QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_STA_FLAGS: Attribute containing a + * &struct nl80211_sta_flag_update for the respective connected link. MAC + * address of the peer represented by + * QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR. + */ +enum qca_wlan_vendor_attr_link_properties { + QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_INVALID = 0, + /* 1 - 3 are reserved */ + QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR = 4, + QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_STA_FLAGS = 5, + + /* Keep last */ + QCA_VENDOR_ATTR_LINK_PROPERTIES_AFTER_LAST, + QCA_VENDOR_ATTR_LINK_PROPERTIES_MAX = + QCA_VENDOR_ATTR_LINK_PROPERTIES_AFTER_LAST - 1, +}; + #endif /* QCA_VENDOR_H */ diff --git a/src/common/sae.c b/src/common/sae.c index 9f70f036ba763..981e788dc7518 100644 --- a/src/common/sae.c +++ b/src/common/sae.c @@ -29,6 +29,8 @@ int sae_set_group(struct sae_data *sae, int group) /* First, check if this is an ECC group */ tmp->ec = crypto_ec_init(group); if (tmp->ec) { + wpa_printf(MSG_DEBUG, "SAE: Selecting supported ECC group %d", + group); sae->group = group; tmp->prime_len = crypto_ec_prime_len(tmp->ec); tmp->prime = crypto_ec_get_prime(tmp->ec); @@ -39,6 +41,8 @@ int sae_set_group(struct sae_data *sae, int group) /* Not an ECC group, check FFC */ tmp->dh = dh_groups_get(group); if (tmp->dh) { + wpa_printf(MSG_DEBUG, "SAE: Selecting supported FFC group %d", + group); sae->group = group; tmp->prime_len = tmp->dh->prime_len; if (tmp->prime_len > SAE_MAX_PRIME_LEN) { @@ -66,6 +70,8 @@ int sae_set_group(struct sae_data *sae, int group) } /* Unsupported group */ + wpa_printf(MSG_DEBUG, + "SAE: Group %d not supported by the crypto library", group); return -1; } @@ -88,6 +94,7 @@ void sae_clear_temp_data(struct sae_data *sae) crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0); crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0); wpabuf_free(tmp->anti_clogging_token); + os_free(tmp->pw_id); bin_clear_free(tmp, sizeof(*tmp)); sae->tmp = NULL; } @@ -417,12 +424,13 @@ static int get_random_qr_qnr(const u8 *prime, size_t prime_len, static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, const u8 *addr2, const u8 *password, - size_t password_len) + size_t password_len, const char *identifier) { u8 counter, k = 40; u8 addrs[2 * ETH_ALEN]; - const u8 *addr[2]; - size_t len[2]; + const u8 *addr[3]; + size_t len[3]; + size_t num_elem; u8 dummy_password[32]; size_t dummy_password_len; int pwd_seed_odd = 0; @@ -454,10 +462,13 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password", password, password_len); + if (identifier) + wpa_printf(MSG_DEBUG, "SAE: password identifier: %s", + identifier); /* * H(salt, ikm) = HMAC-SHA256(salt, ikm) - * base = password + * base = password [|| identifier] * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), * base || counter) */ @@ -465,8 +476,15 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, addr[0] = password; len[0] = password_len; - addr[1] = &counter; - len[1] = sizeof(counter); + num_elem = 1; + if (identifier) { + addr[num_elem] = (const u8 *) identifier; + len[num_elem] = os_strlen(identifier); + num_elem++; + } + addr[num_elem] = &counter; + len[num_elem] = sizeof(counter); + num_elem++; /* * Continue for at least k iterations to protect against side-channel @@ -484,8 +502,8 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1, } wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); - if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len, - pwd_seed) < 0) + if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, + addr, len, pwd_seed) < 0) break; res = sae_test_pwd_seed_ecc(sae, pwd_seed, @@ -544,12 +562,13 @@ fail: static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, const u8 *addr2, const u8 *password, - size_t password_len) + size_t password_len, const char *identifier) { u8 counter; u8 addrs[2 * ETH_ALEN]; - const u8 *addr[2]; - size_t len[2]; + const u8 *addr[3]; + size_t len[3]; + size_t num_elem; int found = 0; if (sae->tmp->pwe_ffc == NULL) { @@ -564,14 +583,21 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, /* * H(salt, ikm) = HMAC-SHA256(salt, ikm) * pwd-seed = H(MAX(STA-A-MAC, STA-B-MAC) || MIN(STA-A-MAC, STA-B-MAC), - * password || counter) + * password [|| identifier] || counter) */ sae_pwd_seed_key(addr1, addr2, addrs); addr[0] = password; len[0] = password_len; - addr[1] = &counter; - len[1] = sizeof(counter); + num_elem = 1; + if (identifier) { + addr[num_elem] = (const u8 *) identifier; + len[num_elem] = os_strlen(identifier); + num_elem++; + } + addr[num_elem] = &counter; + len[num_elem] = sizeof(counter); + num_elem++; for (counter = 1; !found; counter++) { u8 pwd_seed[SHA256_MAC_LEN]; @@ -584,8 +610,8 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1, } wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter); - if (hmac_sha256_vector(addrs, sizeof(addrs), 2, addr, len, - pwd_seed) < 0) + if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem, + addr, len, pwd_seed) < 0) break; res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc); if (res < 0) @@ -696,13 +722,15 @@ fail: int sae_prepare_commit(const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len, - struct sae_data *sae) + const char *identifier, struct sae_data *sae) { if (sae->tmp == NULL || (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password, - password_len) < 0) || + password_len, + identifier) < 0) || (sae->tmp->dh && sae_derive_pwe_ffc(sae, addr1, addr2, password, - password_len) < 0) || + password_len, + identifier) < 0) || sae_derive_commit(sae) < 0) return -1; return 0; @@ -842,7 +870,7 @@ int sae_process_commit(struct sae_data *sae) void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, - const struct wpabuf *token) + const struct wpabuf *token, const char *identifier) { u8 *pos; @@ -876,6 +904,16 @@ void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, wpa_hexdump(MSG_DEBUG, "SAE: own commit-element", pos, sae->tmp->prime_len); } + + if (identifier) { + /* Password Identifier element */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); + wpabuf_put_u8(buf, 1 + os_strlen(identifier)); + wpabuf_put_u8(buf, WLAN_EID_EXT_PASSWORD_IDENTIFIER); + wpabuf_put_str(buf, identifier); + wpa_printf(MSG_DEBUG, "SAE: own Password Identifier: %s", + identifier); + } } @@ -921,25 +959,70 @@ u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group) } +static int sae_is_password_id_elem(const u8 *pos, const u8 *end) +{ + return end - pos >= 3 && + pos[0] == WLAN_EID_EXTENSION && + pos[1] >= 1 && + end - pos - 2 >= pos[1] && + pos[2] == WLAN_EID_EXT_PASSWORD_IDENTIFIER; +} + + static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos, const u8 *end, const u8 **token, size_t *token_len) { - if ((sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len < end - *pos) { - size_t tlen = end - (*pos + (sae->tmp->ec ? 3 : 2) * - sae->tmp->prime_len); - wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); - if (token) - *token = *pos; - if (token_len) - *token_len = tlen; - *pos += tlen; - } else { - if (token) - *token = NULL; - if (token_len) - *token_len = 0; + size_t scalar_elem_len, tlen; + const u8 *elem; + + if (token) + *token = NULL; + if (token_len) + *token_len = 0; + + scalar_elem_len = (sae->tmp->ec ? 3 : 2) * sae->tmp->prime_len; + if (scalar_elem_len >= (size_t) (end - *pos)) + return; /* No extra data beyond peer scalar and element */ + + /* It is a bit difficult to parse this now that there is an + * optional variable length Anti-Clogging Token field and + * optional variable length Password Identifier element in the + * frame. We are sending out fixed length Anti-Clogging Token + * fields, so use that length as a requirement for the received + * token and check for the presence of possible Password + * Identifier element based on the element header information. + */ + tlen = end - (*pos + scalar_elem_len); + + if (tlen < SHA256_MAC_LEN) { + wpa_printf(MSG_DEBUG, + "SAE: Too short optional data (%u octets) to include our Anti-Clogging Token", + (unsigned int) tlen); + return; + } + + elem = *pos + scalar_elem_len; + if (sae_is_password_id_elem(elem, end)) { + /* Password Identifier element takes out all available + * extra octets, so there can be no Anti-Clogging token in + * this frame. */ + return; } + + elem += SHA256_MAC_LEN; + if (sae_is_password_id_elem(elem, end)) { + /* Password Identifier element is included in the end, so + * remove its length from the Anti-Clogging token field. */ + tlen -= 2 + elem[1]; + } + + wpa_hexdump(MSG_DEBUG, "SAE: Anti-Clogging Token", *pos, tlen); + if (token) + *token = *pos; + if (token_len) + *token_len = tlen; + *pos += tlen; } @@ -991,12 +1074,12 @@ static u16 sae_parse_commit_scalar(struct sae_data *sae, const u8 **pos, } -static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, +static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 **pos, const u8 *end) { u8 prime[SAE_MAX_ECC_PRIME_LEN]; - if (2 * sae->tmp->prime_len > end - pos) { + if (2 * sae->tmp->prime_len > end - *pos) { wpa_printf(MSG_DEBUG, "SAE: Not enough data for " "commit-element"); return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -1007,8 +1090,8 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, return WLAN_STATUS_UNSPECIFIED_FAILURE; /* element x and y coordinates < p */ - if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 || - os_memcmp(pos + sae->tmp->prime_len, prime, + if (os_memcmp(*pos, prime, sae->tmp->prime_len) >= 0 || + os_memcmp(*pos + sae->tmp->prime_len, prime, sae->tmp->prime_len) >= 0) { wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer " "element"); @@ -1016,13 +1099,13 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, } wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(x)", - pos, sae->tmp->prime_len); + *pos, sae->tmp->prime_len); wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element(y)", - pos + sae->tmp->prime_len, sae->tmp->prime_len); + *pos + sae->tmp->prime_len, sae->tmp->prime_len); crypto_ec_point_deinit(sae->tmp->peer_commit_element_ecc, 0); sae->tmp->peer_commit_element_ecc = - crypto_ec_point_from_bin(sae->tmp->ec, pos); + crypto_ec_point_from_bin(sae->tmp->ec, *pos); if (sae->tmp->peer_commit_element_ecc == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; @@ -1032,27 +1115,29 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos, return WLAN_STATUS_UNSPECIFIED_FAILURE; } + *pos += 2 * sae->tmp->prime_len; + return WLAN_STATUS_SUCCESS; } -static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos, +static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 **pos, const u8 *end) { struct crypto_bignum *res, *one; const u8 one_bin[1] = { 0x01 }; - if (sae->tmp->prime_len > end - pos) { + if (sae->tmp->prime_len > end - *pos) { wpa_printf(MSG_DEBUG, "SAE: Not enough data for " "commit-element"); return WLAN_STATUS_UNSPECIFIED_FAILURE; } - wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", pos, + wpa_hexdump(MSG_DEBUG, "SAE: Peer commit-element", *pos, sae->tmp->prime_len); crypto_bignum_deinit(sae->tmp->peer_commit_element_ffc, 0); sae->tmp->peer_commit_element_ffc = - crypto_bignum_init_set(pos, sae->tmp->prime_len); + crypto_bignum_init_set(*pos, sae->tmp->prime_len); if (sae->tmp->peer_commit_element_ffc == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; /* 1 < element < p - 1 */ @@ -1080,11 +1165,13 @@ static u16 sae_parse_commit_element_ffc(struct sae_data *sae, const u8 *pos, } crypto_bignum_deinit(res, 0); + *pos += sae->tmp->prime_len; + return WLAN_STATUS_SUCCESS; } -static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos, +static u16 sae_parse_commit_element(struct sae_data *sae, const u8 **pos, const u8 *end) { if (sae->tmp->dh) @@ -1093,6 +1180,44 @@ static u16 sae_parse_commit_element(struct sae_data *sae, const u8 *pos, } +static int sae_parse_password_identifier(struct sae_data *sae, + const u8 *pos, const u8 *end) +{ + wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame", + pos, end - pos); + if (!sae_is_password_id_elem(pos, end)) { + if (sae->tmp->pw_id) { + wpa_printf(MSG_DEBUG, + "SAE: No Password Identifier included, but expected one (%s)", + sae->tmp->pw_id); + return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; + } + os_free(sae->tmp->pw_id); + sae->tmp->pw_id = NULL; + return WLAN_STATUS_SUCCESS; /* No Password Identifier */ + } + + if (sae->tmp->pw_id && + (pos[1] - 1 != (int) os_strlen(sae->tmp->pw_id) || + os_memcmp(sae->tmp->pw_id, pos + 3, pos[1] - 1) != 0)) { + wpa_printf(MSG_DEBUG, + "SAE: The included Password Identifier does not match the expected one (%s)", + sae->tmp->pw_id); + return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER; + } + + os_free(sae->tmp->pw_id); + sae->tmp->pw_id = os_malloc(pos[1]); + if (!sae->tmp->pw_id) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + os_memcpy(sae->tmp->pw_id, pos + 3, pos[1] - 1); + sae->tmp->pw_id[pos[1] - 1] = '\0'; + wpa_hexdump_ascii(MSG_DEBUG, "SAE: Received Password Identifier", + sae->tmp->pw_id, pos[1] - 1); + return WLAN_STATUS_SUCCESS; +} + + u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, const u8 **token, size_t *token_len, int *allowed_groups) { @@ -1116,7 +1241,12 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, return res; /* commit-element */ - res = sae_parse_commit_element(sae, pos, end); + res = sae_parse_commit_element(sae, &pos, end); + if (res != WLAN_STATUS_SUCCESS) + return res; + + /* Optional Password Identifier element */ + res = sae_parse_password_identifier(sae, pos, end); if (res != WLAN_STATUS_SUCCESS) return res; @@ -1235,7 +1365,8 @@ void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf) /* Send-Confirm */ sc = wpabuf_put(buf, 0); wpabuf_put_le16(buf, sae->send_confirm); - sae->send_confirm++; + if (sae->send_confirm < 0xffff) + sae->send_confirm++; if (sae->tmp->ec) sae_cn_confirm_ecc(sae, sc, sae->tmp->own_commit_scalar, @@ -1292,3 +1423,19 @@ int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len) return 0; } + + +const char * sae_state_txt(enum sae_state state) +{ + switch (state) { + case SAE_NOTHING: + return "Nothing"; + case SAE_COMMITTED: + return "Committed"; + case SAE_CONFIRMED: + return "Confirmed"; + case SAE_ACCEPTED: + return "Accepted"; + } + return "?"; +} diff --git a/src/common/sae.h b/src/common/sae.h index a4270bc22d14a..3fbcb58d155a0 100644 --- a/src/common/sae.h +++ b/src/common/sae.h @@ -39,16 +39,22 @@ struct sae_temporary_data { struct crypto_bignum *prime_buf; struct crypto_bignum *order_buf; struct wpabuf *anti_clogging_token; + char *pw_id; +}; + +enum sae_state { + SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED }; struct sae_data { - enum { SAE_NOTHING, SAE_COMMITTED, SAE_CONFIRMED, SAE_ACCEPTED } state; + enum sae_state state; u16 send_confirm; u8 pmk[SAE_PMK_LEN]; u8 pmkid[SAE_PMKID_LEN]; struct crypto_bignum *peer_commit_scalar; int group; - int sync; + unsigned int sync; /* protocol instance variable: Sync */ + u16 rc; /* protocol instance variable: Rc (received send-confirm) */ struct sae_temporary_data *tmp; }; @@ -58,14 +64,15 @@ void sae_clear_data(struct sae_data *sae); int sae_prepare_commit(const u8 *addr1, const u8 *addr2, const u8 *password, size_t password_len, - struct sae_data *sae); + const char *identifier, struct sae_data *sae); int sae_process_commit(struct sae_data *sae); void sae_write_commit(struct sae_data *sae, struct wpabuf *buf, - const struct wpabuf *token); + const struct wpabuf *token, const char *identifier); u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len, const u8 **token, size_t *token_len, int *allowed_groups); void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf); int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len); u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group); +const char * sae_state_txt(enum sae_state state); #endif /* SAE_H */ diff --git a/src/common/version.h b/src/common/version.h index 75e5c6e006cce..2f47903d407c2 100644 --- a/src/common/version.h +++ b/src/common/version.h @@ -9,6 +9,6 @@ #define GIT_VERSION_STR_POSTFIX "" #endif /* GIT_VERSION_STR_POSTFIX */ -#define VERSION_STR "2.6" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX +#define VERSION_STR "2.7" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX #endif /* VERSION_H */ diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index 299b8bbee031a..2d989048ac943 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -1,6 +1,6 @@ /* * WPA/RSN - Shared functions for supplicant and authenticator - * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -13,6 +13,7 @@ #include "crypto/sha1.h" #include "crypto/sha256.h" #include "crypto/sha384.h" +#include "crypto/sha512.h" #include "crypto/aes_wrap.h" #include "crypto/crypto.h" #include "ieee802_11_defs.h" @@ -20,27 +21,151 @@ #include "wpa_common.h" -static unsigned int wpa_kck_len(int akmp) +static unsigned int wpa_kck_len(int akmp, size_t pmk_len) { - if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + switch (akmp) { + case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + return 24; + case WPA_KEY_MGMT_FILS_SHA256: + case WPA_KEY_MGMT_FT_FILS_SHA256: + case WPA_KEY_MGMT_FILS_SHA384: + case WPA_KEY_MGMT_FT_FILS_SHA384: + return 0; + case WPA_KEY_MGMT_DPP: + return pmk_len / 2; + case WPA_KEY_MGMT_OWE: + return pmk_len / 2; + default: + return 16; + } +} + + +#ifdef CONFIG_IEEE80211R +static unsigned int wpa_kck2_len(int akmp) +{ + switch (akmp) { + case WPA_KEY_MGMT_FT_FILS_SHA256: + return 16; + case WPA_KEY_MGMT_FT_FILS_SHA384: return 24; - return 16; + default: + return 0; + } } +#endif /* CONFIG_IEEE80211R */ -static unsigned int wpa_kek_len(int akmp) +static unsigned int wpa_kek_len(int akmp, size_t pmk_len) { - if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + switch (akmp) { + case WPA_KEY_MGMT_FILS_SHA384: + case WPA_KEY_MGMT_FT_FILS_SHA384: + return 64; + case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: + case WPA_KEY_MGMT_FILS_SHA256: + case WPA_KEY_MGMT_FT_FILS_SHA256: + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: return 32; - return 16; + case WPA_KEY_MGMT_DPP: + return pmk_len <= 32 ? 16 : 32; + case WPA_KEY_MGMT_OWE: + return pmk_len <= 32 ? 16 : 32; + default: + return 16; + } } -unsigned int wpa_mic_len(int akmp) +#ifdef CONFIG_IEEE80211R +static unsigned int wpa_kek2_len(int akmp) { - if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + switch (akmp) { + case WPA_KEY_MGMT_FT_FILS_SHA256: + return 16; + case WPA_KEY_MGMT_FT_FILS_SHA384: + return 32; + default: + return 0; + } +} +#endif /* CONFIG_IEEE80211R */ + + +unsigned int wpa_mic_len(int akmp, size_t pmk_len) +{ + switch (akmp) { + case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: return 24; - return 16; + case WPA_KEY_MGMT_FILS_SHA256: + case WPA_KEY_MGMT_FILS_SHA384: + case WPA_KEY_MGMT_FT_FILS_SHA256: + case WPA_KEY_MGMT_FT_FILS_SHA384: + return 0; + case WPA_KEY_MGMT_DPP: + return pmk_len / 2; + case WPA_KEY_MGMT_OWE: + return pmk_len / 2; + default: + return 16; + } +} + + +/** + * wpa_use_akm_defined - Is AKM-defined Key Descriptor Version used + * @akmp: WPA_KEY_MGMT_* used in key derivation + * Returns: 1 if AKM-defined Key Descriptor Version is used; 0 otherwise + */ +int wpa_use_akm_defined(int akmp) +{ + return akmp == WPA_KEY_MGMT_OSEN || + akmp == WPA_KEY_MGMT_OWE || + akmp == WPA_KEY_MGMT_DPP || + akmp == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 || + wpa_key_mgmt_sae(akmp) || + wpa_key_mgmt_suite_b(akmp) || + wpa_key_mgmt_fils(akmp); +} + + +/** + * wpa_use_cmac - Is CMAC integrity algorithm used for EAPOL-Key MIC + * @akmp: WPA_KEY_MGMT_* used in key derivation + * Returns: 1 if CMAC is used; 0 otherwise + */ +int wpa_use_cmac(int akmp) +{ + return akmp == WPA_KEY_MGMT_OSEN || + akmp == WPA_KEY_MGMT_OWE || + akmp == WPA_KEY_MGMT_DPP || + wpa_key_mgmt_ft(akmp) || + wpa_key_mgmt_sha256(akmp) || + wpa_key_mgmt_sae(akmp) || + wpa_key_mgmt_suite_b(akmp); +} + + +/** + * wpa_use_aes_key_wrap - Is AES Keywrap algorithm used for EAPOL-Key Key Data + * @akmp: WPA_KEY_MGMT_* used in key derivation + * Returns: 1 if AES Keywrap is used; 0 otherwise + * + * Note: AKM 00-0F-AC:1 and 00-0F-AC:2 have special rules for selecting whether + * to use AES Keywrap based on the negotiated pairwise cipher. This function + * does not cover those special cases. + */ +int wpa_use_aes_key_wrap(int akmp) +{ + return akmp == WPA_KEY_MGMT_OSEN || + akmp == WPA_KEY_MGMT_OWE || + akmp == WPA_KEY_MGMT_DPP || + wpa_key_mgmt_ft(akmp) || + wpa_key_mgmt_sha256(akmp) || + wpa_key_mgmt_sae(akmp) || + wpa_key_mgmt_suite_b(akmp); } @@ -67,30 +192,50 @@ unsigned int wpa_mic_len(int akmp) int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, const u8 *buf, size_t len, u8 *mic) { - u8 hash[SHA384_MAC_LEN]; + u8 hash[SHA512_MAC_LEN]; + + if (key_len == 0) { + wpa_printf(MSG_DEBUG, + "WPA: KCK not set - cannot calculate MIC"); + return -1; + } switch (ver) { #ifndef CONFIG_FIPS case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4: + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using HMAC-MD5"); return hmac_md5(key, key_len, buf, len, mic); #endif /* CONFIG_FIPS */ case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES: + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using HMAC-SHA1"); if (hmac_sha1(key, key_len, buf, len, hash)) return -1; os_memcpy(mic, hash, MD5_MAC_LEN); break; #if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) case WPA_KEY_INFO_TYPE_AES_128_CMAC: + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key MIC using AES-CMAC"); return omac1_aes_128(key, buf, len, mic); #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ case WPA_KEY_INFO_TYPE_AKM_DEFINED: switch (akmp) { +#ifdef CONFIG_SAE + case WPA_KEY_MGMT_SAE: + case WPA_KEY_MGMT_FT_SAE: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - SAE)"); + return omac1_aes_128(key, buf, len, mic); +#endif /* CONFIG_SAE */ #ifdef CONFIG_HS20 case WPA_KEY_MGMT_OSEN: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - OSEN)"); return omac1_aes_128(key, buf, len, mic); #endif /* CONFIG_HS20 */ #ifdef CONFIG_SUITEB case WPA_KEY_MGMT_IEEE8021X_SUITE_B: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC using HMAC-SHA256 (AKM-defined - Suite B)"); if (hmac_sha256(key, key_len, buf, len, hash)) return -1; os_memcpy(mic, hash, MD5_MAC_LEN); @@ -98,16 +243,79 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, #endif /* CONFIG_SUITEB */ #ifdef CONFIG_SUITEB192 case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC using HMAC-SHA384 (AKM-defined - Suite B 192-bit)"); if (hmac_sha384(key, key_len, buf, len, hash)) return -1; os_memcpy(mic, hash, 24); break; #endif /* CONFIG_SUITEB192 */ +#ifdef CONFIG_OWE + case WPA_KEY_MGMT_OWE: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - OWE)", + (unsigned int) key_len * 8 * 2); + if (key_len == 128 / 8) { + if (hmac_sha256(key, key_len, buf, len, hash)) + return -1; + } else if (key_len == 192 / 8) { + if (hmac_sha384(key, key_len, buf, len, hash)) + return -1; + } else if (key_len == 256 / 8) { + if (hmac_sha512(key, key_len, buf, len, hash)) + return -1; + } else { + wpa_printf(MSG_INFO, + "OWE: Unsupported KCK length: %u", + (unsigned int) key_len); + return -1; + } + os_memcpy(mic, hash, key_len); + break; +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + case WPA_KEY_MGMT_DPP: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - DPP)", + (unsigned int) key_len * 8 * 2); + if (key_len == 128 / 8) { + if (hmac_sha256(key, key_len, buf, len, hash)) + return -1; + } else if (key_len == 192 / 8) { + if (hmac_sha384(key, key_len, buf, len, hash)) + return -1; + } else if (key_len == 256 / 8) { + if (hmac_sha512(key, key_len, buf, len, hash)) + return -1; + } else { + wpa_printf(MSG_INFO, + "DPP: Unsupported KCK length: %u", + (unsigned int) key_len); + return -1; + } + os_memcpy(mic, hash, key_len); + break; +#endif /* CONFIG_DPP */ +#if defined(CONFIG_IEEE80211R) && defined(CONFIG_SHA384) + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC using HMAC-SHA384 (AKM-defined - FT 802.1X SHA384)"); + if (hmac_sha384(key, key_len, buf, len, hash)) + return -1; + os_memcpy(mic, hash, 24); + break; +#endif /* CONFIG_IEEE80211R && CONFIG_SHA384 */ default: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC algorithm not known (AKM-defined - akmp=0x%x)", + akmp); return -1; } break; default: + wpa_printf(MSG_DEBUG, + "WPA: EAPOL-Key MIC algorithm not known (ver=%d)", + ver); return -1; } @@ -133,10 +341,6 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver, * PTK = PRF-X(PMK, "Pairwise key expansion", * Min(AA, SA) || Max(AA, SA) || * Min(ANonce, SNonce) || Max(ANonce, SNonce)) - * - * STK = PRF-X(SMK, "Peer key expansion", - * Min(MAC_I, MAC_P) || Max(MAC_I, MAC_P) || - * Min(INonce, PNonce) || Max(INonce, PNonce)) */ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, const u8 *addr1, const u8 *addr2, @@ -147,6 +351,11 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN]; size_t ptk_len; + if (pmk_len == 0) { + wpa_printf(MSG_ERROR, "WPA: No PMK set for PTK derivation"); + return -1; + } + if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) { os_memcpy(data, addr1, ETH_ALEN); os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN); @@ -165,24 +374,62 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, WPA_NONCE_LEN); } - ptk->kck_len = wpa_kck_len(akmp); - ptk->kek_len = wpa_kek_len(akmp); + ptk->kck_len = wpa_kck_len(akmp, pmk_len); + ptk->kek_len = wpa_kek_len(akmp, pmk_len); ptk->tk_len = wpa_cipher_key_len(cipher); + if (ptk->tk_len == 0) { + wpa_printf(MSG_ERROR, + "WPA: Unsupported cipher (0x%x) used in PTK derivation", + cipher); + return -1; + } ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len; -#ifdef CONFIG_SUITEB192 - if (wpa_key_mgmt_sha384(akmp)) - sha384_prf(pmk, pmk_len, label, data, sizeof(data), - tmp, ptk_len); - else -#endif /* CONFIG_SUITEB192 */ -#ifdef CONFIG_IEEE80211W - if (wpa_key_mgmt_sha256(akmp)) - sha256_prf(pmk, pmk_len, label, data, sizeof(data), - tmp, ptk_len); - else -#endif /* CONFIG_IEEE80211W */ - sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp, ptk_len); + if (wpa_key_mgmt_sha384(akmp)) { +#if defined(CONFIG_SUITEB192) || defined(CONFIG_FILS) + wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)"); + if (sha384_prf(pmk, pmk_len, label, data, sizeof(data), + tmp, ptk_len) < 0) + return -1; +#else /* CONFIG_SUITEB192 || CONFIG_FILS */ + return -1; +#endif /* CONFIG_SUITEB192 || CONFIG_FILS */ + } else if (wpa_key_mgmt_sha256(akmp) || akmp == WPA_KEY_MGMT_OWE) { +#if defined(CONFIG_IEEE80211W) || defined(CONFIG_SAE) || defined(CONFIG_FILS) + wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)"); + if (sha256_prf(pmk, pmk_len, label, data, sizeof(data), + tmp, ptk_len) < 0) + return -1; +#else /* CONFIG_IEEE80211W or CONFIG_SAE or CONFIG_FILS */ + return -1; +#endif /* CONFIG_IEEE80211W or CONFIG_SAE or CONFIG_FILS */ +#ifdef CONFIG_DPP + } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 32) { + wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)"); + if (sha256_prf(pmk, pmk_len, label, data, sizeof(data), + tmp, ptk_len) < 0) + return -1; + } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 48) { + wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)"); + if (sha384_prf(pmk, pmk_len, label, data, sizeof(data), + tmp, ptk_len) < 0) + return -1; + } else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 64) { + wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA512)"); + if (sha512_prf(pmk, pmk_len, label, data, sizeof(data), + tmp, ptk_len) < 0) + return -1; + } else if (akmp == WPA_KEY_MGMT_DPP) { + wpa_printf(MSG_INFO, "DPP: Unknown PMK length %u", + (unsigned int) pmk_len); + return -1; +#endif /* CONFIG_DPP */ + } else { + wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA1)"); + if (sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp, + ptk_len) < 0) + return -1; + } wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR, MAC2STR(addr1), MAC2STR(addr2)); @@ -200,10 +447,290 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len); wpa_hexdump_key(MSG_DEBUG, "WPA: TK", ptk->tk, ptk->tk_len); + ptk->kek2_len = 0; + ptk->kck2_len = 0; + os_memset(tmp, 0, sizeof(tmp)); return 0; } +#ifdef CONFIG_FILS + +int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len, + const u8 *snonce, const u8 *anonce, const u8 *dh_ss, + size_t dh_ss_len, u8 *pmk, size_t *pmk_len) +{ + u8 nonces[2 * FILS_NONCE_LEN]; + const u8 *addr[2]; + size_t len[2]; + size_t num_elem; + int res; + + /* PMK = HMAC-Hash(SNonce || ANonce, rMSK [ || DHss ]) */ + wpa_printf(MSG_DEBUG, "FILS: rMSK to PMK derivation"); + + if (wpa_key_mgmt_sha384(akmp)) + *pmk_len = SHA384_MAC_LEN; + else if (wpa_key_mgmt_sha256(akmp)) + *pmk_len = SHA256_MAC_LEN; + else + return -1; + + wpa_hexdump_key(MSG_DEBUG, "FILS: rMSK", rmsk, rmsk_len); + wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: DHss", dh_ss, dh_ss_len); + + os_memcpy(nonces, snonce, FILS_NONCE_LEN); + os_memcpy(&nonces[FILS_NONCE_LEN], anonce, FILS_NONCE_LEN); + addr[0] = rmsk; + len[0] = rmsk_len; + num_elem = 1; + if (dh_ss) { + addr[1] = dh_ss; + len[1] = dh_ss_len; + num_elem++; + } + if (wpa_key_mgmt_sha384(akmp)) + res = hmac_sha384_vector(nonces, 2 * FILS_NONCE_LEN, num_elem, + addr, len, pmk); + else + res = hmac_sha256_vector(nonces, 2 * FILS_NONCE_LEN, num_elem, + addr, len, pmk); + if (res == 0) + wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, *pmk_len); + else + *pmk_len = 0; + return res; +} + + +int fils_pmkid_erp(int akmp, const u8 *reauth, size_t reauth_len, + u8 *pmkid) +{ + const u8 *addr[1]; + size_t len[1]; + u8 hash[SHA384_MAC_LEN]; + int res; + + /* PMKID = Truncate-128(Hash(EAP-Initiate/Reauth)) */ + addr[0] = reauth; + len[0] = reauth_len; + if (wpa_key_mgmt_sha384(akmp)) + res = sha384_vector(1, addr, len, hash); + else if (wpa_key_mgmt_sha256(akmp)) + res = sha256_vector(1, addr, len, hash); + else + return -1; + if (res) + return res; + os_memcpy(pmkid, hash, PMKID_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN); + return 0; +} + + +int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa, + const u8 *snonce, const u8 *anonce, const u8 *dhss, + size_t dhss_len, struct wpa_ptk *ptk, + u8 *ick, size_t *ick_len, int akmp, int cipher, + u8 *fils_ft, size_t *fils_ft_len) +{ + u8 *data, *pos; + size_t data_len; + u8 tmp[FILS_ICK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN + + FILS_FT_MAX_LEN]; + size_t key_data_len; + const char *label = "FILS PTK Derivation"; + int ret = -1; + + /* + * FILS-Key-Data = PRF-X(PMK, "FILS PTK Derivation", + * SPA || AA || SNonce || ANonce [ || DHss ]) + * ICK = L(FILS-Key-Data, 0, ICK_bits) + * KEK = L(FILS-Key-Data, ICK_bits, KEK_bits) + * TK = L(FILS-Key-Data, ICK_bits + KEK_bits, TK_bits) + * If doing FT initial mobility domain association: + * FILS-FT = L(FILS-Key-Data, ICK_bits + KEK_bits + TK_bits, + * FILS-FT_bits) + */ + data_len = 2 * ETH_ALEN + 2 * FILS_NONCE_LEN + dhss_len; + data = os_malloc(data_len); + if (!data) + goto err; + pos = data; + os_memcpy(pos, spa, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, aa, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, snonce, FILS_NONCE_LEN); + pos += FILS_NONCE_LEN; + os_memcpy(pos, anonce, FILS_NONCE_LEN); + pos += FILS_NONCE_LEN; + if (dhss) + os_memcpy(pos, dhss, dhss_len); + + ptk->kck_len = 0; + ptk->kek_len = wpa_kek_len(akmp, pmk_len); + ptk->tk_len = wpa_cipher_key_len(cipher); + if (wpa_key_mgmt_sha384(akmp)) + *ick_len = 48; + else if (wpa_key_mgmt_sha256(akmp)) + *ick_len = 32; + else + goto err; + key_data_len = *ick_len + ptk->kek_len + ptk->tk_len; + + if (fils_ft && fils_ft_len) { + if (akmp == WPA_KEY_MGMT_FT_FILS_SHA256) { + *fils_ft_len = 32; + } else if (akmp == WPA_KEY_MGMT_FT_FILS_SHA384) { + *fils_ft_len = 48; + } else { + *fils_ft_len = 0; + fils_ft = NULL; + } + key_data_len += *fils_ft_len; + } + + if (wpa_key_mgmt_sha384(akmp)) { + wpa_printf(MSG_DEBUG, "FILS: PTK derivation using PRF(SHA384)"); + if (sha384_prf(pmk, pmk_len, label, data, data_len, + tmp, key_data_len) < 0) + goto err; + } else { + wpa_printf(MSG_DEBUG, "FILS: PTK derivation using PRF(SHA256)"); + if (sha256_prf(pmk, pmk_len, label, data, data_len, + tmp, key_data_len) < 0) + goto err; + } + + wpa_printf(MSG_DEBUG, "FILS: PTK derivation - SPA=" MACSTR + " AA=" MACSTR, MAC2STR(spa), MAC2STR(aa)); + wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN); + if (dhss) + wpa_hexdump_key(MSG_DEBUG, "FILS: DHss", dhss, dhss_len); + wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, pmk_len); + wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-Key-Data", tmp, key_data_len); + + os_memcpy(ick, tmp, *ick_len); + wpa_hexdump_key(MSG_DEBUG, "FILS: ICK", ick, *ick_len); + + os_memcpy(ptk->kek, tmp + *ick_len, ptk->kek_len); + wpa_hexdump_key(MSG_DEBUG, "FILS: KEK", ptk->kek, ptk->kek_len); + + os_memcpy(ptk->tk, tmp + *ick_len + ptk->kek_len, ptk->tk_len); + wpa_hexdump_key(MSG_DEBUG, "FILS: TK", ptk->tk, ptk->tk_len); + + if (fils_ft && fils_ft_len) { + os_memcpy(fils_ft, tmp + *ick_len + ptk->kek_len + ptk->tk_len, + *fils_ft_len); + wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-FT", + fils_ft, *fils_ft_len); + } + + ptk->kek2_len = 0; + ptk->kck2_len = 0; + + os_memset(tmp, 0, sizeof(tmp)); + ret = 0; +err: + bin_clear_free(data, data_len); + return ret; +} + + +int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce, + const u8 *anonce, const u8 *sta_addr, const u8 *bssid, + const u8 *g_sta, size_t g_sta_len, + const u8 *g_ap, size_t g_ap_len, + int akmp, u8 *key_auth_sta, u8 *key_auth_ap, + size_t *key_auth_len) +{ + const u8 *addr[6]; + size_t len[6]; + size_t num_elem = 4; + int res; + + wpa_printf(MSG_DEBUG, "FILS: Key-Auth derivation: STA-MAC=" MACSTR + " AP-BSSID=" MACSTR, MAC2STR(sta_addr), MAC2STR(bssid)); + wpa_hexdump_key(MSG_DEBUG, "FILS: ICK", ick, ick_len); + wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: gSTA", g_sta, g_sta_len); + wpa_hexdump(MSG_DEBUG, "FILS: gAP", g_ap, g_ap_len); + + /* + * For (Re)Association Request frame (STA->AP): + * Key-Auth = HMAC-Hash(ICK, SNonce || ANonce || STA-MAC || AP-BSSID + * [ || gSTA || gAP ]) + */ + addr[0] = snonce; + len[0] = FILS_NONCE_LEN; + addr[1] = anonce; + len[1] = FILS_NONCE_LEN; + addr[2] = sta_addr; + len[2] = ETH_ALEN; + addr[3] = bssid; + len[3] = ETH_ALEN; + if (g_sta && g_ap_len && g_ap && g_ap_len) { + addr[4] = g_sta; + len[4] = g_sta_len; + addr[5] = g_ap; + len[5] = g_ap_len; + num_elem = 6; + } + + if (wpa_key_mgmt_sha384(akmp)) { + *key_auth_len = 48; + res = hmac_sha384_vector(ick, ick_len, num_elem, addr, len, + key_auth_sta); + } else if (wpa_key_mgmt_sha256(akmp)) { + *key_auth_len = 32; + res = hmac_sha256_vector(ick, ick_len, num_elem, addr, len, + key_auth_sta); + } else { + return -1; + } + if (res < 0) + return res; + + /* + * For (Re)Association Response frame (AP->STA): + * Key-Auth = HMAC-Hash(ICK, ANonce || SNonce || AP-BSSID || STA-MAC + * [ || gAP || gSTA ]) + */ + addr[0] = anonce; + addr[1] = snonce; + addr[2] = bssid; + addr[3] = sta_addr; + if (g_sta && g_ap_len && g_ap && g_ap_len) { + addr[4] = g_ap; + len[4] = g_ap_len; + addr[5] = g_sta; + len[5] = g_sta_len; + } + + if (wpa_key_mgmt_sha384(akmp)) + res = hmac_sha384_vector(ick, ick_len, num_elem, addr, len, + key_auth_ap); + else if (wpa_key_mgmt_sha256(akmp)) + res = hmac_sha256_vector(ick, ick_len, num_elem, addr, len, + key_auth_ap); + if (res < 0) + return res; + + wpa_hexdump(MSG_DEBUG, "FILS: Key-Auth (STA)", + key_auth_sta, *key_auth_len); + wpa_hexdump(MSG_DEBUG, "FILS: Key-Auth (AP)", + key_auth_ap, *key_auth_len); + + return 0; +} + +#endif /* CONFIG_FILS */ + #ifdef CONFIG_IEEE80211R int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, @@ -216,14 +743,23 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, const u8 *addr[9]; size_t len[9]; size_t i, num_elem = 0; - u8 zero_mic[16]; - - if (kck_len != 16) { + u8 zero_mic[24]; + size_t mic_len, fte_fixed_len; + + if (kck_len == 16) { + mic_len = 16; +#ifdef CONFIG_SHA384 + } else if (kck_len == 24) { + mic_len = 24; +#endif /* CONFIG_SHA384 */ + } else { wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u", (unsigned int) kck_len); return -1; } + fte_fixed_len = sizeof(struct rsn_ftie) - 16 + mic_len; + addr[num_elem] = sta_addr; len[num_elem] = ETH_ALEN; num_elem++; @@ -247,7 +783,7 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, num_elem++; } if (ftie) { - if (ftie_len < 2 + sizeof(struct rsn_ftie)) + if (ftie_len < 2 + fte_fixed_len) return -1; /* IE hdr and mic_control */ @@ -256,14 +792,14 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, num_elem++; /* MIC field with all zeros */ - os_memset(zero_mic, 0, sizeof(zero_mic)); + os_memset(zero_mic, 0, mic_len); addr[num_elem] = zero_mic; - len[num_elem] = sizeof(zero_mic); + len[num_elem] = mic_len; num_elem++; /* Rest of FTIE */ - addr[num_elem] = ftie + 2 + 2 + 16; - len[num_elem] = ftie_len - (2 + 2 + 16); + addr[num_elem] = ftie + 2 + 2 + mic_len; + len[num_elem] = ftie_len - (2 + 2 + mic_len); num_elem++; } if (ric) { @@ -274,7 +810,17 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, for (i = 0; i < num_elem; i++) wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", addr[i], len[i]); - if (omac1_aes_128_vector(kck, num_elem, addr, len, mic)) +#ifdef CONFIG_SHA384 + if (kck_len == 24) { + u8 hash[SHA384_MAC_LEN]; + + if (hmac_sha384_vector(kck, kck_len, num_elem, addr, len, hash)) + return -1; + os_memcpy(mic, hash, 24); + } +#endif /* CONFIG_SHA384 */ + if (kck_len == 16 && + omac1_aes_128_vector(kck, num_elem, addr, len, mic)) return -1; return 0; @@ -282,23 +828,27 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, - struct wpa_ft_ies *parse) + struct wpa_ft_ies *parse, int use_sha384) { const u8 *end, *pos; parse->ftie = ie; parse->ftie_len = ie_len; - pos = ie + sizeof(struct rsn_ftie); + pos = ie + (use_sha384 ? sizeof(struct rsn_ftie_sha384) : + sizeof(struct rsn_ftie)); end = ie + ie_len; + wpa_hexdump(MSG_DEBUG, "FT: Parse FTE subelements", pos, end - pos); while (end - pos >= 2) { u8 id, len; id = *pos++; len = *pos++; - if (len > end - pos) + if (len > end - pos) { + wpa_printf(MSG_DEBUG, "FT: Truncated subelement"); break; + } switch (id) { case FTIE_SUBELEM_R1KH_ID: @@ -330,6 +880,9 @@ static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, parse->igtk_len = len; break; #endif /* CONFIG_IEEE80211W */ + default: + wpa_printf(MSG_DEBUG, "FT: Unknown subelem id %u", id); + break; } pos += len; @@ -340,13 +893,19 @@ static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, - struct wpa_ft_ies *parse) + struct wpa_ft_ies *parse, int use_sha384) { const u8 *end, *pos; struct wpa_ie_data data; int ret; const struct rsn_ftie *ftie; int prot_ie_count = 0; + int update_use_sha384 = 0; + + if (use_sha384 < 0) { + use_sha384 = 0; + update_use_sha384 = 1; + } os_memset(parse, 0, sizeof(*parse)); if (ies == NULL) @@ -364,6 +923,7 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, switch (id) { case WLAN_EID_RSN: + wpa_hexdump(MSG_DEBUG, "FT: RSNE", pos, len); parse->rsn = pos; parse->rsn_len = len; ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2, @@ -376,22 +936,65 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, } if (data.num_pmkid == 1 && data.pmkid) parse->rsn_pmkid = data.pmkid; + parse->key_mgmt = data.key_mgmt; + parse->pairwise_cipher = data.pairwise_cipher; + if (update_use_sha384) { + use_sha384 = + wpa_key_mgmt_sha384(parse->key_mgmt); + update_use_sha384 = 0; + } break; case WLAN_EID_MOBILITY_DOMAIN: + wpa_hexdump(MSG_DEBUG, "FT: MDE", pos, len); if (len < sizeof(struct rsn_mdie)) return -1; parse->mdie = pos; parse->mdie_len = len; break; case WLAN_EID_FAST_BSS_TRANSITION: + wpa_hexdump(MSG_DEBUG, "FT: FTE", pos, len); + if (use_sha384) { + const struct rsn_ftie_sha384 *ftie_sha384; + + if (len < sizeof(*ftie_sha384)) + return -1; + ftie_sha384 = + (const struct rsn_ftie_sha384 *) pos; + wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control", + ftie_sha384->mic_control, 2); + wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC", + ftie_sha384->mic, + sizeof(ftie_sha384->mic)); + wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce", + ftie_sha384->anonce, + WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce", + ftie_sha384->snonce, + WPA_NONCE_LEN); + prot_ie_count = ftie_sha384->mic_control[1]; + if (wpa_ft_parse_ftie(pos, len, parse, 1) < 0) + return -1; + break; + } + if (len < sizeof(*ftie)) return -1; ftie = (const struct rsn_ftie *) pos; + wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC Control", + ftie->mic_control, 2); + wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC", + ftie->mic, sizeof(ftie->mic)); + wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce", + ftie->anonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce", + ftie->snonce, WPA_NONCE_LEN); prot_ie_count = ftie->mic_control[1]; - if (wpa_ft_parse_ftie(pos, len, parse) < 0) + if (wpa_ft_parse_ftie(pos, len, parse, 0) < 0) return -1; break; case WLAN_EID_TIMEOUT_INTERVAL: + wpa_hexdump(MSG_DEBUG, "FT: Timeout Interval", + pos, len); if (len != 5) break; parse->tie = pos; @@ -493,6 +1096,10 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s) return WPA_KEY_MGMT_FT_IEEE8021X; if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK) return WPA_KEY_MGMT_FT_PSK; +#ifdef CONFIG_SHA384 + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384) + return WPA_KEY_MGMT_FT_IEEE8021X_SHA384; +#endif /* CONFIG_SHA384 */ #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256) @@ -510,6 +1117,22 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s) return WPA_KEY_MGMT_IEEE8021X_SUITE_B; if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192) return WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FILS_SHA256) + return WPA_KEY_MGMT_FILS_SHA256; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FILS_SHA384) + return WPA_KEY_MGMT_FILS_SHA384; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA256) + return WPA_KEY_MGMT_FT_FILS_SHA256; + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA384) + return WPA_KEY_MGMT_FT_FILS_SHA384; +#ifdef CONFIG_OWE + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OWE) + return WPA_KEY_MGMT_OWE; +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_DPP) + return WPA_KEY_MGMT_DPP; +#endif /* CONFIG_DPP */ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN) return WPA_KEY_MGMT_OSEN; return 0; @@ -579,6 +1202,8 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, pos = rsn_ie + 6; left = rsn_ie_len - 6; + data->group_cipher = WPA_CIPHER_GTK_NOT_USED; + data->key_mgmt = WPA_KEY_MGMT_OSEN; data->proto = WPA_PROTO_OSEN; } else { const struct rsn_ie_hdr *hdr; @@ -849,27 +1474,39 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, * * IEEE Std 802.11r-2008 - 8.5.1.5.3 */ -void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, - const u8 *ssid, size_t ssid_len, - const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, - const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name) +int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, + const u8 *ssid, size_t ssid_len, + const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, + const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name, + int use_sha384) { u8 buf[1 + SSID_MAX_LEN + MOBILITY_DOMAIN_ID_LEN + 1 + FT_R0KH_ID_MAX_LEN + ETH_ALEN]; - u8 *pos, r0_key_data[48], hash[32]; + u8 *pos, r0_key_data[64], hash[48]; const u8 *addr[2]; size_t len[2]; + size_t q = use_sha384 ? 48 : 32; + size_t r0_key_data_len = q + 16; /* * R0-Key-Data = KDF-384(XXKey, "FT-R0", * SSIDlength || SSID || MDID || R0KHlength || * R0KH-ID || S0KH-ID) - * XXKey is either the second 256 bits of MSK or PSK. - * PMK-R0 = L(R0-Key-Data, 0, 256) - * PMK-R0Name-Salt = L(R0-Key-Data, 256, 128) + * XXKey is either the second 256 bits of MSK or PSK; or the first + * 384 bits of MSK for FT-EAP-SHA384. + * PMK-R0 = L(R0-Key-Data, 0, Q) + * PMK-R0Name-Salt = L(R0-Key-Data, Q, 128) + * Q = 384 for FT-EAP-SHA384; otherwise, 256 */ if (ssid_len > SSID_MAX_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN) - return; + return -1; + wpa_printf(MSG_DEBUG, "FT: Derive PMK-R0 using KDF-%s", + use_sha384 ? "SHA384" : "SHA256"); + wpa_hexdump_key(MSG_DEBUG, "FT: XXKey", xxkey, xxkey_len); + wpa_hexdump_ascii(MSG_DEBUG, "FT: SSID", ssid, ssid_len); + wpa_hexdump(MSG_DEBUG, "FT: MDID", mdid, MOBILITY_DOMAIN_ID_LEN); + wpa_hexdump_ascii(MSG_DEBUG, "FT: R0KH-ID", r0kh_id, r0kh_id_len); + wpa_printf(MSG_DEBUG, "FT: S0KH-ID: " MACSTR, MAC2STR(s0kh_id)); pos = buf; *pos++ = ssid_len; os_memcpy(pos, ssid, ssid_len); @@ -882,20 +1519,51 @@ void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, os_memcpy(pos, s0kh_id, ETH_ALEN); pos += ETH_ALEN; - sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf, - r0_key_data, sizeof(r0_key_data)); - os_memcpy(pmk_r0, r0_key_data, PMK_LEN); +#ifdef CONFIG_SHA384 + if (use_sha384) { + if (xxkey_len != SHA384_MAC_LEN) { + wpa_printf(MSG_ERROR, + "FT: Unexpected XXKey length %d (expected %d)", + (int) xxkey_len, SHA384_MAC_LEN); + return -1; + } + if (sha384_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf, + r0_key_data, r0_key_data_len) < 0) + return -1; + } +#endif /* CONFIG_SHA384 */ + if (!use_sha384) { + if (xxkey_len != PMK_LEN) { + wpa_printf(MSG_ERROR, + "FT: Unexpected XXKey length %d (expected %d)", + (int) xxkey_len, PMK_LEN); + return -1; + } + if (sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf, + r0_key_data, r0_key_data_len) < 0) + return -1; + } + os_memcpy(pmk_r0, r0_key_data, q); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, q); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0Name-Salt", &r0_key_data[q], 16); /* - * PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt) + * PMKR0Name = Truncate-128(Hash("FT-R0N" || PMK-R0Name-Salt) */ addr[0] = (const u8 *) "FT-R0N"; len[0] = 6; - addr[1] = r0_key_data + PMK_LEN; + addr[1] = &r0_key_data[q]; len[1] = 16; - sha256_vector(2, addr, len, hash); +#ifdef CONFIG_SHA384 + if (use_sha384 && sha384_vector(2, addr, len, hash) < 0) + return -1; +#endif /* CONFIG_SHA384 */ + if (!use_sha384 && sha256_vector(2, addr, len, hash) < 0) + return -1; os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN); + os_memset(r0_key_data, 0, sizeof(r0_key_data)); + return 0; } @@ -904,16 +1572,16 @@ void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, * * IEEE Std 802.11r-2008 - 8.5.1.5.4 */ -void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, - const u8 *s1kh_id, u8 *pmk_r1_name) +int wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, + const u8 *s1kh_id, u8 *pmk_r1_name, int use_sha384) { - u8 hash[32]; + u8 hash[48]; const u8 *addr[4]; size_t len[4]; /* - * PMKR1Name = Truncate-128(SHA-256("FT-R1N" || PMKR0Name || - * R1KH-ID || S1KH-ID)) + * PMKR1Name = Truncate-128(Hash("FT-R1N" || PMKR0Name || + * R1KH-ID || S1KH-ID)) */ addr[0] = (const u8 *) "FT-R1N"; len[0] = 6; @@ -924,8 +1592,14 @@ void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, addr[3] = s1kh_id; len[3] = ETH_ALEN; - sha256_vector(4, addr, len, hash); +#ifdef CONFIG_SHA384 + if (use_sha384 && sha384_vector(4, addr, len, hash) < 0) + return -1; +#endif /* CONFIG_SHA384 */ + if (!use_sha384 && sha256_vector(4, addr, len, hash) < 0) + return -1; os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN); + return 0; } @@ -934,23 +1608,46 @@ void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, * * IEEE Std 802.11r-2008 - 8.5.1.5.4 */ -void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name, - const u8 *r1kh_id, const u8 *s1kh_id, - u8 *pmk_r1, u8 *pmk_r1_name) +int wpa_derive_pmk_r1(const u8 *pmk_r0, size_t pmk_r0_len, + const u8 *pmk_r0_name, + const u8 *r1kh_id, const u8 *s1kh_id, + u8 *pmk_r1, u8 *pmk_r1_name) { u8 buf[FT_R1KH_ID_LEN + ETH_ALEN]; u8 *pos; /* PMK-R1 = KDF-256(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */ + wpa_printf(MSG_DEBUG, "FT: Derive PMK-R1 using KDF-%s", + pmk_r0_len == SHA384_MAC_LEN ? "SHA384" : "SHA256"); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, pmk_r0_len); + wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", r1kh_id, FT_R1KH_ID_LEN); + wpa_printf(MSG_DEBUG, "FT: S1KH-ID: " MACSTR, MAC2STR(s1kh_id)); pos = buf; os_memcpy(pos, r1kh_id, FT_R1KH_ID_LEN); pos += FT_R1KH_ID_LEN; os_memcpy(pos, s1kh_id, ETH_ALEN); pos += ETH_ALEN; - sha256_prf(pmk_r0, PMK_LEN, "FT-R1", buf, pos - buf, pmk_r1, PMK_LEN); +#ifdef CONFIG_SHA384 + if (pmk_r0_len == SHA384_MAC_LEN && + sha384_prf(pmk_r0, pmk_r0_len, "FT-R1", + buf, pos - buf, pmk_r1, pmk_r0_len) < 0) + return -1; +#endif /* CONFIG_SHA384 */ + if (pmk_r0_len == PMK_LEN && + sha256_prf(pmk_r0, pmk_r0_len, "FT-R1", + buf, pos - buf, pmk_r1, pmk_r0_len) < 0) + return -1; + if (pmk_r0_len != SHA384_MAC_LEN && pmk_r0_len != PMK_LEN) { + wpa_printf(MSG_ERROR, "FT: Unexpected PMK-R0 length %d", + (int) pmk_r0_len); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r0_len); - wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id, pmk_r1_name); + return wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id, + pmk_r1_name, + pmk_r0_len == SHA384_MAC_LEN); } @@ -959,7 +1656,8 @@ void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name, * * IEEE Std 802.11r-2008 - 8.5.1.5.5 */ -int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, +int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, + const u8 *snonce, const u8 *anonce, const u8 *sta_addr, const u8 *bssid, const u8 *pmk_r1_name, struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher) @@ -968,13 +1666,21 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, u8 *pos, hash[32]; const u8 *addr[6]; size_t len[6]; - u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN]; - size_t ptk_len; + u8 tmp[2 * WPA_KCK_MAX_LEN + 2 * WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN]; + size_t ptk_len, offset; + int use_sha384 = wpa_key_mgmt_sha384(akmp); /* * PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce || * BSSID || STA-ADDR) */ + wpa_printf(MSG_DEBUG, "FT: Derive PTK using KDF-%s", + use_sha384 ? "SHA384" : "SHA256"); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, pmk_r1_len); + wpa_hexdump(MSG_DEBUG, "FT: SNonce", snonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: ANonce", anonce, WPA_NONCE_LEN); + wpa_printf(MSG_DEBUG, "FT: BSSID=" MACSTR " STA-ADDR=" MACSTR, + MAC2STR(bssid), MAC2STR(sta_addr)); pos = buf; os_memcpy(pos, snonce, WPA_NONCE_LEN); pos += WPA_NONCE_LEN; @@ -985,17 +1691,45 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, os_memcpy(pos, sta_addr, ETH_ALEN); pos += ETH_ALEN; - ptk->kck_len = wpa_kck_len(akmp); - ptk->kek_len = wpa_kek_len(akmp); + ptk->kck_len = wpa_kck_len(akmp, PMK_LEN); + ptk->kck2_len = wpa_kck2_len(akmp); + ptk->kek_len = wpa_kek_len(akmp, PMK_LEN); + ptk->kek2_len = wpa_kek2_len(akmp); ptk->tk_len = wpa_cipher_key_len(cipher); - ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len; - - sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, tmp, ptk_len); + ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len + + ptk->kck2_len + ptk->kek2_len; + +#ifdef CONFIG_SHA384 + if (use_sha384) { + if (pmk_r1_len != SHA384_MAC_LEN) { + wpa_printf(MSG_ERROR, + "FT: Unexpected PMK-R1 length %d (expected %d)", + (int) pmk_r1_len, SHA384_MAC_LEN); + return -1; + } + if (sha384_prf(pmk_r1, pmk_r1_len, "FT-PTK", + buf, pos - buf, tmp, ptk_len) < 0) + return -1; + } +#endif /* CONFIG_SHA384 */ + if (!use_sha384) { + if (pmk_r1_len != PMK_LEN) { + wpa_printf(MSG_ERROR, + "FT: Unexpected PMK-R1 length %d (expected %d)", + (int) pmk_r1_len, PMK_LEN); + return -1; + } + if (sha256_prf(pmk_r1, pmk_r1_len, "FT-PTK", + buf, pos - buf, tmp, ptk_len) < 0) + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "FT: PTK", tmp, ptk_len); /* * PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce || * ANonce || BSSID || STA-ADDR)) */ + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); addr[0] = pmk_r1_name; len[0] = WPA_PMK_NAME_LEN; addr[1] = (const u8 *) "FT-PTKN"; @@ -1009,15 +1743,28 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, addr[5] = sta_addr; len[5] = ETH_ALEN; - sha256_vector(6, addr, len, hash); + if (sha256_vector(6, addr, len, hash) < 0) + return -1; os_memcpy(ptk_name, hash, WPA_PMK_NAME_LEN); os_memcpy(ptk->kck, tmp, ptk->kck_len); - os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len); - os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len); + offset = ptk->kck_len; + os_memcpy(ptk->kek, tmp + offset, ptk->kek_len); + offset += ptk->kek_len; + os_memcpy(ptk->tk, tmp + offset, ptk->tk_len); + offset += ptk->tk_len; + os_memcpy(ptk->kck2, tmp + offset, ptk->kck2_len); + offset = ptk->kck2_len; + os_memcpy(ptk->kek2, tmp + offset, ptk->kek2_len); wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len); wpa_hexdump_key(MSG_DEBUG, "FT: KEK", ptk->kek, ptk->kek_len); + if (ptk->kck2_len) + wpa_hexdump_key(MSG_DEBUG, "FT: KCK2", + ptk->kck2, ptk->kck2_len); + if (ptk->kek2_len) + wpa_hexdump_key(MSG_DEBUG, "FT: KEK2", + ptk->kek2, ptk->kek2_len); wpa_hexdump_key(MSG_DEBUG, "FT: TK", ptk->tk, ptk->tk_len); wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); @@ -1036,29 +1783,48 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, * @aa: Authenticator address * @spa: Supplicant address * @pmkid: Buffer for PMKID - * @use_sha256: Whether to use SHA256-based KDF + * @akmp: Negotiated key management protocol * - * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy - * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA) + * IEEE Std 802.11-2016 - 12.7.1.3 Pairwise key hierarchy + * AKM: 00-0F-AC:5, 00-0F-AC:6, 00-0F-AC:14, 00-0F-AC:16 + * PMKID = Truncate-128(HMAC-SHA-256(PMK, "PMK Name" || AA || SPA)) + * AKM: 00-0F-AC:11 + * See rsn_pmkid_suite_b() + * AKM: 00-0F-AC:12 + * See rsn_pmkid_suite_b_192() + * AKM: 00-0F-AC:13, 00-0F-AC:15, 00-0F-AC:17 + * PMKID = Truncate-128(HMAC-SHA-384(PMK, "PMK Name" || AA || SPA)) + * Otherwise: + * PMKID = Truncate-128(HMAC-SHA-1(PMK, "PMK Name" || AA || SPA)) */ void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, - u8 *pmkid, int use_sha256) + u8 *pmkid, int akmp) { char *title = "PMK Name"; const u8 *addr[3]; const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; - unsigned char hash[SHA256_MAC_LEN]; + unsigned char hash[SHA384_MAC_LEN]; addr[0] = (u8 *) title; addr[1] = aa; addr[2] = spa; -#ifdef CONFIG_IEEE80211W - if (use_sha256) + if (0) { +#if defined(CONFIG_FILS) || defined(CONFIG_SHA384) + } else if (wpa_key_mgmt_sha384(akmp)) { + wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-384"); + hmac_sha384_vector(pmk, pmk_len, 3, addr, len, hash); +#endif /* CONFIG_FILS || CONFIG_SHA384 */ +#if defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) + } else if (wpa_key_mgmt_sha256(akmp)) { + wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-256"); hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash); - else -#endif /* CONFIG_IEEE80211W */ +#endif /* CONFIG_IEEE80211W || CONFIG_FILS */ + } else { + wpa_printf(MSG_DEBUG, "RSN: Derive PMKID using HMAC-SHA-1"); hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash); + } + wpa_hexdump(MSG_DEBUG, "RSN: Derived PMKID", hash, PMKID_LEN); os_memcpy(pmkid, hash, PMKID_LEN); } @@ -1155,6 +1921,14 @@ const char * wpa_cipher_txt(int cipher) return "GCMP-256"; case WPA_CIPHER_CCMP_256: return "CCMP-256"; + case WPA_CIPHER_AES_128_CMAC: + return "BIP"; + case WPA_CIPHER_BIP_GMAC_128: + return "BIP-GMAC-128"; + case WPA_CIPHER_BIP_GMAC_256: + return "BIP-GMAC-256"; + case WPA_CIPHER_BIP_CMAC_256: + return "BIP-CMAC-256"; case WPA_CIPHER_GTK_NOT_USED: return "GTK_NOT_USED"; default: @@ -1191,6 +1965,8 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto) #ifdef CONFIG_IEEE80211R case WPA_KEY_MGMT_FT_IEEE8021X: return "FT-EAP"; + case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: + return "FT-EAP-SHA384"; case WPA_KEY_MGMT_FT_PSK: return "FT-PSK"; #endif /* CONFIG_IEEE80211R */ @@ -1212,6 +1988,18 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto) return "WPA2-EAP-SUITE-B"; case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: return "WPA2-EAP-SUITE-B-192"; + case WPA_KEY_MGMT_FILS_SHA256: + return "FILS-SHA256"; + case WPA_KEY_MGMT_FILS_SHA384: + return "FILS-SHA384"; + case WPA_KEY_MGMT_FT_FILS_SHA256: + return "FT-FILS-SHA256"; + case WPA_KEY_MGMT_FT_FILS_SHA384: + return "FT-FILS-SHA384"; + case WPA_KEY_MGMT_OWE: + return "OWE"; + case WPA_KEY_MGMT_DPP: + return "DPP"; default: return "UNKNOWN"; } @@ -1220,28 +2008,36 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto) u32 wpa_akm_to_suite(int akm) { + if (akm & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) + return RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384; if (akm & WPA_KEY_MGMT_FT_IEEE8021X) - return WLAN_AKM_SUITE_FT_8021X; + return RSN_AUTH_KEY_MGMT_FT_802_1X; if (akm & WPA_KEY_MGMT_FT_PSK) - return WLAN_AKM_SUITE_FT_PSK; - if (akm & WPA_KEY_MGMT_IEEE8021X) - return WLAN_AKM_SUITE_8021X; + return RSN_AUTH_KEY_MGMT_FT_PSK; if (akm & WPA_KEY_MGMT_IEEE8021X_SHA256) - return WLAN_AKM_SUITE_8021X_SHA256; + return RSN_AUTH_KEY_MGMT_802_1X_SHA256; if (akm & WPA_KEY_MGMT_IEEE8021X) - return WLAN_AKM_SUITE_8021X; + return RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; if (akm & WPA_KEY_MGMT_PSK_SHA256) - return WLAN_AKM_SUITE_PSK_SHA256; + return RSN_AUTH_KEY_MGMT_PSK_SHA256; if (akm & WPA_KEY_MGMT_PSK) - return WLAN_AKM_SUITE_PSK; + return RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; if (akm & WPA_KEY_MGMT_CCKM) - return WLAN_AKM_SUITE_CCKM; + return RSN_AUTH_KEY_MGMT_CCKM; if (akm & WPA_KEY_MGMT_OSEN) - return WLAN_AKM_SUITE_OSEN; + return RSN_AUTH_KEY_MGMT_OSEN; if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B) - return WLAN_AKM_SUITE_8021X_SUITE_B; + return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) - return WLAN_AKM_SUITE_8021X_SUITE_B_192; + return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; + if (akm & WPA_KEY_MGMT_FILS_SHA256) + return RSN_AUTH_KEY_MGMT_FILS_SHA256; + if (akm & WPA_KEY_MGMT_FILS_SHA384) + return RSN_AUTH_KEY_MGMT_FILS_SHA384; + if (akm & WPA_KEY_MGMT_FT_FILS_SHA256) + return RSN_AUTH_KEY_MGMT_FT_FILS_SHA256; + if (akm & WPA_KEY_MGMT_FT_FILS_SHA384) + return RSN_AUTH_KEY_MGMT_FT_FILS_SHA384; return 0; } @@ -1283,7 +2079,7 @@ int wpa_compare_rsn_ie(int ft_initial_assoc, } -#ifdef CONFIG_IEEE80211R +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS) int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid) { u8 *start, *end, *rpos, *rend; @@ -1382,7 +2178,7 @@ int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid) return 0; } -#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211R || CONFIG_FILS */ int wpa_cipher_key_len(int cipher) @@ -1421,7 +2217,7 @@ int wpa_cipher_rsc_len(int cipher) } -int wpa_cipher_to_alg(int cipher) +enum wpa_alg wpa_cipher_to_alg(int cipher) { switch (cipher) { case WPA_CIPHER_CCMP_256: @@ -1616,6 +2412,14 @@ int wpa_parse_cipher(const char *value) val |= WPA_CIPHER_NONE; else if (os_strcmp(start, "GTK_NOT_USED") == 0) val |= WPA_CIPHER_GTK_NOT_USED; + else if (os_strcmp(start, "AES-128-CMAC") == 0) + val |= WPA_CIPHER_AES_128_CMAC; + else if (os_strcmp(start, "BIP-GMAC-128") == 0) + val |= WPA_CIPHER_BIP_GMAC_128; + else if (os_strcmp(start, "BIP-GMAC-256") == 0) + val |= WPA_CIPHER_BIP_GMAC_256; + else if (os_strcmp(start, "BIP-CMAC-256") == 0) + val |= WPA_CIPHER_BIP_CMAC_256; else { os_free(buf); return -1; @@ -1671,6 +2475,34 @@ int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim) return -1; pos += ret; } + if (ciphers & WPA_CIPHER_AES_128_CMAC) { + ret = os_snprintf(pos, end - pos, "%sAES-128-CMAC", + pos == start ? "" : delim); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_BIP_GMAC_128) { + ret = os_snprintf(pos, end - pos, "%sBIP-GMAC-128", + pos == start ? "" : delim); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_BIP_GMAC_256) { + ret = os_snprintf(pos, end - pos, "%sBIP-GMAC-256", + pos == start ? "" : delim); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + if (ciphers & WPA_CIPHER_BIP_CMAC_256) { + ret = os_snprintf(pos, end - pos, "%sBIP-CMAC-256", + pos == start ? "" : delim); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } if (ciphers & WPA_CIPHER_NONE) { ret = os_snprintf(pos, end - pos, "%sNONE", pos == start ? "" : delim); @@ -1705,3 +2537,29 @@ int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise) return WPA_CIPHER_CCMP_256; return WPA_CIPHER_CCMP; } + + +#ifdef CONFIG_FILS +int fils_domain_name_hash(const char *domain, u8 *hash) +{ + char buf[255], *wpos = buf; + const char *pos = domain; + size_t len; + const u8 *addr[1]; + u8 mac[SHA256_MAC_LEN]; + + for (len = 0; len < sizeof(buf) && *pos; len++) { + if (isalpha(*pos) && isupper(*pos)) + *wpos++ = tolower(*pos); + else + *wpos++ = *pos; + pos++; + } + + addr[0] = (const u8 *) buf; + if (sha256_vector(1, addr, &len, mac) < 0) + return -1; + os_memcpy(hash, mac, 2); + return 0; +} +#endif /* CONFIG_FILS */ diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 1021ccb05a717..62617444058b4 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -1,6 +1,6 @@ /* * WPA definitions shared between hostapd and wpa_supplicant - * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2018, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -13,13 +13,15 @@ #define PMKID_LEN 16 #define PMK_LEN 32 #define PMK_LEN_SUITE_B_192 48 -#define PMK_LEN_MAX 48 +#define PMK_LEN_MAX 64 #define WPA_REPLAY_COUNTER_LEN 8 #define WPA_NONCE_LEN 32 #define WPA_KEY_RSC_LEN 8 #define WPA_GMK_LEN 32 #define WPA_GTK_MAX_LEN 32 +#define OWE_DH_GROUP 19 + #define WPA_ALLOWED_PAIRWISE_CIPHERS \ (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \ WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256) @@ -27,6 +29,9 @@ WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256) (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | \ WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \ WPA_CIPHER_GTK_NOT_USED) +#define WPA_ALLOWED_GROUP_MGMT_CIPHERS \ +(WPA_CIPHER_AES_128_CMAC | WPA_CIPHER_BIP_GMAC_128 | WPA_CIPHER_BIP_GMAC_256 | \ +WPA_CIPHER_BIP_CMAC_256) #define WPA_SELECTOR_LEN 4 #define WPA_VERSION 1 @@ -48,10 +53,8 @@ WPA_CIPHER_GTK_NOT_USED) #define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1) #define RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 2) -#ifdef CONFIG_IEEE80211R #define RSN_AUTH_KEY_MGMT_FT_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 3) #define RSN_AUTH_KEY_MGMT_FT_PSK RSN_SELECTOR(0x00, 0x0f, 0xac, 4) -#endif /* CONFIG_IEEE80211R */ #define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) #define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #define RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE RSN_SELECTOR(0x00, 0x0f, 0xac, 7) @@ -59,17 +62,24 @@ WPA_CIPHER_GTK_NOT_USED) #define RSN_AUTH_KEY_MGMT_FT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 9) #define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B RSN_SELECTOR(0x00, 0x0f, 0xac, 11) #define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192 RSN_SELECTOR(0x00, 0x0f, 0xac, 12) -#define RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_192 \ -RSN_SELECTOR(0x00, 0x0f, 0xac, 13) +#define RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 13) +#define RSN_AUTH_KEY_MGMT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 14) +#define RSN_AUTH_KEY_MGMT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 15) +#define RSN_AUTH_KEY_MGMT_FT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 16) +#define RSN_AUTH_KEY_MGMT_FT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 17) +#define RSN_AUTH_KEY_MGMT_OWE RSN_SELECTOR(0x00, 0x0f, 0xac, 18) #define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00) #define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01) +#define RSN_AUTH_KEY_MGMT_DPP RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x02) #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0) +#define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1) #define RSN_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x0f, 0xac, 2) #if 0 #define RSN_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x0f, 0xac, 3) #endif #define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4) +#define RSN_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x0f, 0xac, 5) #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6) #define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7) #define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8) @@ -78,6 +88,12 @@ RSN_SELECTOR(0x00, 0x0f, 0xac, 13) #define RSN_CIPHER_SUITE_BIP_GMAC_128 RSN_SELECTOR(0x00, 0x0f, 0xac, 11) #define RSN_CIPHER_SUITE_BIP_GMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 12) #define RSN_CIPHER_SUITE_BIP_CMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 13) +#define RSN_CIPHER_SUITE_SMS4 RSN_SELECTOR(0x00, 0x14, 0x72, 1) +#define RSN_CIPHER_SUITE_CKIP RSN_SELECTOR(0x00, 0x40, 0x96, 0) +#define RSN_CIPHER_SUITE_CKIP_CMIC RSN_SELECTOR(0x00, 0x40, 0x96, 1) +#define RSN_CIPHER_SUITE_CMIC RSN_SELECTOR(0x00, 0x40, 0x96, 2) +/* KRK is defined for nl80211 use only */ +#define RSN_CIPHER_SUITE_KRK RSN_SELECTOR(0x00, 0x40, 0x96, 255) /* EAPOL-Key Key Data Encapsulation * GroupKey and PeerKey require encryption, otherwise, encryption is optional. @@ -88,12 +104,6 @@ RSN_SELECTOR(0x00, 0x0f, 0xac, 13) #endif #define RSN_KEY_DATA_MAC_ADDR RSN_SELECTOR(0x00, 0x0f, 0xac, 3) #define RSN_KEY_DATA_PMKID RSN_SELECTOR(0x00, 0x0f, 0xac, 4) -#ifdef CONFIG_PEERKEY -#define RSN_KEY_DATA_SMK RSN_SELECTOR(0x00, 0x0f, 0xac, 5) -#define RSN_KEY_DATA_NONCE RSN_SELECTOR(0x00, 0x0f, 0xac, 6) -#define RSN_KEY_DATA_LIFETIME RSN_SELECTOR(0x00, 0x0f, 0xac, 7) -#define RSN_KEY_DATA_ERROR RSN_SELECTOR(0x00, 0x0f, 0xac, 8) -#endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211W #define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9) #endif /* CONFIG_IEEE80211W */ @@ -179,30 +189,17 @@ struct wpa_eapol_key { u8 key_iv[16]; u8 key_rsc[WPA_KEY_RSC_LEN]; u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */ - u8 key_mic[16]; - u8 key_data_length[2]; /* big endian */ - /* followed by key_data_length bytes of key_data */ -} STRUCT_PACKED; - -struct wpa_eapol_key_192 { - u8 type; - /* Note: key_info, key_length, and key_data_length are unaligned */ - u8 key_info[2]; /* big endian */ - u8 key_length[2]; /* big endian */ - u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; - u8 key_nonce[WPA_NONCE_LEN]; - u8 key_iv[16]; - u8 key_rsc[WPA_KEY_RSC_LEN]; - u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */ - u8 key_mic[24]; - u8 key_data_length[2]; /* big endian */ - /* followed by key_data_length bytes of key_data */ + /* variable length Key MIC field */ + /* big endian 2-octet Key Data Length field */ + /* followed by Key Data Length bytes of Key Data */ } STRUCT_PACKED; -#define WPA_EAPOL_KEY_MIC_MAX_LEN 24 -#define WPA_KCK_MAX_LEN 24 -#define WPA_KEK_MAX_LEN 32 +#define WPA_EAPOL_KEY_MIC_MAX_LEN 32 +#define WPA_KCK_MAX_LEN 32 +#define WPA_KEK_MAX_LEN 64 #define WPA_TK_MAX_LEN 32 +#define FILS_ICK_MAX_LEN 48 +#define FILS_FT_MAX_LEN 48 /** * struct wpa_ptk - WPA Pairwise Transient Key @@ -212,9 +209,13 @@ struct wpa_ptk { u8 kck[WPA_KCK_MAX_LEN]; /* EAPOL-Key Key Confirmation Key (KCK) */ u8 kek[WPA_KEK_MAX_LEN]; /* EAPOL-Key Key Encryption Key (KEK) */ u8 tk[WPA_TK_MAX_LEN]; /* Temporal Key (TK) */ + u8 kck2[WPA_KCK_MAX_LEN]; /* FT reasoc Key Confirmation Key (KCK2) */ + u8 kek2[WPA_KEK_MAX_LEN]; /* FT reassoc Key Encryption Key (KEK2) */ size_t kck_len; size_t kek_len; size_t tk_len; + size_t kck2_len; + size_t kek2_len; int installed; /* 1 if key has already been installed to driver */ }; @@ -283,22 +284,6 @@ struct rsn_ie_hdr { } STRUCT_PACKED; -#ifdef CONFIG_PEERKEY -enum { - STK_MUI_4WAY_STA_AP = 1, - STK_MUI_4WAY_STAT_STA = 2, - STK_MUI_GTK = 3, - STK_MUI_SMK = 4 -}; - -enum { - STK_ERR_STA_NR = 1, - STK_ERR_STA_NRSN = 2, - STK_ERR_CPHR_NS = 3, - STK_ERR_NO_STSL = 4 -}; -#endif /* CONFIG_PEERKEY */ - struct rsn_error_kde { be16 mui; be16 error_type; @@ -329,6 +314,14 @@ struct rsn_ftie { /* followed by optional parameters */ } STRUCT_PACKED; +struct rsn_ftie_sha384 { + u8 mic_control[2]; + u8 mic[24]; + u8 anonce[WPA_NONCE_LEN]; + u8 snonce[WPA_NONCE_LEN]; + /* followed by optional parameters */ +} STRUCT_PACKED; + #define FTIE_SUBELEM_R1KH_ID 1 #define FTIE_SUBELEM_GTK 2 #define FTIE_SUBELEM_R0KH_ID 3 @@ -352,6 +345,22 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label, const u8 *addr1, const u8 *addr2, const u8 *nonce1, const u8 *nonce2, struct wpa_ptk *ptk, int akmp, int cipher); +int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len, + const u8 *snonce, const u8 *anonce, const u8 *dh_ss, + size_t dh_ss_len, u8 *pmk, size_t *pmk_len); +int fils_pmkid_erp(int akmp, const u8 *reauth, size_t reauth_len, + u8 *pmkid); +int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa, + const u8 *snonce, const u8 *anonce, const u8 *dhss, + size_t dhss_len, struct wpa_ptk *ptk, + u8 *ick, size_t *ick_len, int akmp, int cipher, + u8 *fils_ft, size_t *fils_ft_len); +int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce, + const u8 *anonce, const u8 *sta_addr, const u8 *bssid, + const u8 *g_sta, size_t g_sta_len, + const u8 *g_ap, size_t g_ap_len, + int akmp, u8 *key_auth_sta, u8 *key_auth_ap, + size_t *key_auth_len); #ifdef CONFIG_IEEE80211R int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, @@ -360,17 +369,19 @@ int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr, const u8 *ftie, size_t ftie_len, const u8 *rsnie, size_t rsnie_len, const u8 *ric, size_t ric_len, u8 *mic); -void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, - const u8 *ssid, size_t ssid_len, - const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, - const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name); -void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, - const u8 *s1kh_id, u8 *pmk_r1_name); -void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name, - const u8 *r1kh_id, const u8 *s1kh_id, - u8 *pmk_r1, u8 *pmk_r1_name); -int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, - const u8 *sta_addr, const u8 *bssid, +int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len, + const u8 *ssid, size_t ssid_len, + const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len, + const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name, + int use_sha384); +int wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id, + const u8 *s1kh_id, u8 *pmk_r1_name, int use_sha384); +int wpa_derive_pmk_r1(const u8 *pmk_r0, size_t pmk_r0_len, + const u8 *pmk_r0_name, + const u8 *r1kh_id, const u8 *s1kh_id, + u8 *pmk_r1, u8 *pmk_r1_name); +int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, const u8 *snonce, + const u8 *anonce, const u8 *sta_addr, const u8 *bssid, const u8 *pmk_r1_name, struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher); #endif /* CONFIG_IEEE80211R */ @@ -393,7 +404,7 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ie_data *data); void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, - u8 *pmkid, int use_sha256); + u8 *pmkid, int akmp); #ifdef CONFIG_SUITEB int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa, const u8 *spa, u8 *pmkid); @@ -442,13 +453,16 @@ struct wpa_ft_ies { size_t igtk_len; const u8 *ric; size_t ric_len; + int key_mgmt; + int pairwise_cipher; }; -int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse); +int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse, + int use_sha384); int wpa_cipher_key_len(int cipher); int wpa_cipher_rsc_len(int cipher); -int wpa_cipher_to_alg(int cipher); +enum wpa_alg wpa_cipher_to_alg(int cipher); int wpa_cipher_valid_group(int cipher); int wpa_cipher_valid_pairwise(int cipher); int wpa_cipher_valid_mgmt_group(int cipher); @@ -460,6 +474,10 @@ int wpa_pick_group_cipher(int ciphers); int wpa_parse_cipher(const char *value); int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim); int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise); -unsigned int wpa_mic_len(int akmp); +unsigned int wpa_mic_len(int akmp, size_t pmk_len); +int wpa_use_akm_defined(int akmp); +int wpa_use_cmac(int akmp); +int wpa_use_aes_key_wrap(int akmp); +int fils_domain_name_hash(const char *domain, u8 *hash); #endif /* WPA_COMMON_H */ diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 4dcba81dc1a44..f65077e04282e 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -1,6 +1,6 @@ /* * wpa_supplicant/hostapd control interface library - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -50,10 +50,19 @@ extern "C" { #define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR " /** EAP status */ #define WPA_EVENT_EAP_STATUS "CTRL-EVENT-EAP-STATUS " +/** Retransmit the previous request packet */ +#define WPA_EVENT_EAP_RETRANSMIT "CTRL-EVENT-EAP-RETRANSMIT " +#define WPA_EVENT_EAP_RETRANSMIT2 "CTRL-EVENT-EAP-RETRANSMIT2 " /** EAP authentication completed successfully */ #define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS " +#define WPA_EVENT_EAP_SUCCESS2 "CTRL-EVENT-EAP-SUCCESS2 " /** EAP authentication failed (EAP-Failure received) */ #define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE " +#define WPA_EVENT_EAP_FAILURE2 "CTRL-EVENT-EAP-FAILURE2 " +/** EAP authentication failed due to no response received */ +#define WPA_EVENT_EAP_TIMEOUT_FAILURE "CTRL-EVENT-EAP-TIMEOUT-FAILURE " +#define WPA_EVENT_EAP_TIMEOUT_FAILURE2 "CTRL-EVENT-EAP-TIMEOUT-FAILURE2 " +#define WPA_EVENT_EAP_ERROR_CODE "EAP-ERROR-CODE " /** Network block temporarily disabled (e.g., due to authentication failure) */ #define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED " /** Temporarily disabled network block re-enabled */ @@ -74,10 +83,15 @@ extern "C" { #define WPA_EVENT_NETWORK_NOT_FOUND "CTRL-EVENT-NETWORK-NOT-FOUND " /** Change in the signal level was reported by the driver */ #define WPA_EVENT_SIGNAL_CHANGE "CTRL-EVENT-SIGNAL-CHANGE " +/** Beacon loss reported by the driver */ +#define WPA_EVENT_BEACON_LOSS "CTRL-EVENT-BEACON-LOSS " /** Regulatory domain channel */ #define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE " /** Channel switch (followed by freq=<MHz> and other channel parameters) */ #define WPA_EVENT_CHANNEL_SWITCH "CTRL-EVENT-CHANNEL-SWITCH " +/** SAE authentication failed due to unknown password identifier */ +#define WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER \ + "CTRL-EVENT-SAE-UNKNOWN-PASSWORD-IDENTIFIER " /** IP subnet status change notification * @@ -141,6 +155,33 @@ extern "C" { #define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS " #define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG " +/* DPP events */ +#define DPP_EVENT_AUTH_SUCCESS "DPP-AUTH-SUCCESS " +#define DPP_EVENT_AUTH_INIT_FAILED "DPP-AUTH-INIT-FAILED " +#define DPP_EVENT_NOT_COMPATIBLE "DPP-NOT-COMPATIBLE " +#define DPP_EVENT_RESPONSE_PENDING "DPP-RESPONSE-PENDING " +#define DPP_EVENT_SCAN_PEER_QR_CODE "DPP-SCAN-PEER-QR-CODE " +#define DPP_EVENT_AUTH_DIRECTION "DPP-AUTH-DIRECTION " +#define DPP_EVENT_CONF_RECEIVED "DPP-CONF-RECEIVED " +#define DPP_EVENT_CONF_SENT "DPP-CONF-SENT " +#define DPP_EVENT_CONF_FAILED "DPP-CONF-FAILED " +#define DPP_EVENT_CONFOBJ_AKM "DPP-CONFOBJ-AKM " +#define DPP_EVENT_CONFOBJ_SSID "DPP-CONFOBJ-SSID " +#define DPP_EVENT_CONFOBJ_PASS "DPP-CONFOBJ-PASS " +#define DPP_EVENT_CONFOBJ_PSK "DPP-CONFOBJ-PSK " +#define DPP_EVENT_CONNECTOR "DPP-CONNECTOR " +#define DPP_EVENT_C_SIGN_KEY "DPP-C-SIGN-KEY " +#define DPP_EVENT_NET_ACCESS_KEY "DPP-NET-ACCESS-KEY " +#define DPP_EVENT_MISSING_CONNECTOR "DPP-MISSING-CONNECTOR " +#define DPP_EVENT_NETWORK_ID "DPP-NETWORK-ID " +#define DPP_EVENT_RX "DPP-RX " +#define DPP_EVENT_TX "DPP-TX " +#define DPP_EVENT_TX_STATUS "DPP-TX-STATUS " +#define DPP_EVENT_FAIL "DPP-FAIL " +#define DPP_EVENT_PKEX_T_LIMIT "DPP-PKEX-T-LIMIT " +#define DPP_EVENT_INTRO "DPP-INTRO " +#define DPP_EVENT_CONF_REQ_RX "DPP-CONF-REQ-RX " + /* MESH events */ #define MESH_GROUP_STARTED "MESH-GROUP-STARTED " #define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED " @@ -232,9 +273,14 @@ extern "C" { #define RX_HS20_ANQP "RX-HS20-ANQP " #define RX_HS20_ANQP_ICON "RX-HS20-ANQP-ICON " #define RX_HS20_ICON "RX-HS20-ICON " +#define RX_MBO_ANQP "RX-MBO-ANQP " + +/* parameters: <Venue Number> <Venue URL> */ +#define RX_VENUE_URL "RX-VENUE-URL " #define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION " #define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE " +#define HS20_T_C_ACCEPTANCE "HS20-T-C-ACCEPTANCE " #define EXT_RADIO_WORK_START "EXT-RADIO-WORK-START " #define EXT_RADIO_WORK_TIMEOUT "EXT-RADIO-WORK-TIMEOUT " @@ -258,6 +304,9 @@ extern "C" { #define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA " #define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA " +#define HS20_T_C_FILTERING_ADD "HS20-T-C-FILTERING-ADD " +#define HS20_T_C_FILTERING_REMOVE "HS20-T-C-FILTERING-REMOVE " + #define AP_EVENT_ENABLED "AP-ENABLED " #define AP_EVENT_DISABLED "AP-DISABLED " @@ -273,6 +322,7 @@ extern "C" { #define DFS_EVENT_CAC_START "DFS-CAC-START " #define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED " #define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED " +#define DFS_EVENT_PRE_CAC_EXPIRED "DFS-PRE-CAC-EXPIRED " #define AP_CSA_FINISHED "AP-CSA-FINISHED " @@ -282,12 +332,46 @@ extern "C" { /* BSS Transition Management Response frame received */ #define BSS_TM_RESP "BSS-TM-RESP " +/* Collocated Interference Request frame received; + * parameters: <dialog token> <automatic report enabled> <report timeout> */ +#define COLOC_INTF_REQ "COLOC-INTF-REQ " +/* Collocated Interference Report frame received; + * parameters: <STA address> <dialog token> <hexdump of report elements> */ +#define COLOC_INTF_REPORT "COLOC-INTF-REPORT " + /* MBO IE with cellular data connection preference received */ #define MBO_CELL_PREFERENCE "MBO-CELL-PREFERENCE " /* BSS Transition Management Request received with MBO transition reason */ #define MBO_TRANSITION_REASON "MBO-TRANSITION-REASON " +/* parameters: <STA address> <dialog token> <ack=0/1> */ +#define BEACON_REQ_TX_STATUS "BEACON-REQ-TX-STATUS " +/* parameters: <STA address> <dialog token> <report mode> <beacon report> */ +#define BEACON_RESP_RX "BEACON-RESP-RX " + +/* PMKSA cache entry added; parameters: <BSSID> <network_id> */ +#define PMKSA_CACHE_ADDED "PMKSA-CACHE-ADDED " +/* PMKSA cache entry removed; parameters: <BSSID> <network_id> */ +#define PMKSA_CACHE_REMOVED "PMKSA-CACHE-REMOVED " + +/* FILS HLP Container receive; parameters: dst=<addr> src=<addr> frame=<hexdump> + */ +#define FILS_HLP_RX "FILS-HLP-RX " + +/* Event to indicate Probe Request frame; + * parameters: sa=<STA MAC address> signal=<signal> */ +#define RX_PROBE_REQUEST "RX-PROBE-REQUEST " + +/* Event to indicate station's HT/VHT operation mode change information */ +#define STA_OPMODE_MAX_BW_CHANGED "STA-OPMODE-MAX-BW-CHANGED " +#define STA_OPMODE_SMPS_MODE_CHANGED "STA-OPMODE-SMPS-MODE-CHANGED " +#define STA_OPMODE_N_SS_CHANGED "STA-OPMODE-N_SS-CHANGED " + +/* New interface addition or removal for 4addr WDS SDA */ +#define WDS_STA_INTERFACE_ADDED "WDS-STA-INTERFACE-ADDED " +#define WDS_STA_INTERFACE_REMOVED "WDS-STA-INTERFACE-REMOVED " + /* BSS command information masks */ #define WPA_BSS_MASK_ALL 0xFFFDFFFF @@ -313,6 +397,9 @@ extern "C" { #define WPA_BSS_MASK_SNR BIT(19) #define WPA_BSS_MASK_EST_THROUGHPUT BIT(20) #define WPA_BSS_MASK_FST BIT(21) +#define WPA_BSS_MASK_UPDATE_IDX BIT(22) +#define WPA_BSS_MASK_BEACON_IE BIT(23) +#define WPA_BSS_MASK_FILS_INDICATION BIT(24) /* VENDOR_ELEM_* frame id values */ @@ -386,7 +473,7 @@ void wpa_ctrl_close(struct wpa_ctrl *ctrl); * * This function is used to send commands to wpa_supplicant/hostapd. Received * response will be written to reply and reply_len is set to the actual length - * of the reply. This function will block for up to two seconds while waiting + * of the reply. This function will block for up to 10 seconds while waiting * for the reply. If unsolicited messages are received, the blocking time may * be longer. * diff --git a/src/common/wpa_helpers.c b/src/common/wpa_helpers.c index f1594213f97f5..8e1c09ec5929e 100644 --- a/src/common/wpa_helpers.c +++ b/src/common/wpa_helpers.c @@ -222,7 +222,8 @@ int wait_ip_addr(const char *ifname, int timeout) if (get_wpa_status(ifname, "ip_address", ip, sizeof(ip)) == 0 && strlen(ip) > 0) { printf("IP address found: '%s'\n", ip); - return 0; + if (strncmp(ip, "169.254.", 8) != 0) + return 0; } ctrl = wpa_open_ctrl(ifname); if (ctrl == NULL) diff --git a/src/crypto/Makefile b/src/crypto/Makefile index d181e723134eb..ee93e41fbbb17 100644 --- a/src/crypto/Makefile +++ b/src/crypto/Makefile @@ -14,6 +14,9 @@ CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER #CFLAGS += -DALL_DH_GROUPS CFLAGS += -DCONFIG_SHA256 +CFLAGS += -DCONFIG_SHA384 +CFLAGS += -DCONFIG_HMAC_SHA384_KDF +CFLAGS += -DCONFIG_INTERNAL_SHA384 LIB_OBJS= \ aes-cbc.o \ @@ -48,6 +51,8 @@ LIB_OBJS= \ sha256-prf.o \ sha256-tlsprf.o \ sha256-internal.o \ + sha384.o \ + sha384-prf.o \ sha384-internal.o \ sha512-internal.o diff --git a/src/crypto/aes-ctr.c b/src/crypto/aes-ctr.c index d4d874daacd01..e27f3bbd07064 100644 --- a/src/crypto/aes-ctr.c +++ b/src/crypto/aes-ctr.c @@ -1,5 +1,5 @@ /* - * AES-128 CTR + * AES-128/192/256 CTR * * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> * @@ -14,15 +14,16 @@ #include "aes_wrap.h" /** - * aes_128_ctr_encrypt - AES-128 CTR mode encryption - * @key: Key for encryption (16 bytes) + * aes_ctr_encrypt - AES-128/192/256 CTR mode encryption + * @key: Key for encryption (key_len bytes) + * @key_len: Length of the key (16, 24, or 32 bytes) * @nonce: Nonce for counter mode (16 bytes) * @data: Data to encrypt in-place * @data_len: Length of data in bytes * Returns: 0 on success, -1 on failure */ -int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, - u8 *data, size_t data_len) +int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce, + u8 *data, size_t data_len) { void *ctx; size_t j, len, left = data_len; @@ -30,7 +31,7 @@ int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, u8 *pos = data; u8 counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE]; - ctx = aes_encrypt_init(key, 16); + ctx = aes_encrypt_init(key, key_len); if (ctx == NULL) return -1; os_memcpy(counter, nonce, AES_BLOCK_SIZE); @@ -53,3 +54,18 @@ int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, aes_encrypt_deinit(ctx); return 0; } + + +/** + * aes_128_ctr_encrypt - AES-128 CTR mode encryption + * @key: Key for encryption (key_len bytes) + * @nonce: Nonce for counter mode (16 bytes) + * @data: Data to encrypt in-place + * @data_len: Length of data in bytes + * Returns: 0 on success, -1 on failure + */ +int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, + u8 *data, size_t data_len) +{ + return aes_ctr_encrypt(key, 16, nonce, data, data_len); +} diff --git a/src/crypto/aes-internal-dec.c b/src/crypto/aes-internal-dec.c index 720c7036e4e7b..7482295949046 100644 --- a/src/crypto/aes-internal-dec.c +++ b/src/crypto/aes-internal-dec.c @@ -147,10 +147,12 @@ d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3] PUTU32(pt + 12, s3); } -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) + +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { u32 *rk = ctx; rijndaelDecrypt(ctx, rk[AES_PRIV_NR_POS], crypt, plain); + return 0; } diff --git a/src/crypto/aes-internal-enc.c b/src/crypto/aes-internal-enc.c index f3c61b8508f3c..9fdb4f35cb9b7 100644 --- a/src/crypto/aes-internal-enc.c +++ b/src/crypto/aes-internal-enc.c @@ -112,10 +112,11 @@ void * aes_encrypt_init(const u8 *key, size_t len) } -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { u32 *rk = ctx; rijndaelEncrypt(ctx, rk[AES_PRIV_NR_POS], plain, crypt); + return 0; } diff --git a/src/crypto/aes-siv.c b/src/crypto/aes-siv.c index 5ac82c2e4b5c0..b682f3ad565df 100644 --- a/src/crypto/aes-siv.c +++ b/src/crypto/aes-siv.c @@ -61,26 +61,33 @@ static void pad_block(u8 *pad, const u8 *addr, size_t len) } -static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[], - size_t *len, u8 *mac) +static int aes_s2v(const u8 *key, size_t key_len, + size_t num_elem, const u8 *addr[], size_t *len, u8 *mac) { u8 tmp[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE]; u8 *buf = NULL; int ret; size_t i; + const u8 *data[1]; + size_t data_len[1]; if (!num_elem) { os_memcpy(tmp, zero, sizeof(zero)); tmp[AES_BLOCK_SIZE - 1] = 1; - return omac1_aes_128(key, tmp, sizeof(tmp), mac); + data[0] = tmp; + data_len[0] = sizeof(tmp); + return omac1_aes_vector(key, key_len, 1, data, data_len, mac); } - ret = omac1_aes_128(key, zero, sizeof(zero), tmp); + data[0] = zero; + data_len[0] = sizeof(zero); + ret = omac1_aes_vector(key, key_len, 1, data, data_len, tmp); if (ret) return ret; for (i = 0; i < num_elem - 1; i++) { - ret = omac1_aes_128(key, addr[i], len[i], tmp2); + ret = omac1_aes_vector(key, key_len, 1, &addr[i], &len[i], + tmp2); if (ret) return ret; @@ -88,13 +95,13 @@ static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[], xor(tmp, tmp2); } if (len[i] >= AES_BLOCK_SIZE) { - buf = os_malloc(len[i]); + buf = os_memdup(addr[i], len[i]); if (!buf) return -ENOMEM; - os_memcpy(buf, addr[i], len[i]); xorend(buf, len[i], tmp, AES_BLOCK_SIZE); - ret = omac1_aes_128(key, buf, len[i], mac); + data[0] = buf; + ret = omac1_aes_vector(key, key_len, 1, data, &len[i], mac); bin_clear_free(buf, len[i]); return ret; } @@ -103,24 +110,32 @@ static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[], pad_block(tmp2, addr[i], len[i]); xor(tmp, tmp2); - return omac1_aes_128(key, tmp, sizeof(tmp), mac); + data[0] = tmp; + data_len[0] = sizeof(tmp); + return omac1_aes_vector(key, key_len, 1, data, data_len, mac); } -int aes_siv_encrypt(const u8 *key, const u8 *pw, - size_t pwlen, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *out) +int aes_siv_encrypt(const u8 *key, size_t key_len, + const u8 *pw, size_t pwlen, + size_t num_elem, const u8 *addr[], const size_t *len, + u8 *out) { const u8 *_addr[6]; size_t _len[6]; - const u8 *k1 = key, *k2 = key + 16; + const u8 *k1, *k2; u8 v[AES_BLOCK_SIZE]; size_t i; u8 *iv, *crypt_pw; - if (num_elem > ARRAY_SIZE(_addr) - 1) + if (num_elem > ARRAY_SIZE(_addr) - 1 || + (key_len != 32 && key_len != 48 && key_len != 64)) return -1; + key_len /= 2; + k1 = key; + k2 = key + key_len; + for (i = 0; i < num_elem; i++) { _addr[i] = addr[i]; _len[i] = len[i]; @@ -128,7 +143,7 @@ int aes_siv_encrypt(const u8 *key, const u8 *pw, _addr[num_elem] = pw; _len[num_elem] = pwlen; - if (aes_s2v(k1, num_elem + 1, _addr, _len, v)) + if (aes_s2v(k1, key_len, num_elem + 1, _addr, _len, v)) return -1; iv = out; @@ -140,26 +155,31 @@ int aes_siv_encrypt(const u8 *key, const u8 *pw, /* zero out 63rd and 31st bits of ctr (from right) */ v[8] &= 0x7f; v[12] &= 0x7f; - return aes_128_ctr_encrypt(k2, v, crypt_pw, pwlen); + return aes_ctr_encrypt(k2, key_len, v, crypt_pw, pwlen); } -int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len, +int aes_siv_decrypt(const u8 *key, size_t key_len, + const u8 *iv_crypt, size_t iv_c_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *out) { const u8 *_addr[6]; size_t _len[6]; - const u8 *k1 = key, *k2 = key + 16; + const u8 *k1, *k2; size_t crypt_len; size_t i; int ret; u8 iv[AES_BLOCK_SIZE]; u8 check[AES_BLOCK_SIZE]; - if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1) + if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1 || + (key_len != 32 && key_len != 48 && key_len != 64)) return -1; crypt_len = iv_c_len - AES_BLOCK_SIZE; + key_len /= 2; + k1 = key; + k2 = key + key_len; for (i = 0; i < num_elem; i++) { _addr[i] = addr[i]; @@ -174,11 +194,11 @@ int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len, iv[8] &= 0x7f; iv[12] &= 0x7f; - ret = aes_128_ctr_encrypt(k2, iv, out, crypt_len); + ret = aes_ctr_encrypt(k2, key_len, iv, out, crypt_len); if (ret) return ret; - ret = aes_s2v(k1, num_elem + 1, _addr, _len, check); + ret = aes_s2v(k1, key_len, num_elem + 1, _addr, _len, check); if (ret) return ret; if (os_memcmp(check, iv_crypt, AES_BLOCK_SIZE) == 0) diff --git a/src/crypto/aes.h b/src/crypto/aes.h index 2de59e04efa5f..8ab3de2ee83f3 100644 --- a/src/crypto/aes.h +++ b/src/crypto/aes.h @@ -12,10 +12,10 @@ #define AES_BLOCK_SIZE 16 void * aes_encrypt_init(const u8 *key, size_t len); -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); void aes_encrypt_deinit(void *ctx); void * aes_decrypt_init(const u8 *key, size_t len); -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); void aes_decrypt_deinit(void *ctx); #endif /* AES_H */ diff --git a/src/crypto/aes_siv.h b/src/crypto/aes_siv.h index 463cf6536107c..fb05d80c1f12f 100644 --- a/src/crypto/aes_siv.h +++ b/src/crypto/aes_siv.h @@ -9,10 +9,12 @@ #ifndef AES_SIV_H #define AES_SIV_H -int aes_siv_encrypt(const u8 *key, const u8 *pw, - size_t pwlen, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *out); -int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len, +int aes_siv_encrypt(const u8 *key, size_t key_len, + const u8 *pw, size_t pwlen, + size_t num_elem, const u8 *addr[], const size_t *len, + u8 *out); +int aes_siv_decrypt(const u8 *key, size_t key_len, + const u8 *iv_crypt, size_t iv_c_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *out); diff --git a/src/crypto/aes_wrap.h b/src/crypto/aes_wrap.h index 4a142093b0d62..b70b1d26e5503 100644 --- a/src/crypto/aes_wrap.h +++ b/src/crypto/aes_wrap.h @@ -3,7 +3,7 @@ * * - AES Key Wrap Algorithm (RFC3394) * - One-Key CBC MAC (OMAC1) hash with AES-128 and AES-256 - * - AES-128 CTR mode encryption + * - AES-128/192/256 CTR mode encryption * - AES-128 EAX mode encryption/decryption * - AES-128 CBC * - AES-GCM @@ -33,6 +33,8 @@ int __must_check omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, int __must_check omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac); int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out); +int __must_check aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce, + u8 *data, size_t data_len); int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, u8 *data, size_t data_len); int __must_check aes_128_eax_encrypt(const u8 *key, diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index bdc3ba6f37e0d..507b7cab86fc7 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -1,6 +1,6 @@ /* * Wrapper functions for crypto libraries - * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -106,8 +106,9 @@ int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, * @clear: 8 octets (in) * @key: 7 octets (in) (no parity bits included) * @cypher: 8 octets (out) + * Returns: 0 on success, -1 on failure */ -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher); +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher); /** * aes_encrypt_init - Initialize AES for encryption @@ -122,8 +123,9 @@ void * aes_encrypt_init(const u8 *key, size_t len); * @ctx: Context pointer from aes_encrypt_init() * @plain: Plaintext data to be encrypted (16 bytes) * @crypt: Buffer for the encrypted data (16 bytes) + * Returns: 0 on success, -1 on failure */ -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); /** * aes_encrypt_deinit - Deinitialize AES encryption @@ -144,8 +146,9 @@ void * aes_decrypt_init(const u8 *key, size_t len); * @ctx: Context pointer from aes_encrypt_init() * @crypt: Encrypted data (16 bytes) * @plain: Buffer for the decrypted data (16 bytes) + * Returns: 0 on success, -1 on failure */ -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); /** * aes_decrypt_deinit - Deinitialize AES decryption @@ -414,6 +417,13 @@ int __must_check crypto_public_key_decrypt_pkcs1( struct crypto_public_key *key, const u8 *crypt, size_t crypt_len, u8 *plain, size_t *plain_len); +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey); +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len); + /** * crypto_global_init - Initialize crypto wrapper * @@ -526,6 +536,14 @@ int crypto_bignum_to_bin(const struct crypto_bignum *a, u8 *buf, size_t buflen, size_t padlen); /** + * crypto_bignum_rand - Create a random number in range of modulus + * @r: Bignum; set to a random value + * @m: Bignum; modulus + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m); + +/** * crypto_bignum_add - c = a + b * @a: Bignum * @b: Bignum @@ -607,6 +625,16 @@ int crypto_bignum_mulmod(const struct crypto_bignum *a, struct crypto_bignum *d); /** + * crypto_bignum_rshift - r = a >> n + * @a: Bignum + * @n: Number of bits + * @r: Bignum; used to store the result of a >> n + * Returns: 0 on success, -1 on failure + */ +int crypto_bignum_rshift(const struct crypto_bignum *a, int n, + struct crypto_bignum *r); + +/** * crypto_bignum_cmp - Compare two bignums * @a: Bignum * @b: Bignum @@ -637,6 +665,13 @@ int crypto_bignum_is_zero(const struct crypto_bignum *a); int crypto_bignum_is_one(const struct crypto_bignum *a); /** + * crypto_bignum_is_odd - Is the given bignum odd + * @a: Bignum + * Returns: 1 if @a is odd or 0 if not + */ +int crypto_bignum_is_odd(const struct crypto_bignum *a); + +/** * crypto_bignum_legendre - Compute the Legendre symbol (a/p) * @a: Bignum * @p: Bignum @@ -668,6 +703,14 @@ struct crypto_ec * crypto_ec_init(int group); void crypto_ec_deinit(struct crypto_ec *e); /** + * crypto_ec_cofactor - Set the cofactor into the big number + * @e: EC context from crypto_ec_init() + * @cofactor: Cofactor of curve. + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor); + +/** * crypto_ec_prime_len - Get length of the prime in octets * @e: EC context from crypto_ec_init() * Returns: Length of the prime defining the group @@ -682,6 +725,13 @@ size_t crypto_ec_prime_len(struct crypto_ec *e); size_t crypto_ec_prime_len_bits(struct crypto_ec *e); /** + * crypto_ec_order_len - Get length of the order in octets + * @e: EC context from crypto_ec_init() + * Returns: Length of the order defining the group + */ +size_t crypto_ec_order_len(struct crypto_ec *e); + +/** * crypto_ec_get_prime - Get prime defining an EC group * @e: EC context from crypto_ec_init() * Returns: Prime (bignum) defining the group @@ -718,6 +768,16 @@ struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e); void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear); /** + * crypto_ec_point_x - Copies the x-ordinate point into big number + * @e: EC context from crypto_ec_init() + * @p: EC point data + * @x: Big number to set to the copy of x-ordinate + * Returns: 0 on success, -1 on failure + */ +int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p, + struct crypto_bignum *x); + +/** * crypto_ec_point_to_bin - Write EC point value as binary data * @e: EC context from crypto_ec_init() * @p: EC point data from crypto_ec_point_init() @@ -746,7 +806,7 @@ struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, const u8 *val); /** - * crypto_bignum_add - c = a + b + * crypto_ec_point_add - c = a + b * @e: EC context from crypto_ec_init() * @a: Bignum * @b: Bignum @@ -758,7 +818,7 @@ int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, struct crypto_ec_point *c); /** - * crypto_bignum_mul - res = b * p + * crypto_ec_point_mul - res = b * p * @e: EC context from crypto_ec_init() * @p: EC point * @b: Bignum @@ -829,4 +889,12 @@ int crypto_ec_point_cmp(const struct crypto_ec *e, const struct crypto_ec_point *a, const struct crypto_ec_point *b); +struct crypto_ecdh; + +struct crypto_ecdh * crypto_ecdh_init(int group); +struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y); +struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y, + const u8 *key, size_t len); +void crypto_ecdh_deinit(struct crypto_ecdh *ecdh); + #endif /* CRYPTO_H */ diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c index 0dfd54d22d475..7a797b5c359d1 100644 --- a/src/crypto/crypto_gnutls.c +++ b/src/crypto/crypto_gnutls.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / wrapper functions for libgcrypt - * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,27 +10,42 @@ #include <gcrypt.h> #include "common.h" +#include "md5.h" +#include "sha1.h" +#include "sha256.h" +#include "sha384.h" +#include "sha512.h" #include "crypto.h" -int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +static int gnutls_digest_vector(int algo, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) { gcry_md_hd_t hd; unsigned char *p; size_t i; - if (gcry_md_open(&hd, GCRY_MD_MD4, 0) != GPG_ERR_NO_ERROR) + if (TEST_FAIL()) + return -1; + + if (gcry_md_open(&hd, algo, 0) != GPG_ERR_NO_ERROR) return -1; for (i = 0; i < num_elem; i++) gcry_md_write(hd, addr[i], len[i]); - p = gcry_md_read(hd, GCRY_MD_MD4); + p = gcry_md_read(hd, algo); if (p) - memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD4)); + memcpy(mac, p, gcry_md_get_algo_dlen(algo)); gcry_md_close(hd); return 0; } -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_digest_vector(GCRY_MD_MD4, num_elem, addr, len, mac); +} + + +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { gcry_cipher_hd_t hd; u8 pkey[8], next, tmp; @@ -49,49 +64,161 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) gcry_err_code(gcry_cipher_setkey(hd, pkey, 8)); gcry_cipher_encrypt(hd, cypher, 8, clear, 8); gcry_cipher_close(hd); + return 0; } int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - gcry_md_hd_t hd; - unsigned char *p; - size_t i; - - if (gcry_md_open(&hd, GCRY_MD_MD5, 0) != GPG_ERR_NO_ERROR) - return -1; - for (i = 0; i < num_elem; i++) - gcry_md_write(hd, addr[i], len[i]); - p = gcry_md_read(hd, GCRY_MD_MD5); - if (p) - memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD5)); - gcry_md_close(hd); - return 0; + return gnutls_digest_vector(GCRY_MD_MD5, num_elem, addr, len, mac); } int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { + return gnutls_digest_vector(GCRY_MD_SHA1, num_elem, addr, len, mac); +} + + +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_digest_vector(GCRY_MD_SHA256, num_elem, addr, len, mac); +} + + +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_digest_vector(GCRY_MD_SHA384, num_elem, addr, len, mac); +} + + +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_digest_vector(GCRY_MD_SHA512, num_elem, addr, len, mac); +} + + +static int gnutls_hmac_vector(int algo, const u8 *key, size_t key_len, + size_t num_elem, const u8 *addr[], + const size_t *len, u8 *mac) +{ gcry_md_hd_t hd; unsigned char *p; size_t i; - if (gcry_md_open(&hd, GCRY_MD_SHA1, 0) != GPG_ERR_NO_ERROR) + if (TEST_FAIL()) + return -1; + + if (gcry_md_open(&hd, algo, GCRY_MD_FLAG_HMAC) != GPG_ERR_NO_ERROR) + return -1; + if (gcry_md_setkey(hd, key, key_len) != GPG_ERR_NO_ERROR) { + gcry_md_close(hd); return -1; + } for (i = 0; i < num_elem; i++) gcry_md_write(hd, addr[i], len[i]); - p = gcry_md_read(hd, GCRY_MD_SHA1); + p = gcry_md_read(hd, algo); if (p) - memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_SHA1)); + memcpy(mac, p, gcry_md_get_algo_dlen(algo)); gcry_md_close(hd); return 0; } +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_hmac_vector(GCRY_MD_MD5, key, key_len, num_elem, addr, + len, mac); +} + + +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); +} + + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_hmac_vector(GCRY_MD_SHA1, key, key_len, num_elem, addr, + len, mac); +} + + +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} + + +#ifdef CONFIG_SHA256 + +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_hmac_vector(GCRY_MD_SHA256, key, key_len, num_elem, addr, + len, mac); +} + + +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA256 */ + + +#ifdef CONFIG_SHA384 + +int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_hmac_vector(GCRY_MD_SHA384, key, key_len, num_elem, addr, + len, mac); +} + + +int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA384 */ + + +#ifdef CONFIG_SHA512 + +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return gnutls_hmac_vector(GCRY_MD_SHA512, key, key_len, num_elem, addr, + len, mac); +} + + +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA512 */ + + void * aes_encrypt_init(const u8 *key, size_t len) { gcry_cipher_hd_t hd; + if (TEST_FAIL()) + return NULL; + if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) != GPG_ERR_NO_ERROR) { printf("cipher open failed\n"); @@ -107,10 +234,11 @@ void * aes_encrypt_init(const u8 *key, size_t len) } -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { gcry_cipher_hd_t hd = ctx; gcry_cipher_encrypt(hd, crypt, 16, plain, 16); + return 0; } @@ -125,6 +253,9 @@ void * aes_decrypt_init(const u8 *key, size_t len) { gcry_cipher_hd_t hd; + if (TEST_FAIL()) + return NULL; + if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) != GPG_ERR_NO_ERROR) return NULL; @@ -137,10 +268,11 @@ void * aes_decrypt_init(const u8 *key, size_t len) } -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { gcry_cipher_hd_t hd = ctx; gcry_cipher_decrypt(hd, plain, 16, crypt, 16); + return 0; } @@ -151,6 +283,42 @@ void aes_decrypt_deinit(void *ctx) } +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey) +{ + size_t pubkey_len, pad; + + if (os_get_random(privkey, prime_len) < 0) + return -1; + if (os_memcmp(privkey, prime, prime_len) > 0) { + /* Make sure private value is smaller than prime */ + privkey[0] = 0; + } + + pubkey_len = prime_len; + if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len, + pubkey, &pubkey_len) < 0) + return -1; + if (pubkey_len < prime_len) { + pad = prime_len - pubkey_len; + os_memmove(pubkey + pad, pubkey, pubkey_len); + os_memset(pubkey, 0, pad); + } + + return 0; +} + + +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len) +{ + return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +} + + int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, diff --git a/src/crypto/crypto_internal-modexp.c b/src/crypto/crypto_internal-modexp.c index 9dcabb95bdd2a..92581ac676d31 100644 --- a/src/crypto/crypto_internal-modexp.c +++ b/src/crypto/crypto_internal-modexp.c @@ -13,6 +13,42 @@ #include "crypto.h" +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey) +{ + size_t pubkey_len, pad; + + if (os_get_random(privkey, prime_len) < 0) + return -1; + if (os_memcmp(privkey, prime, prime_len) > 0) { + /* Make sure private value is smaller than prime */ + privkey[0] = 0; + } + + pubkey_len = prime_len; + if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len, + pubkey, &pubkey_len) < 0) + return -1; + if (pubkey_len < prime_len) { + pad = prime_len - pubkey_len; + os_memmove(pubkey + pad, pubkey, pubkey_len); + os_memset(pubkey, 0, pad); + } + + return 0; +} + + +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len) +{ + return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +} + + int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c index a55edd14e2d3c..259f99500bcd3 100644 --- a/src/crypto/crypto_libtomcrypt.c +++ b/src/crypto/crypto_libtomcrypt.c @@ -35,7 +35,7 @@ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) } -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { u8 pkey[8], next, tmp; int i; @@ -53,6 +53,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) des_setup(pkey, 8, 0, &skey); des_ecb_encrypt(clear, cypher, &skey); des_done(&skey); + return 0; } @@ -96,10 +97,10 @@ void * aes_encrypt_init(const u8 *key, size_t len) } -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { symmetric_key *skey = ctx; - aes_ecb_encrypt(plain, crypt, skey); + return aes_ecb_encrypt(plain, crypt, skey) == CRYPT_OK ? 0 : -1; } @@ -125,10 +126,10 @@ void * aes_decrypt_init(const u8 *key, size_t len) } -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { symmetric_key *skey = ctx; - aes_ecb_encrypt(plain, (u8 *) crypt, skey); + return aes_ecb_encrypt(plain, (u8 *) crypt, skey) == CRYPT_OK ? 0 : -1; } @@ -297,7 +298,7 @@ struct crypto_cipher { struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, const u8 *iv, const u8 *key, size_t key_len) -{ +{ struct crypto_cipher *ctx; int idx, res, rc4 = 0; @@ -693,6 +694,42 @@ void crypto_global_deinit(void) #ifdef CONFIG_MODEXP +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey) +{ + size_t pubkey_len, pad; + + if (os_get_random(privkey, prime_len) < 0) + return -1; + if (os_memcmp(privkey, prime, prime_len) > 0) { + /* Make sure private value is smaller than prime */ + privkey[0] = 0; + } + + pubkey_len = prime_len; + if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len, + pubkey, &pubkey_len) < 0) + return -1; + if (pubkey_len < prime_len) { + pad = prime_len - pubkey_len; + os_memmove(pubkey + pad, pubkey, pubkey_len); + os_memset(pubkey, 0, pad); + } + + return 0; +} + + +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len) +{ + return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +} + + int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, diff --git a/src/crypto/crypto_linux.c b/src/crypto/crypto_linux.c new file mode 100644 index 0000000000000..8099193bf0682 --- /dev/null +++ b/src/crypto/crypto_linux.c @@ -0,0 +1,1006 @@ +/* + * Crypto wrapper for Linux kernel AF_ALG + * Copyright (c) 2017, 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 <linux/if_alg.h> + +#include "common.h" +#include "crypto.h" +#include "md5.h" +#include "sha1.h" +#include "sha256.h" +#include "sha384.h" +#include "aes.h" + + +#ifndef SOL_ALG +#define SOL_ALG 279 +#endif /* SOL_ALG */ + + +static int linux_af_alg_socket(const char *type, const char *name) +{ + struct sockaddr_alg sa; + int s; + + if (TEST_FAIL()) + return -1; + + s = socket(AF_ALG, SOCK_SEQPACKET, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, "%s: Failed to open AF_ALG socket: %s", + __func__, strerror(errno)); + return -1; + } + + os_memset(&sa, 0, sizeof(sa)); + sa.salg_family = AF_ALG; + os_strlcpy((char *) sa.salg_type, type, sizeof(sa.salg_type)); + os_strlcpy((char *) sa.salg_name, name, sizeof(sa.salg_type)); + if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) { + wpa_printf(MSG_ERROR, + "%s: Failed to bind AF_ALG socket(%s,%s): %s", + __func__, type, name, strerror(errno)); + close(s); + return -1; + } + + return s; +} + + +static int linux_af_alg_hash_vector(const char *alg, const u8 *key, + size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, + u8 *mac, size_t mac_len) +{ + int s, t; + size_t i; + ssize_t res; + int ret = -1; + + s = linux_af_alg_socket("hash", alg); + if (s < 0) + return -1; + + if (key && setsockopt(s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) { + wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s", + __func__, strerror(errno)); + close(s); + return -1; + } + + t = accept(s, NULL, NULL); + if (t < 0) { + wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s", + __func__, strerror(errno)); + close(s); + return -1; + } + + for (i = 0; i < num_elem; i++) { + res = send(t, addr[i], len[i], i + 1 < num_elem ? MSG_MORE : 0); + if (res < 0) { + wpa_printf(MSG_ERROR, + "%s: send on AF_ALG socket failed: %s", + __func__, strerror(errno)); + goto fail; + } + if ((size_t) res < len[i]) { + wpa_printf(MSG_ERROR, + "%s: send on AF_ALG socket did not accept full buffer (%d/%d)", + __func__, (int) res, (int) len[i]); + goto fail; + } + } + + res = recv(t, mac, mac_len, 0); + if (res < 0) { + wpa_printf(MSG_ERROR, + "%s: recv on AF_ALG socket failed: %s", + __func__, strerror(errno)); + goto fail; + } + if ((size_t) res < mac_len) { + wpa_printf(MSG_ERROR, + "%s: recv on AF_ALG socket did not return full buffer (%d/%d)", + __func__, (int) res, (int) mac_len); + goto fail; + } + + ret = 0; +fail: + close(t); + close(s); + + return ret; +} + + +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("md4", NULL, 0, num_elem, addr, len, + mac, 16); +} + + +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("md5", NULL, 0, num_elem, addr, len, + mac, MD5_MAC_LEN); +} + + +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return linux_af_alg_hash_vector("sha1", NULL, 0, num_elem, addr, len, + mac, SHA1_MAC_LEN); +} + + +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return linux_af_alg_hash_vector("sha256", NULL, 0, num_elem, addr, len, + mac, SHA256_MAC_LEN); +} + + +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return linux_af_alg_hash_vector("sha384", NULL, 0, num_elem, addr, len, + mac, SHA384_MAC_LEN); +} + + +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return linux_af_alg_hash_vector("sha512", NULL, 0, num_elem, addr, len, + mac, 64); +} + + +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("hmac(md5)", key, key_len, num_elem, + addr, len, mac, 16); +} + + +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); +} + + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("hmac(sha1)", key, key_len, num_elem, + addr, len, mac, SHA1_MAC_LEN); +} + + +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} + + +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("hmac(sha256)", key, key_len, num_elem, + addr, len, mac, SHA256_MAC_LEN); +} + + +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + + +int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("hmac(sha384)", key, key_len, num_elem, + addr, len, mac, SHA384_MAC_LEN); +} + + +int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac); +} + + +struct crypto_hash { + int s; + int t; + size_t mac_len; + int failed; +}; + + +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len) +{ + struct crypto_hash *ctx; + const char *name; + + ctx = os_zalloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + switch (alg) { + case CRYPTO_HASH_ALG_MD5: + name = "md5"; + ctx->mac_len = MD5_MAC_LEN; + break; + case CRYPTO_HASH_ALG_SHA1: + name = "sha1"; + ctx->mac_len = SHA1_MAC_LEN; + break; + case CRYPTO_HASH_ALG_HMAC_MD5: + name = "hmac(md5)"; + ctx->mac_len = MD5_MAC_LEN; + break; + case CRYPTO_HASH_ALG_HMAC_SHA1: + name = "hmac(sha1)"; + ctx->mac_len = SHA1_MAC_LEN; + break; + case CRYPTO_HASH_ALG_SHA256: + name = "sha256"; + ctx->mac_len = SHA256_MAC_LEN; + break; + case CRYPTO_HASH_ALG_HMAC_SHA256: + name = "hmac(sha256)"; + ctx->mac_len = SHA256_MAC_LEN; + break; + case CRYPTO_HASH_ALG_SHA384: + name = "sha384"; + ctx->mac_len = SHA384_MAC_LEN; + break; + case CRYPTO_HASH_ALG_SHA512: + name = "sha512"; + ctx->mac_len = 64; + break; + default: + os_free(ctx); + return NULL; + } + + ctx->s = linux_af_alg_socket("hash", name); + if (ctx->s < 0) { + os_free(ctx); + return NULL; + } + + if (key && key_len && + setsockopt(ctx->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) { + wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s", + __func__, strerror(errno)); + close(ctx->s); + os_free(ctx); + return NULL; + } + + ctx->t = accept(ctx->s, NULL, NULL); + if (ctx->t < 0) { + wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s", + __func__, strerror(errno)); + close(ctx->s); + os_free(ctx); + return NULL; + } + + return ctx; +} + + +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) +{ + ssize_t res; + + if (!ctx) + return; + + res = send(ctx->t, data, len, MSG_MORE); + if (res < 0) { + wpa_printf(MSG_ERROR, + "%s: send on AF_ALG socket failed: %s", + __func__, strerror(errno)); + ctx->failed = 1; + return; + } + if ((size_t) res < len) { + wpa_printf(MSG_ERROR, + "%s: send on AF_ALG socket did not accept full buffer (%d/%d)", + __func__, (int) res, (int) len); + ctx->failed = 1; + return; + } +} + + +static void crypto_hash_deinit(struct crypto_hash *ctx) +{ + close(ctx->s); + close(ctx->t); + os_free(ctx); +} + + +int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) +{ + ssize_t res; + + if (!ctx) + return -2; + + if (!mac || !len) { + crypto_hash_deinit(ctx); + return 0; + } + + if (ctx->failed) { + crypto_hash_deinit(ctx); + return -2; + } + + if (*len < ctx->mac_len) { + crypto_hash_deinit(ctx); + *len = ctx->mac_len; + return -1; + } + *len = ctx->mac_len; + + res = recv(ctx->t, mac, ctx->mac_len, 0); + if (res < 0) { + wpa_printf(MSG_ERROR, + "%s: recv on AF_ALG socket failed: %s", + __func__, strerror(errno)); + crypto_hash_deinit(ctx); + return -2; + } + if ((size_t) res < ctx->mac_len) { + wpa_printf(MSG_ERROR, + "%s: recv on AF_ALG socket did not return full buffer (%d/%d)", + __func__, (int) res, (int) ctx->mac_len); + crypto_hash_deinit(ctx); + return -2; + } + + crypto_hash_deinit(ctx); + return 0; +} + + +struct linux_af_alg_skcipher { + int s; + int t; +}; + + +static void linux_af_alg_skcipher_deinit(struct linux_af_alg_skcipher *skcipher) +{ + if (!skcipher) + return; + if (skcipher->s >= 0) + close(skcipher->s); + if (skcipher->t >= 0) + close(skcipher->t); + os_free(skcipher); +} + + +static struct linux_af_alg_skcipher * +linux_af_alg_skcipher(const char *alg, const u8 *key, size_t key_len) +{ + struct linux_af_alg_skcipher *skcipher; + + skcipher = os_zalloc(sizeof(*skcipher)); + if (!skcipher) + goto fail; + skcipher->t = -1; + + skcipher->s = linux_af_alg_socket("skcipher", alg); + if (skcipher->s < 0) + goto fail; + + if (setsockopt(skcipher->s, SOL_ALG, ALG_SET_KEY, key, key_len) < 0) { + wpa_printf(MSG_ERROR, "%s: setsockopt(ALG_SET_KEY) failed: %s", + __func__, strerror(errno)); + goto fail; + } + + skcipher->t = accept(skcipher->s, NULL, NULL); + if (skcipher->t < 0) { + wpa_printf(MSG_ERROR, "%s: accept on AF_ALG socket failed: %s", + __func__, strerror(errno)); + goto fail; + } + + return skcipher; +fail: + linux_af_alg_skcipher_deinit(skcipher); + return NULL; +} + + +static int linux_af_alg_skcipher_oper(struct linux_af_alg_skcipher *skcipher, + int enc, const u8 *in, u8 *out) +{ + char buf[CMSG_SPACE(sizeof(u32))]; + struct iovec io[1]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + u32 *op; + + io[0].iov_base = (void *) in; + io[0].iov_len = AES_BLOCK_SIZE; + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(u32)); + msg.msg_iov = io; + msg.msg_iovlen = 1; + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_OP; + hdr->cmsg_len = CMSG_LEN(sizeof(u32)); + op = (u32 *) CMSG_DATA(hdr); + *op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT; + + ret = sendmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + return -1; + } + + ret = read(skcipher->t, out, AES_BLOCK_SIZE); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: read failed: %s", + __func__, strerror(errno)); + return -1; + } + if (ret < AES_BLOCK_SIZE) { + wpa_printf(MSG_ERROR, + "%s: read did not return full data (%d/%d)", + __func__, (int) ret, AES_BLOCK_SIZE); + return -1; + } + + return 0; +} + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + return linux_af_alg_skcipher("ecb(aes)", key, len); +} + + +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + struct linux_af_alg_skcipher *skcipher = ctx; + + return linux_af_alg_skcipher_oper(skcipher, 1, plain, crypt); +} + + +void aes_encrypt_deinit(void *ctx) +{ + linux_af_alg_skcipher_deinit(ctx); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + return linux_af_alg_skcipher("ecb(aes)", key, len); +} + + +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + struct linux_af_alg_skcipher *skcipher = ctx; + + return linux_af_alg_skcipher_oper(skcipher, 0, crypt, plain); +} + + +void aes_decrypt_deinit(void *ctx) +{ + linux_af_alg_skcipher_deinit(ctx); +} + + +int rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len) +{ + struct linux_af_alg_skcipher *skcipher; + u8 *skip_buf; + char buf[CMSG_SPACE(sizeof(u32))]; + struct iovec io[2]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + u32 *op; + + skip_buf = os_zalloc(skip + 1); + if (!skip_buf) + return -1; + skcipher = linux_af_alg_skcipher("ecb(arc4)", key, keylen); + if (!skcipher) { + os_free(skip_buf); + return -1; + } + + io[0].iov_base = skip_buf; + io[0].iov_len = skip; + io[1].iov_base = data; + io[1].iov_len = data_len; + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(u32)); + msg.msg_iov = io; + msg.msg_iovlen = 2; + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_OP; + hdr->cmsg_len = CMSG_LEN(sizeof(u32)); + op = (u32 *) CMSG_DATA(hdr); + *op = ALG_OP_ENCRYPT; + + ret = sendmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + os_free(skip_buf); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + os_free(skip_buf); + + msg.msg_control = NULL; + msg.msg_controllen = 0; + ret = recvmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s", + __func__, strerror(errno)); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + linux_af_alg_skcipher_deinit(skcipher); + + if ((size_t) ret < skip + data_len) { + wpa_printf(MSG_ERROR, + "%s: recvmsg did not return full data (%d/%d)", + __func__, (int) ret, (int) (skip + data_len)); + return -1; + } + + return 0; +} + + +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + u8 pkey[8], next, tmp; + int i; + struct linux_af_alg_skcipher *skcipher; + char buf[CMSG_SPACE(sizeof(u32))]; + struct iovec io[1]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + u32 *op; + int res = -1; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + skcipher = linux_af_alg_skcipher("ecb(des)", pkey, sizeof(pkey)); + if (!skcipher) + goto fail; + + io[0].iov_base = (void *) clear; + io[0].iov_len = 8; + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(u32)); + msg.msg_iov = io; + msg.msg_iovlen = 1; + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_OP; + hdr->cmsg_len = CMSG_LEN(sizeof(u32)); + op = (u32 *) CMSG_DATA(hdr); + *op = ALG_OP_ENCRYPT; + + ret = sendmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + goto fail; + } + + ret = read(skcipher->t, cypher, 8); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: read failed: %s", + __func__, strerror(errno)); + goto fail; + } + if (ret < 8) { + wpa_printf(MSG_ERROR, + "%s: read did not return full data (%d/8)", + __func__, (int) ret); + goto fail; + } + + res = 0; +fail: + linux_af_alg_skcipher_deinit(skcipher); + return res; +} + + +static int aes_128_cbc_oper(const u8 *key, int enc, const u8 *iv, + u8 *data, size_t data_len) +{ + struct linux_af_alg_skcipher *skcipher; + char buf[100]; + struct iovec io[1]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + u32 *op; + struct af_alg_iv *alg_iv; + size_t iv_len = AES_BLOCK_SIZE; + + skcipher = linux_af_alg_skcipher("cbc(aes)", key, 16); + if (!skcipher) + return -1; + + io[0].iov_base = (void *) data; + io[0].iov_len = data_len; + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(u32)) + + CMSG_SPACE(sizeof(*alg_iv) + iv_len); + msg.msg_iov = io; + msg.msg_iovlen = 1; + + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_OP; + hdr->cmsg_len = CMSG_LEN(sizeof(u32)); + op = (u32 *) CMSG_DATA(hdr); + *op = enc ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT; + + hdr = CMSG_NXTHDR(&msg, hdr); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_IV; + hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len); + alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr); + alg_iv->ivlen = iv_len; + os_memcpy(alg_iv->iv, iv, iv_len); + + ret = sendmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + + ret = recvmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: recvmsg failed: %s", + __func__, strerror(errno)); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + if ((size_t) ret < data_len) { + wpa_printf(MSG_ERROR, + "%s: recvmsg not return full data (%d/%d)", + __func__, (int) ret, (int) data_len); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + + linux_af_alg_skcipher_deinit(skcipher); + return 0; +} + + +int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + return aes_128_cbc_oper(key, 1, iv, data, data_len); +} + + +int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + return aes_128_cbc_oper(key, 0, iv, data, data_len); +} + + +int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return linux_af_alg_hash_vector("cmac(aes)", key, key_len, num_elem, + addr, len, mac, AES_BLOCK_SIZE); +} + + +int omac1_aes_128_vector(const u8 *key, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return omac1_aes_vector(key, 16, num_elem, addr, len, mac); +} + + +int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_128_vector(key, 1, &data, &data_len, mac); +} + + +int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_vector(key, 32, 1, &data, &data_len, mac); +} + + +int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, + u8 *plain) +{ + struct linux_af_alg_skcipher *skcipher; + char buf[100]; + struct iovec io[1]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + u32 *op; + struct af_alg_iv *alg_iv; + size_t iv_len = 8; + + skcipher = linux_af_alg_skcipher("kw(aes)", kek, kek_len); + if (!skcipher) + return -1; + + io[0].iov_base = (void *) (cipher + iv_len); + io[0].iov_len = n * 8; + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(u32)) + + CMSG_SPACE(sizeof(*alg_iv) + iv_len); + msg.msg_iov = io; + msg.msg_iovlen = 1; + + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_OP; + hdr->cmsg_len = CMSG_LEN(sizeof(u32)); + op = (u32 *) CMSG_DATA(hdr); + *op = ALG_OP_DECRYPT; + + hdr = CMSG_NXTHDR(&msg, hdr); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_IV; + hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len); + alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr); + alg_iv->ivlen = iv_len; + os_memcpy(alg_iv->iv, cipher, iv_len); + + ret = sendmsg(skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + return -1; + } + + ret = read(skcipher->t, plain, n * 8); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: read failed: %s", + __func__, strerror(errno)); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + if (ret < n * 8) { + wpa_printf(MSG_ERROR, + "%s: read not return full data (%d/%d)", + __func__, (int) ret, n * 8); + linux_af_alg_skcipher_deinit(skcipher); + return -1; + } + + linux_af_alg_skcipher_deinit(skcipher); + return 0; +} + + +struct crypto_cipher { + struct linux_af_alg_skcipher *skcipher; +}; + + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + const char *name; + struct af_alg_iv *alg_iv; + size_t iv_len = 0; + char buf[100]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + + ctx = os_zalloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + switch (alg) { + case CRYPTO_CIPHER_ALG_RC4: + name = "ecb(arc4)"; + break; + case CRYPTO_CIPHER_ALG_AES: + name = "cbc(aes)"; + iv_len = AES_BLOCK_SIZE; + break; + case CRYPTO_CIPHER_ALG_3DES: + name = "cbc(des3_ede)"; + iv_len = 8; + break; + case CRYPTO_CIPHER_ALG_DES: + name = "cbc(des)"; + iv_len = 8; + break; + default: + os_free(ctx); + return NULL; + } + + ctx->skcipher = linux_af_alg_skcipher(name, key, key_len); + if (!ctx->skcipher) { + os_free(ctx); + return NULL; + } + + if (iv && iv_len) { + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(*alg_iv) + iv_len); + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_IV; + hdr->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv_len); + alg_iv = (struct af_alg_iv *) CMSG_DATA(hdr); + alg_iv->ivlen = iv_len; + os_memcpy(alg_iv->iv, iv, iv_len); + + ret = sendmsg(ctx->skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + linux_af_alg_skcipher_deinit(ctx->skcipher); + os_free(ctx); + return NULL; + } + } + + return ctx; +} + + +static int crypto_cipher_oper(struct crypto_cipher *ctx, u32 type, const u8 *in, + u8 *out, size_t len) +{ + char buf[CMSG_SPACE(sizeof(u32))]; + struct iovec io[1]; + struct msghdr msg; + struct cmsghdr *hdr; + ssize_t ret; + u32 *op; + + io[0].iov_base = (void *) in; + io[0].iov_len = len; + os_memset(&msg, 0, sizeof(msg)); + os_memset(buf, 0, sizeof(buf)); + msg.msg_control = buf; + msg.msg_controllen = CMSG_SPACE(sizeof(u32)); + msg.msg_iov = io; + msg.msg_iovlen = 1; + hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_ALG; + hdr->cmsg_type = ALG_SET_OP; + hdr->cmsg_len = CMSG_LEN(sizeof(u32)); + op = (u32 *) CMSG_DATA(hdr); + *op = type; + + ret = sendmsg(ctx->skcipher->t, &msg, 0); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: sendmsg failed: %s", + __func__, strerror(errno)); + return -1; + } + + ret = read(ctx->skcipher->t, out, len); + if (ret < 0) { + wpa_printf(MSG_ERROR, "%s: read failed: %s", + __func__, strerror(errno)); + return -1; + } + if (ret < (ssize_t) len) { + wpa_printf(MSG_ERROR, + "%s: read did not return full data (%d/%d)", + __func__, (int) ret, (int) len); + return -1; + } + + return 0; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + return crypto_cipher_oper(ctx, ALG_OP_ENCRYPT, plain, crypt, len); +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + return crypto_cipher_oper(ctx, ALG_OP_DECRYPT, crypt, plain, len); +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + if (ctx) { + linux_af_alg_skcipher_deinit(ctx->skcipher); + os_free(ctx); + } +} + + +int crypto_global_init(void) +{ + return 0; +} + + +void crypto_global_deinit(void) +{ +} diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c index ffd23942e32d7..1cc73d8ec16ec 100644 --- a/src/crypto/crypto_module_tests.c +++ b/src/crypto/crypto_module_tests.c @@ -17,6 +17,7 @@ #include "crypto/crypto.h" #include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/sha384.h" static int test_siv(void) @@ -92,7 +93,7 @@ static int test_siv(void) addr[0] = ad; len[0] = sizeof(ad); - if (aes_siv_encrypt(key, plaintext, sizeof(plaintext), + if (aes_siv_encrypt(key, sizeof(key), plaintext, sizeof(plaintext), 1, addr, len, out)) { wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed"); return 1; @@ -103,7 +104,8 @@ static int test_siv(void) return 1; } - if (aes_siv_decrypt(key, iv_c, sizeof(iv_c), 1, addr, len, out)) { + if (aes_siv_decrypt(key, sizeof(key), iv_c, sizeof(iv_c), + 1, addr, len, out)) { wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed"); return 1; } @@ -121,7 +123,8 @@ static int test_siv(void) addr[2] = nonce_2; len[2] = sizeof(nonce_2); - if (aes_siv_encrypt(key_2, plaintext_2, sizeof(plaintext_2), + if (aes_siv_encrypt(key_2, sizeof(key_2), + plaintext_2, sizeof(plaintext_2), 3, addr, len, out)) { wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed"); return 1; @@ -132,7 +135,8 @@ static int test_siv(void) return 1; } - if (aes_siv_decrypt(key_2, iv_c_2, sizeof(iv_c_2), 3, addr, len, out)) { + if (aes_siv_decrypt(key_2, sizeof(key_2), iv_c_2, sizeof(iv_c_2), + 3, addr, len, out)) { wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed"); return 1; } @@ -1292,13 +1296,14 @@ static const struct { }; static const struct hmac_test { - u8 key[80]; + u8 key[150]; size_t key_len; - u8 data[128]; + u8 data[160]; size_t data_len; - u8 hash[32]; + u8 hash[32]; /* HMAC-SHA-256 */ + u8 hash384[48]; /* HMAC-SHA-384 */ } hmac_tests[] = { - /* draft-ietf-ipsec-ciph-sha-256-01.txt */ + /* draft-ietf-ipsec-ciph-sha-256-01.txt; RFC 4231 */ { { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, @@ -1313,7 +1318,8 @@ static const struct hmac_test { 0x4d, 0xd9, 0x39, 0x75, 0x0f, 0x7a, 0x06, 0x6a, 0x7f, 0x98, 0xcc, 0x13, 0x1c, 0xb1, 0x6a, 0x66, 0x92, 0x75, 0x90, 0x21, 0xcf, 0xab, 0x81, 0x81 - } + }, + { } }, { { @@ -1330,7 +1336,8 @@ static const struct hmac_test { 0x18, 0x4b, 0xa7, 0x31, 0x31, 0xc5, 0x3c, 0xae, 0xe6, 0x98, 0xe3, 0x61, 0x19, 0x42, 0x11, 0x49, 0xea, 0x8c, 0x71, 0x24, 0x56, 0x69, 0x7d, 0x30 - } + }, + { } }, { { @@ -1348,7 +1355,8 @@ static const struct hmac_test { 0xd3, 0xee, 0xb3, 0xe7, 0x73, 0xd9, 0x5a, 0xab, 0x73, 0xac, 0xf0, 0xfd, 0x06, 0x04, 0x47, 0xa5, 0xeb, 0x45, 0x95, 0xbf, 0x33, 0xa9, 0xd1, 0xa3 - } + }, + { } }, { { @@ -1365,9 +1373,34 @@ static const struct hmac_test { 0x99, 0x03, 0xa0, 0xf1, 0xcf, 0x2b, 0xbd, 0xc5, 0xba, 0x0a, 0xa3, 0xf3, 0xd9, 0xae, 0x3c, 0x1c, 0x7a, 0x3b, 0x16, 0x96, 0xa0, 0xb6, 0x8c, 0xf7 + }, + { } + }, + { /* RFC 4231 - Test Case 1 */ + { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b + }, + 20, + "Hi There", + 8, + { + 0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, + 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 0xf1, 0x2b, + 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7, + 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7 + }, + { + 0xaf, 0xd0, 0x39, 0x44, 0xd8, 0x48, 0x95, 0x62, + 0x6b, 0x08, 0x25, 0xf4, 0xab, 0x46, 0x90, 0x7f, + 0x15, 0xf9, 0xda, 0xdb, 0xe4, 0x10, 0x1e, 0xc6, + 0x82, 0xaa, 0x03, 0x4c, 0x7c, 0xeb, 0xc5, 0x9c, + 0xfa, 0xea, 0x9e, 0xa9, 0x07, 0x6e, 0xde, 0x7f, + 0x4a, 0xf1, 0x52, 0xe8, 0xb2, 0xfa, 0x9c, 0xb6 } }, - { + { /* RFC 4231 - Test Case 2 */ "Jefe", 4, "what do ya want for nothing?", @@ -1377,6 +1410,14 @@ static const struct hmac_test { 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7, 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83, 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43 + }, + { + 0xaf, 0x45, 0xd2, 0xe3, 0x76, 0x48, 0x40, 0x31, + 0x61, 0x7f, 0x78, 0xd2, 0xb5, 0x8a, 0x6b, 0x1b, + 0x9c, 0x7e, 0xf4, 0x64, 0xf5, 0xa0, 0x1b, 0x47, + 0xe4, 0x2e, 0xc3, 0x73, 0x63, 0x22, 0x44, 0x5e, + 0x8e, 0x22, 0x40, 0xca, 0x5e, 0x69, 0xe2, 0xc7, + 0x8b, 0x32, 0x39, 0xec, 0xfa, 0xb2, 0x16, 0x49 } }, { @@ -1402,6 +1443,39 @@ static const struct hmac_test { 0x91, 0xe5, 0x3a, 0xba, 0x30, 0x92, 0xf9, 0x62, 0xe5, 0x49, 0xfe, 0x6c, 0xe9, 0xed, 0x7f, 0xdc, 0x43, 0x19, 0x1f, 0xbd, 0xe4, 0x5c, 0x30, 0xb0 + }, + { } + }, + { /* RFC 4231 - Test Case 3 */ + { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa + }, + 20, + { + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, + 0xdd, 0xdd + }, + 50, + { + 0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, + 0x85, 0x4d, 0xb8, 0xeb, 0xd0, 0x91, 0x81, 0xa7, + 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, 0xc1, 0x22, + 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe + }, + { + 0x88, 0x06, 0x26, 0x08, 0xd3, 0xe6, 0xad, 0x8a, + 0x0a, 0xa2, 0xac, 0xe0, 0x14, 0xc8, 0xa8, 0x6f, + 0x0a, 0xa6, 0x35, 0xd9, 0x47, 0xac, 0x9f, 0xeb, + 0xe8, 0x3e, 0xf4, 0xe5, 0x59, 0x66, 0x14, 0x4b, + 0x2a, 0x5a, 0xb3, 0x9d, 0xc1, 0x38, 0x14, 0xb9, + 0x4e, 0x3a, 0xb6, 0xe1, 0x01, 0xa3, 0x4f, 0x27 } }, { @@ -1428,6 +1502,40 @@ static const struct hmac_test { 0x4c, 0x66, 0xde, 0xe0, 0xf8, 0xf0, 0x74, 0x55, 0x6e, 0xc4, 0xaf, 0x55, 0xef, 0x07, 0x99, 0x85, 0x41, 0x46, 0x8e, 0xb4, 0x9b, 0xd2, 0xe9, 0x17 + }, + { } + }, + { /* RFC 4231 - Test Case 4 */ + { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, + }, + 25, + { + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd + }, + 50, + { + 0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e, + 0xa4, 0xcc, 0x81, 0x98, 0x99, 0xf2, 0x08, 0x3a, + 0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78, 0xf8, 0x07, + 0x7a, 0x2e, 0x3f, 0xf4, 0x67, 0x29, 0x66, 0x5b + }, + { + 0x3e, 0x8a, 0x69, 0xb7, 0x78, 0x3c, 0x25, 0x85, + 0x19, 0x33, 0xab, 0x62, 0x90, 0xaf, 0x6c, 0xa7, + 0x7a, 0x99, 0x81, 0x48, 0x08, 0x50, 0x00, 0x9c, + 0xc5, 0x57, 0x7c, 0x6e, 0x1f, 0x57, 0x3b, 0x4e, + 0x68, 0x01, 0xdd, 0x23, 0xc4, 0xa7, 0xd6, 0x79, + 0xcc, 0xf8, 0xa3, 0x86, 0xc6, 0x74, 0xcf, 0xfb } }, { @@ -1445,7 +1553,8 @@ static const struct hmac_test { 0x1a, 0xb9, 0xc3, 0x74, 0x9a, 0x5f, 0x1c, 0x17, 0xd4, 0xf5, 0x89, 0x66, 0x8a, 0x58, 0x7b, 0x27, 0x00, 0xa9, 0xc9, 0x7c, 0x11, 0x93, 0xcf, 0x42 - } + }, + { } }, { { @@ -1468,6 +1577,45 @@ static const struct hmac_test { 0xf8, 0x0a, 0x96, 0xf7, 0x8e, 0x65, 0x38, 0xdb, 0xe2, 0xe7, 0xb8, 0x20, 0xe3, 0xdd, 0x97, 0x0e, 0x7d, 0xdd, 0x39, 0x09, 0x1b, 0x32, 0x35, 0x2f + }, + { } + }, + { /* RFC 4231 - Test Case 6 */ + { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa + }, + 131, + "Test Using Larger Than Block-Size Key - Hash Key First", + 54, + { + 0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, + 0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5, 0xb7, 0x7f, + 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14, + 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54 + }, + { + 0x4e, 0xce, 0x08, 0x44, 0x85, 0x81, 0x3e, 0x90, + 0x88, 0xd2, 0xc6, 0x3a, 0x04, 0x1b, 0xc5, 0xb4, + 0x4f, 0x9e, 0xf1, 0x01, 0x2a, 0x2b, 0x58, 0x8f, + 0x3c, 0xd1, 0x1f, 0x05, 0x03, 0x3a, 0xc4, 0xc6, + 0x0c, 0x2e, 0xf6, 0xab, 0x40, 0x30, 0xfe, 0x82, + 0x96, 0x24, 0x8d, 0xf1, 0x63, 0xf4, 0x49, 0x52 } }, { @@ -1492,6 +1640,45 @@ static const struct hmac_test { 0xc8, 0x48, 0x1a, 0x5c, 0xa4, 0x82, 0x5b, 0xc8, 0x84, 0xd3, 0xe7, 0xa1, 0xff, 0x98, 0xa2, 0xfc, 0x2a, 0xc7, 0xd8, 0xe0, 0x64, 0xc3, 0xb2, 0xe6 + }, + { } + }, + { /* RFC 4231 - Test Case 7 */ + { + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa + }, + 131, + "This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.", + 152, + { + 0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, + 0x27, 0x63, 0x5f, 0xbc, 0xd5, 0xb0, 0xe9, 0x44, + 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93, + 0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2 + }, + { + 0x66, 0x17, 0x17, 0x8e, 0x94, 0x1f, 0x02, 0x0d, + 0x35, 0x1e, 0x2f, 0x25, 0x4e, 0x8f, 0xd3, 0x2c, + 0x60, 0x24, 0x20, 0xfe, 0xb0, 0xb8, 0xfb, 0x9a, + 0xdc, 0xce, 0xbb, 0x82, 0x46, 0x1e, 0x99, 0xc5, + 0xa6, 0x78, 0xcc, 0x31, 0xe7, 0x99, 0x17, 0x6d, + 0x38, 0x60, 0xe6, 0x11, 0x0c, 0x46, 0x52, 0x3e } } }; @@ -1606,6 +1793,96 @@ static int test_sha256(void) } +static int test_sha384(void) +{ +#ifdef CONFIG_SHA384 + unsigned int i; + u8 hash[48]; + const u8 *addr[2]; + size_t len[2]; + int errors = 0; + const char *data = "hello"; + const u8 hash_res[] = { + 0x59, 0xe1, 0x74, 0x87, 0x77, 0x44, 0x8c, 0x69, + 0xde, 0x6b, 0x80, 0x0d, 0x7a, 0x33, 0xbb, 0xfb, + 0x9f, 0xf1, 0xb4, 0x63, 0xe4, 0x43, 0x54, 0xc3, + 0x55, 0x3b, 0xcd, 0xb9, 0xc6, 0x66, 0xfa, 0x90, + 0x12, 0x5a, 0x3c, 0x79, 0xf9, 0x03, 0x97, 0xbd, + 0xf5, 0xf6, 0xa1, 0x3d, 0xe8, 0x28, 0x68, 0x4f + }; + + addr[0] = (const u8 *) data; + len[0] = 5; + if (sha384_vector(1, addr, len, hash) < 0 || + os_memcmp(hash, hash_res, 48) != 0) { + wpa_printf(MSG_INFO, "SHA384 test case 1: FAIL"); + errors++; + } else { + wpa_printf(MSG_INFO, "SHA384 test case 1: OK"); + } + + addr[0] = (const u8 *) data; + len[0] = 4; + addr[1] = (const u8 *) data + 4; + len[1] = 1; + if (sha384_vector(2, addr, len, hash) < 0 || + os_memcmp(hash, hash_res, 48) != 0) { + wpa_printf(MSG_INFO, "SHA384 test case 2: FAIL"); + errors++; + } else { + wpa_printf(MSG_INFO, "SHA384 test case 2: OK"); + } + + for (i = 0; i < ARRAY_SIZE(hmac_tests); i++) { + const struct hmac_test *t = &hmac_tests[i]; + + if (t->hash384[0] == 0 && t->hash384[1] == 0 && + t->hash384[2] == 0 && t->hash384[3] == 0) + continue; + wpa_printf(MSG_INFO, "HMAC-SHA384 test case %d:", i + 1); + + if (hmac_sha384(t->key, t->key_len, t->data, t->data_len, + hash) < 0 || + os_memcmp(hash, t->hash384, 48) != 0) { + wpa_printf(MSG_INFO, " FAIL"); + errors++; + } else + wpa_printf(MSG_INFO, " OK"); + + addr[0] = t->data; + len[0] = t->data_len; + if (hmac_sha384_vector(t->key, t->key_len, 1, addr, len, + hash) < 0 || + os_memcmp(hash, t->hash384, 48) != 0) { + wpa_printf(MSG_INFO, " FAIL"); + errors++; + } else + wpa_printf(MSG_INFO, " OK"); + + if (len[0]) { + addr[0] = t->data; + len[0] = 1; + addr[1] = t->data + 1; + len[1] = t->data_len - 1; + if (hmac_sha384_vector(t->key, t->key_len, 2, addr, len, + hash) < 0 || + os_memcmp(hash, t->hash384, 48) != 0) { + wpa_printf(MSG_INFO, " FAIL"); + errors++; + } else + wpa_printf(MSG_INFO, " OK"); + } + } + + if (!errors) + wpa_printf(MSG_INFO, "SHA384 test cases passed"); + return errors; +#else /* CONFIG_SHA384 */ + return 0; +#endif /* CONFIG_SHA384 */ +} + + static int test_fips186_2_prf(void) { /* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */ @@ -1635,6 +1912,135 @@ static int test_fips186_2_prf(void) } +static int test_extract_expand_hkdf(void) +{ + u8 prk[SHA256_MAC_LEN]; + u8 okm[82]; + + /* RFC 5869, A.1 */ + u8 ikm1[22] = { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b + }; + u8 salt1[13] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c + }; + u8 info1[10] = { + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9 + }; + u8 prk1[32] = { + 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, + 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, + 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31, + 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5 + }; + u8 okm1[42] = { + 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, + 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, + 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, + 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, + 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, + 0x58, 0x65 + }; + + /* RFC 5869, A.2 */ + u8 ikm2[80] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f + }; + u8 salt2[80] = { + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf + }; + u8 info2[80] = { + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff + }; + u8 prk2[32] = { + 0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a, + 0x06, 0x10, 0x4c, 0x9c, 0xeb, 0x35, 0xb4, 0x5c, + 0xef, 0x76, 0x00, 0x14, 0x90, 0x46, 0x71, 0x01, + 0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, 0xc2, 0x44 + }; + u8 okm2[82] = { + 0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, + 0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34, + 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8, + 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c, + 0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, + 0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09, + 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8, + 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71, + 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87, + 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f, + 0x1d, 0x87 + }; + + wpa_printf(MSG_INFO, "Testing Extract-and-Expand HKDF (RFC 5869)"); + + wpa_printf(MSG_INFO, "RFC 5869 - Test Case 1"); + if (hmac_sha256(salt1, sizeof(salt1), ikm1, sizeof(ikm1), prk) < 0) + return -1; + if (os_memcmp(prk, prk1, SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_INFO, "HKDF-Extract mismatch in PRK"); + return -1; + } + if (hmac_sha256_kdf(prk1, sizeof(prk1), NULL, info1, sizeof(info1), + okm, sizeof(okm1)) < 0) + return -1; + if (os_memcmp(okm, okm1, sizeof(okm1)) != 0) { + wpa_printf(MSG_INFO, "HKDF-Expand mismatch in OKM"); + return -1; + } + + wpa_printf(MSG_INFO, "RFC 5869 - Test Case 2"); + if (hmac_sha256(salt2, sizeof(salt2), ikm2, sizeof(ikm2), prk) < 0) + return -1; + if (os_memcmp(prk, prk2, SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_INFO, "HKDF-Extract mismatch in PRK"); + return -1; + } + if (hmac_sha256_kdf(prk2, sizeof(prk2), NULL, info2, sizeof(info2), + okm, sizeof(okm2)) < 0) + return -1; + if (os_memcmp(okm, okm2, sizeof(okm2)) != 0) { + wpa_printf(MSG_INFO, "HKDF-Expand mismatch in OKM"); + return -1; + } + + wpa_printf(MSG_INFO, "Extract-and-Expand HKDF test cases passed"); + + return 0; +} + + static int test_ms_funcs(void) { #ifndef CONFIG_FIPS @@ -1751,7 +2157,9 @@ int crypto_module_tests(void) test_md5() || test_sha1() || test_sha256() || + test_sha384() || test_fips186_2_prf() || + test_extract_expand_hkdf() || test_ms_funcs()) ret = -1; diff --git a/src/crypto/crypto_nettle.c b/src/crypto/crypto_nettle.c new file mode 100644 index 0000000000000..4e31bc8011325 --- /dev/null +++ b/src/crypto/crypto_nettle.c @@ -0,0 +1,437 @@ +/* + * Wrapper functions for libnettle and libgmp + * Copyright (c) 2017, 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 <nettle/nettle-meta.h> +#include <nettle/des.h> +#undef des_encrypt +#include <nettle/hmac.h> +#include <nettle/aes.h> +#undef aes_encrypt +#undef aes_decrypt +#include <nettle/arcfour.h> +#include <nettle/bignum.h> + +#include "common.h" +#include "md5.h" +#include "sha1.h" +#include "sha256.h" +#include "sha384.h" +#include "sha512.h" +#include "crypto.h" + + +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + struct des_ctx ctx; + u8 pkey[8], next, tmp; + int i; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + nettle_des_set_key(&ctx, pkey); + nettle_des_encrypt(&ctx, DES_BLOCK_SIZE, cypher, clear); + os_memset(&ctx, 0, sizeof(ctx)); + return 0; +} + + +static int nettle_digest_vector(const struct nettle_hash *alg, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + void *ctx; + size_t i; + + if (TEST_FAIL()) + return -1; + + ctx = os_malloc(alg->context_size); + if (!ctx) + return -1; + alg->init(ctx); + for (i = 0; i < num_elem; i++) + alg->update(ctx, len[i], addr[i]); + alg->digest(ctx, alg->digest_size, mac); + bin_clear_free(ctx, alg->context_size); + return 0; +} + + +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nettle_digest_vector(&nettle_md4, num_elem, addr, len, mac); +} + + +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nettle_digest_vector(&nettle_md5, num_elem, addr, len, mac); +} + + +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nettle_digest_vector(&nettle_sha1, num_elem, addr, len, mac); +} + + +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nettle_digest_vector(&nettle_sha256, num_elem, addr, len, mac); +} + + +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nettle_digest_vector(&nettle_sha384, num_elem, addr, len, mac); +} + + +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nettle_digest_vector(&nettle_sha512, num_elem, addr, len, mac); +} + + +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + struct hmac_md5_ctx ctx; + size_t i; + + if (TEST_FAIL()) + return -1; + + hmac_md5_set_key(&ctx, key_len, key); + for (i = 0; i < num_elem; i++) + hmac_md5_update(&ctx, len[i], addr[i]); + hmac_md5_digest(&ctx, MD5_DIGEST_SIZE, mac); + os_memset(&ctx, 0, sizeof(ctx)); + return 0; +} + + +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); +} + + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + struct hmac_sha1_ctx ctx; + size_t i; + + if (TEST_FAIL()) + return -1; + + hmac_sha1_set_key(&ctx, key_len, key); + for (i = 0; i < num_elem; i++) + hmac_sha1_update(&ctx, len[i], addr[i]); + hmac_sha1_digest(&ctx, SHA1_DIGEST_SIZE, mac); + os_memset(&ctx, 0, sizeof(ctx)); + return 0; +} + + +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} + + +#ifdef CONFIG_SHA256 + +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + struct hmac_sha256_ctx ctx; + size_t i; + + if (TEST_FAIL()) + return -1; + + hmac_sha256_set_key(&ctx, key_len, key); + for (i = 0; i < num_elem; i++) + hmac_sha256_update(&ctx, len[i], addr[i]); + hmac_sha256_digest(&ctx, SHA256_DIGEST_SIZE, mac); + os_memset(&ctx, 0, sizeof(ctx)); + return 0; +} + + +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA256 */ + + +#ifdef CONFIG_SHA384 + +int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + struct hmac_sha384_ctx ctx; + size_t i; + + if (TEST_FAIL()) + return -1; + + hmac_sha384_set_key(&ctx, key_len, key); + for (i = 0; i < num_elem; i++) + hmac_sha384_update(&ctx, len[i], addr[i]); + hmac_sha384_digest(&ctx, SHA384_DIGEST_SIZE, mac); + os_memset(&ctx, 0, sizeof(ctx)); + return 0; +} + + +int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA384 */ + + +#ifdef CONFIG_SHA512 + +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + struct hmac_sha512_ctx ctx; + size_t i; + + if (TEST_FAIL()) + return -1; + + hmac_sha512_set_key(&ctx, key_len, key); + for (i = 0; i < num_elem; i++) + hmac_sha512_update(&ctx, len[i], addr[i]); + hmac_sha512_digest(&ctx, SHA512_DIGEST_SIZE, mac); + os_memset(&ctx, 0, sizeof(ctx)); + return 0; +} + + +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA512 */ + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + struct aes_ctx *ctx; + + if (TEST_FAIL()) + return NULL; + ctx = os_malloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + nettle_aes_set_encrypt_key(ctx, len, key); + + return ctx; +} + + +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + struct aes_ctx *actx = ctx; + nettle_aes_encrypt(actx, AES_BLOCK_SIZE, crypt, plain); + return 0; +} + + +void aes_encrypt_deinit(void *ctx) +{ + struct aes_ctx *actx = ctx; + bin_clear_free(actx, sizeof(*actx)); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + struct aes_ctx *ctx; + + if (TEST_FAIL()) + return NULL; + ctx = os_malloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + nettle_aes_set_decrypt_key(ctx, len, key); + + return ctx; +} + + +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + struct aes_ctx *actx = ctx; + nettle_aes_decrypt(actx, AES_BLOCK_SIZE, plain, crypt); + return 0; +} + + +void aes_decrypt_deinit(void *ctx) +{ + struct aes_ctx *actx = ctx; + bin_clear_free(actx, sizeof(*actx)); +} + + +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey) +{ + size_t pubkey_len, pad; + + if (os_get_random(privkey, prime_len) < 0) + return -1; + if (os_memcmp(privkey, prime, prime_len) > 0) { + /* Make sure private value is smaller than prime */ + privkey[0] = 0; + } + + pubkey_len = prime_len; + if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len, + pubkey, &pubkey_len) < 0) + return -1; + if (pubkey_len < prime_len) { + pad = prime_len - pubkey_len; + os_memmove(pubkey + pad, pubkey, pubkey_len); + os_memset(pubkey, 0, pad); + } + + return 0; +} + + +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len) +{ + return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +} + + +int crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len) +{ + mpz_t bn_base, bn_exp, bn_modulus, bn_result; + int ret = -1; + size_t len; + + mpz_inits(bn_base, bn_exp, bn_modulus, bn_result, NULL); + mpz_import(bn_base, base_len, 1, 1, 1, 0, base); + mpz_import(bn_exp, power_len, 1, 1, 1, 0, power); + mpz_import(bn_modulus, modulus_len, 1, 1, 1, 0, modulus); + + mpz_powm(bn_result, bn_base, bn_exp, bn_modulus); + len = mpz_sizeinbase(bn_result, 2); + len = (len + 7) / 8; + if (*result_len < len) + goto error; + mpz_export(result, result_len, 1, 1, 1, 0, bn_result); + ret = 0; + +error: + mpz_clears(bn_base, bn_exp, bn_modulus, bn_result, NULL); + return ret; +} + + +struct crypto_cipher { + enum crypto_cipher_alg alg; + union { + struct arcfour_ctx arcfour_ctx; + } u; +}; + + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + ctx->alg = alg; + + switch (alg) { + case CRYPTO_CIPHER_ALG_RC4: + nettle_arcfour_set_key(&ctx->u.arcfour_ctx, key_len, key); + break; + default: + os_free(ctx); + return NULL; + } + + return ctx; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + switch (ctx->alg) { + case CRYPTO_CIPHER_ALG_RC4: + nettle_arcfour_crypt(&ctx->u.arcfour_ctx, len, crypt, plain); + break; + default: + return -1; + } + + return 0; +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + switch (ctx->alg) { + case CRYPTO_CIPHER_ALG_RC4: + nettle_arcfour_crypt(&ctx->u.arcfour_ctx, len, plain, crypt); + break; + default: + return -1; + } + + return 0; +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + bin_clear_free(ctx, sizeof(*ctx)); +} diff --git a/src/crypto/crypto_none.c b/src/crypto/crypto_none.c index 011f3f35a0551..547919418af99 100644 --- a/src/crypto/crypto_none.c +++ b/src/crypto/crypto_none.c @@ -18,6 +18,7 @@ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) } -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { + return 0; } diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index 19e0e2be87be3..f89053a89d67e 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -1,6 +1,6 @@ /* * Wrapper functions for OpenSSL libcrypto - * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -29,11 +29,14 @@ #include "sha1.h" #include "sha256.h" #include "sha384.h" +#include "sha512.h" #include "md5.h" #include "aes_wrap.h" #include "crypto.h" -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) /* Compatibility wrappers for older versions. */ static HMAC_CTX * HMAC_CTX_new(void) @@ -79,7 +82,9 @@ static void EVP_MD_CTX_free(EVP_MD_CTX *ctx) static BIGNUM * get_group5_prime(void) { -#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !(defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) return BN_get_rfc3526_prime_1536(NULL); #elif !defined(OPENSSL_IS_BORINGSSL) return get_rfc3526_prime_1536(NULL); @@ -109,6 +114,9 @@ static BIGNUM * get_group5_prime(void) #ifdef OPENSSL_NO_SHA256 #define NO_SHA256_WRAPPER #endif +#ifdef OPENSSL_NO_SHA512 +#define NO_SHA384_WRAPPER +#endif static int openssl_digest_vector(const EVP_MD *type, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) @@ -158,7 +166,7 @@ int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) #endif /* CONFIG_FIPS */ -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { u8 pkey[8], next, tmp; int i; @@ -176,6 +184,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) DES_set_key((DES_cblock *) &pkey, &ks); DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks, DES_ENCRYPT); + return 0; } @@ -243,15 +252,31 @@ int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, #endif /* NO_SHA256_WRAPPER */ +#ifndef NO_SHA384_WRAPPER +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return openssl_digest_vector(EVP_sha384(), num_elem, addr, len, mac); +} +#endif /* NO_SHA384_WRAPPER */ + + +#ifndef NO_SHA512_WRAPPER +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return openssl_digest_vector(EVP_sha512(), num_elem, addr, len, mac); +} +#endif /* NO_SHA512_WRAPPER */ + + static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen) { switch (keylen) { case 16: return EVP_aes_128_ecb(); -#ifndef OPENSSL_IS_BORINGSSL case 24: return EVP_aes_192_ecb(); -#endif /* OPENSSL_IS_BORINGSSL */ case 32: return EVP_aes_256_ecb(); } @@ -269,8 +294,11 @@ void * aes_encrypt_init(const u8 *key, size_t len) return NULL; type = aes_get_evp_cipher(len); - if (type == NULL) + if (!type) { + wpa_printf(MSG_INFO, "%s: Unsupported len=%u", + __func__, (unsigned int) len); return NULL; + } ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) @@ -284,14 +312,16 @@ void * aes_encrypt_init(const u8 *key, size_t len) } -void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) { EVP_CIPHER_CTX *c = ctx; int clen = 16; if (EVP_EncryptUpdate(c, crypt, &clen, plain, 16) != 1) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_EncryptUpdate failed: %s", ERR_error_string(ERR_get_error(), NULL)); + return -1; } + return 0; } @@ -321,8 +351,11 @@ void * aes_decrypt_init(const u8 *key, size_t len) return NULL; type = aes_get_evp_cipher(len); - if (type == NULL) + if (!type) { + wpa_printf(MSG_INFO, "%s: Unsupported len=%u", + __func__, (unsigned int) len); return NULL; + } ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) @@ -336,14 +369,16 @@ void * aes_decrypt_init(const u8 *key, size_t len) } -void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) { EVP_CIPHER_CTX *c = ctx; int plen = 16; if (EVP_DecryptUpdate(c, plain, &plen, crypt, 16) != 1) { wpa_printf(MSG_ERROR, "OpenSSL: EVP_DecryptUpdate failed: %s", ERR_error_string(ERR_get_error(), NULL)); + return -1; } + return 0; } @@ -372,6 +407,8 @@ int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher) AES_KEY actx; int res; + if (TEST_FAIL()) + return -1; if (AES_set_encrypt_key(kek, kek_len << 3, &actx)) return -1; res = AES_wrap_key(&actx, NULL, cipher, plain, n * 8); @@ -386,6 +423,8 @@ int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, AES_KEY actx; int res; + if (TEST_FAIL()) + return -1; if (AES_set_decrypt_key(kek, kek_len << 3, &actx)) return -1; res = AES_unwrap_key(&actx, NULL, plain, cipher, (n + 1) * 8); @@ -452,6 +491,42 @@ int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) } +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey) +{ + size_t pubkey_len, pad; + + if (os_get_random(privkey, prime_len) < 0) + return -1; + if (os_memcmp(privkey, prime, prime_len) > 0) { + /* Make sure private value is smaller than prime */ + privkey[0] = 0; + } + + pubkey_len = prime_len; + if (crypto_mod_exp(&generator, 1, privkey, prime_len, prime, prime_len, + pubkey, &pubkey_len) < 0) + return -1; + if (pubkey_len < prime_len) { + pad = prime_len - pubkey_len; + os_memmove(pubkey + pad, pubkey, pubkey_len); + os_memset(pubkey, 0, pad); + } + + return 0; +} + + +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len) +{ + return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len, + prime, prime_len, secret, len); +} + + int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, const u8 *modulus, size_t modulus_len, @@ -611,7 +686,9 @@ void crypto_cipher_deinit(struct crypto_cipher *ctx) void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) DH *dh; struct wpabuf *pubkey = NULL, *privkey = NULL; size_t publen, privlen; @@ -712,7 +789,9 @@ err: void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) DH *dh; dh = DH_new(); @@ -1016,7 +1095,7 @@ int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { return openssl_hmac_vector(EVP_sha384(), key, key_len, num_elem, addr, - len, mac, 32); + len, mac, 48); } @@ -1029,6 +1108,25 @@ int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, #endif /* CONFIG_SHA384 */ +#ifdef CONFIG_SHA512 + +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return openssl_hmac_vector(EVP_sha512(), key, key_len, num_elem, addr, + len, mac, 64); +} + + +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA512 */ + + int crypto_get_random(void *buf, size_t len) { if (RAND_bytes(buf, len) != 1) @@ -1150,6 +1248,12 @@ int crypto_bignum_to_bin(const struct crypto_bignum *a, } +int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m) +{ + return BN_rand_range((BIGNUM *) r, (const BIGNUM *) m) == 1 ? 0 : -1; +} + + int crypto_bignum_add(const struct crypto_bignum *a, const struct crypto_bignum *b, struct crypto_bignum *c) @@ -1275,6 +1379,15 @@ int crypto_bignum_mulmod(const struct crypto_bignum *a, } +int crypto_bignum_rshift(const struct crypto_bignum *a, int n, + struct crypto_bignum *r) +{ + /* Note: BN_rshift() does not modify the first argument even though it + * has not been marked const. */ + return BN_rshift((BIGNUM *) a, (BIGNUM *) r, n) == 1 ? 0 : -1; +} + + int crypto_bignum_cmp(const struct crypto_bignum *a, const struct crypto_bignum *b) { @@ -1300,6 +1413,12 @@ int crypto_bignum_is_one(const struct crypto_bignum *a) } +int crypto_bignum_is_odd(const struct crypto_bignum *a) +{ + return BN_is_odd((const BIGNUM *) a); +} + + int crypto_bignum_legendre(const struct crypto_bignum *a, const struct crypto_bignum *p) { @@ -1343,6 +1462,7 @@ fail: struct crypto_ec { EC_GROUP *group; + int nid; BN_CTX *bnctx; BIGNUM *prime; BIGNUM *order; @@ -1400,6 +1520,7 @@ struct crypto_ec * crypto_ec_init(int group) if (e == NULL) return NULL; + e->nid = nid; e->bnctx = BN_CTX_new(); e->group = EC_GROUP_new_by_curve_name(nid); e->prime = BN_new(); @@ -1432,6 +1553,13 @@ void crypto_ec_deinit(struct crypto_ec *e) } +int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor) +{ + return EC_GROUP_get_cofactor(e->group, (BIGNUM *) cofactor, + e->bnctx) == 0 ? -1 : 0; +} + + struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) { if (TEST_FAIL()) @@ -1454,6 +1582,12 @@ size_t crypto_ec_prime_len_bits(struct crypto_ec *e) } +size_t crypto_ec_order_len(struct crypto_ec *e) +{ + return BN_num_bytes(e->order); +} + + const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e) { return (const struct crypto_bignum *) e->prime; @@ -1475,6 +1609,16 @@ void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear) } +int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p, + struct crypto_bignum *x) +{ + return EC_POINT_get_affine_coordinates_GFp(e->group, + (const EC_POINT *) p, + (BIGNUM *) x, NULL, + e->bnctx) == 1 ? 0 : -1; +} + + int crypto_ec_point_to_bin(struct crypto_ec *e, const struct crypto_ec_point *point, u8 *x, u8 *y) { @@ -1640,4 +1784,228 @@ int crypto_ec_point_cmp(const struct crypto_ec *e, (const EC_POINT *) b, e->bnctx); } + +struct crypto_ecdh { + struct crypto_ec *ec; + EVP_PKEY *pkey; +}; + +struct crypto_ecdh * crypto_ecdh_init(int group) +{ + struct crypto_ecdh *ecdh; + EVP_PKEY *params = NULL; + EC_KEY *ec_params; + EVP_PKEY_CTX *kctx = NULL; + + ecdh = os_zalloc(sizeof(*ecdh)); + if (!ecdh) + goto fail; + + ecdh->ec = crypto_ec_init(group); + if (!ecdh->ec) + goto fail; + + ec_params = EC_KEY_new_by_curve_name(ecdh->ec->nid); + if (!ec_params) { + wpa_printf(MSG_ERROR, + "OpenSSL: Failed to generate EC_KEY parameters"); + goto fail; + } + EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE); + params = EVP_PKEY_new(); + if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: Failed to generate EVP_PKEY parameters"); + goto fail; + } + + kctx = EVP_PKEY_CTX_new(params, NULL); + if (!kctx) + goto fail; + + if (EVP_PKEY_keygen_init(kctx) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EVP_PKEY_keygen_init failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (EVP_PKEY_keygen(kctx, &ecdh->pkey) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_PKEY_keygen failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + +done: + EVP_PKEY_free(params); + EVP_PKEY_CTX_free(kctx); + + return ecdh; +fail: + crypto_ecdh_deinit(ecdh); + ecdh = NULL; + goto done; +} + + +struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y) +{ + struct wpabuf *buf = NULL; + EC_KEY *eckey; + const EC_POINT *pubkey; + BIGNUM *x, *y = NULL; + int len = BN_num_bytes(ecdh->ec->prime); + int res; + + eckey = EVP_PKEY_get1_EC_KEY(ecdh->pkey); + if (!eckey) + return NULL; + + pubkey = EC_KEY_get0_public_key(eckey); + if (!pubkey) + return NULL; + + x = BN_new(); + if (inc_y) { + y = BN_new(); + if (!y) + goto fail; + } + buf = wpabuf_alloc(inc_y ? 2 * len : len); + if (!x || !buf) + goto fail; + + if (EC_POINT_get_affine_coordinates_GFp(ecdh->ec->group, pubkey, + x, y, ecdh->ec->bnctx) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EC_POINT_get_affine_coordinates_GFp failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + res = crypto_bignum_to_bin((struct crypto_bignum *) x, + wpabuf_put(buf, len), len, len); + if (res < 0) + goto fail; + + if (inc_y) { + res = crypto_bignum_to_bin((struct crypto_bignum *) y, + wpabuf_put(buf, len), len, len); + if (res < 0) + goto fail; + } + +done: + BN_clear_free(x); + BN_clear_free(y); + EC_KEY_free(eckey); + + return buf; +fail: + wpabuf_free(buf); + buf = NULL; + goto done; +} + + +struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y, + const u8 *key, size_t len) +{ + BIGNUM *x, *y = NULL; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *peerkey = NULL; + struct wpabuf *secret = NULL; + size_t secret_len; + EC_POINT *pub; + EC_KEY *eckey = NULL; + + x = BN_bin2bn(key, inc_y ? len / 2 : len, NULL); + pub = EC_POINT_new(ecdh->ec->group); + if (!x || !pub) + goto fail; + + if (inc_y) { + y = BN_bin2bn(key + len / 2, len / 2, NULL); + if (!y) + goto fail; + if (!EC_POINT_set_affine_coordinates_GFp(ecdh->ec->group, pub, + x, y, + ecdh->ec->bnctx)) { + wpa_printf(MSG_ERROR, + "OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + } else if (!EC_POINT_set_compressed_coordinates_GFp(ecdh->ec->group, + pub, x, 0, + ecdh->ec->bnctx)) { + wpa_printf(MSG_ERROR, + "OpenSSL: EC_POINT_set_compressed_coordinates_GFp failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (!EC_POINT_is_on_curve(ecdh->ec->group, pub, ecdh->ec->bnctx)) { + wpa_printf(MSG_ERROR, + "OpenSSL: ECDH peer public key is not on curve"); + goto fail; + } + + eckey = EC_KEY_new_by_curve_name(ecdh->ec->nid); + if (!eckey || EC_KEY_set_public_key(eckey, pub) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EC_KEY_set_public_key failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + peerkey = EVP_PKEY_new(); + if (!peerkey || EVP_PKEY_set1_EC_KEY(peerkey, eckey) != 1) + goto fail; + + ctx = EVP_PKEY_CTX_new(ecdh->pkey, NULL); + if (!ctx || EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, peerkey) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EVP_PKEY_derive(1) failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + secret = wpabuf_alloc(secret_len); + if (!secret) + goto fail; + if (EVP_PKEY_derive(ctx, wpabuf_put(secret, secret_len), + &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: EVP_PKEY_derive(2) failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + +done: + BN_free(x); + BN_free(y); + EC_KEY_free(eckey); + EC_POINT_free(pub); + EVP_PKEY_CTX_free(ctx); + EVP_PKEY_free(peerkey); + return secret; +fail: + wpabuf_free(secret); + secret = NULL; + goto done; +} + + +void crypto_ecdh_deinit(struct crypto_ecdh *ecdh) +{ + if (ecdh) { + crypto_ec_deinit(ecdh->ec); + EVP_PKEY_free(ecdh->pkey); + os_free(ecdh); + } +} + #endif /* CONFIG_ECC */ diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c new file mode 100644 index 0000000000000..b5a1e3fa31bc1 --- /dev/null +++ b/src/crypto/crypto_wolfssl.c @@ -0,0 +1,1791 @@ +/* + * Wrapper functions for libwolfssl + * Copyright (c) 2004-2017, 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 "common.h" +#include "crypto.h" + +/* wolfSSL headers */ +#include <wolfssl/options.h> +#include <wolfssl/wolfcrypt/md4.h> +#include <wolfssl/wolfcrypt/md5.h> +#include <wolfssl/wolfcrypt/sha.h> +#include <wolfssl/wolfcrypt/sha256.h> +#include <wolfssl/wolfcrypt/sha512.h> +#include <wolfssl/wolfcrypt/hmac.h> +#include <wolfssl/wolfcrypt/pwdbased.h> +#include <wolfssl/wolfcrypt/arc4.h> +#include <wolfssl/wolfcrypt/des3.h> +#include <wolfssl/wolfcrypt/aes.h> +#include <wolfssl/wolfcrypt/dh.h> +#include <wolfssl/wolfcrypt/cmac.h> +#include <wolfssl/wolfcrypt/ecc.h> +#include <wolfssl/openssl/bn.h> + + +#ifndef CONFIG_FIPS + +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + Md4 md4; + size_t i; + + if (TEST_FAIL()) + return -1; + + wc_InitMd4(&md4); + + for (i = 0; i < num_elem; i++) + wc_Md4Update(&md4, addr[i], len[i]); + + wc_Md4Final(&md4, mac); + + return 0; +} + + +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + wc_Md5 md5; + size_t i; + + if (TEST_FAIL()) + return -1; + + wc_InitMd5(&md5); + + for (i = 0; i < num_elem; i++) + wc_Md5Update(&md5, addr[i], len[i]); + + wc_Md5Final(&md5, mac); + + return 0; +} + +#endif /* CONFIG_FIPS */ + + +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + wc_Sha sha; + size_t i; + + if (TEST_FAIL()) + return -1; + + wc_InitSha(&sha); + + for (i = 0; i < num_elem; i++) + wc_ShaUpdate(&sha, addr[i], len[i]); + + wc_ShaFinal(&sha, mac); + + return 0; +} + + +#ifndef NO_SHA256_WRAPPER +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + wc_Sha256 sha256; + size_t i; + + if (TEST_FAIL()) + return -1; + + wc_InitSha256(&sha256); + + for (i = 0; i < num_elem; i++) + wc_Sha256Update(&sha256, addr[i], len[i]); + + wc_Sha256Final(&sha256, mac); + + return 0; +} +#endif /* NO_SHA256_WRAPPER */ + + +#ifdef CONFIG_SHA384 +int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + wc_Sha384 sha384; + size_t i; + + if (TEST_FAIL()) + return -1; + + wc_InitSha384(&sha384); + + for (i = 0; i < num_elem; i++) + wc_Sha384Update(&sha384, addr[i], len[i]); + + wc_Sha384Final(&sha384, mac); + + return 0; +} +#endif /* CONFIG_SHA384 */ + + +#ifdef CONFIG_SHA512 +int sha512_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + wc_Sha512 sha512; + size_t i; + + if (TEST_FAIL()) + return -1; + + wc_InitSha512(&sha512); + + for (i = 0; i < num_elem; i++) + wc_Sha512Update(&sha512, addr[i], len[i]); + + wc_Sha512Final(&sha512, mac); + + return 0; +} +#endif /* CONFIG_SHA512 */ + + +static int wolfssl_hmac_vector(int type, const u8 *key, + size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac, + unsigned int mdlen) +{ + Hmac hmac; + size_t i; + + (void) mdlen; + + if (TEST_FAIL()) + return -1; + + if (wc_HmacSetKey(&hmac, type, key, (word32) key_len) != 0) + return -1; + for (i = 0; i < num_elem; i++) + if (wc_HmacUpdate(&hmac, addr[i], len[i]) != 0) + return -1; + if (wc_HmacFinal(&hmac, mac) != 0) + return -1; + return 0; +} + + +#ifndef CONFIG_FIPS + +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return wolfssl_hmac_vector(WC_MD5, key, key_len, num_elem, addr, len, + mac, 16); +} + + +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_FIPS */ + + +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return wolfssl_hmac_vector(WC_SHA, key, key_len, num_elem, addr, len, + mac, 20); +} + + +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac) +{ + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); +} + + +#ifdef CONFIG_SHA256 + +int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return wolfssl_hmac_vector(WC_SHA256, key, key_len, num_elem, addr, len, + mac, 32); +} + + +int hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA256 */ + + +#ifdef CONFIG_SHA384 + +int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return wolfssl_hmac_vector(WC_SHA384, key, key_len, num_elem, addr, len, + mac, 48); +} + + +int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA384 */ + + +#ifdef CONFIG_SHA512 + +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return wolfssl_hmac_vector(WC_SHA512, key, key_len, num_elem, addr, len, + mac, 64); +} + + +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac); +} + +#endif /* CONFIG_SHA512 */ + + +int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen) +{ + if (wc_PBKDF2(buf, (const byte*)passphrase, os_strlen(passphrase), ssid, + ssid_len, iterations, buflen, WC_SHA) != 0) + return -1; + return 0; +} + + +#ifdef CONFIG_DES +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + Des des; + u8 pkey[8], next, tmp; + int i; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + wc_Des_SetKey(&des, pkey, NULL, DES_ENCRYPTION); + wc_Des_EcbEncrypt(&des, cypher, clear, DES_BLOCK_SIZE); + + return 0; +} +#endif /* CONFIG_DES */ + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + Aes *aes; + + if (TEST_FAIL()) + return NULL; + + aes = os_malloc(sizeof(Aes)); + if (!aes) + return NULL; + + if (wc_AesSetKey(aes, key, len, NULL, AES_ENCRYPTION) < 0) { + os_free(aes); + return NULL; + } + + return aes; +} + + +int aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + wc_AesEncryptDirect(ctx, crypt, plain); + return 0; +} + + +void aes_encrypt_deinit(void *ctx) +{ + os_free(ctx); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + Aes *aes; + + if (TEST_FAIL()) + return NULL; + + aes = os_malloc(sizeof(Aes)); + if (!aes) + return NULL; + + if (wc_AesSetKey(aes, key, len, NULL, AES_DECRYPTION) < 0) { + os_free(aes); + return NULL; + } + + return aes; +} + + +int aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + wc_AesDecryptDirect(ctx, plain, crypt); + return 0; +} + + +void aes_decrypt_deinit(void *ctx) +{ + os_free(ctx); +} + + +int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + Aes aes; + int ret; + + if (TEST_FAIL()) + return -1; + + ret = wc_AesSetKey(&aes, key, 16, iv, AES_ENCRYPTION); + if (ret != 0) + return -1; + + ret = wc_AesCbcEncrypt(&aes, data, data, data_len); + if (ret != 0) + return -1; + return 0; +} + + +int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) +{ + Aes aes; + int ret; + + if (TEST_FAIL()) + return -1; + + ret = wc_AesSetKey(&aes, key, 16, iv, AES_DECRYPTION); + if (ret != 0) + return -1; + + ret = wc_AesCbcDecrypt(&aes, data, data, data_len); + if (ret != 0) + return -1; + return 0; +} + + +int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher) +{ + int ret; + + if (TEST_FAIL()) + return -1; + + ret = wc_AesKeyWrap(kek, kek_len, plain, n * 8, cipher, (n + 1) * 8, + NULL); + return ret != (n + 1) * 8 ? -1 : 0; +} + + +int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher, + u8 *plain) +{ + int ret; + + if (TEST_FAIL()) + return -1; + + ret = wc_AesKeyUnWrap(kek, kek_len, cipher, (n + 1) * 8, plain, n * 8, + NULL); + return ret != n * 8 ? -1 : 0; +} + + +#ifndef CONFIG_NO_RC4 +int rc4_skip(const u8 *key, size_t keylen, size_t skip, u8 *data, + size_t data_len) +{ +#ifndef NO_RC4 + Arc4 arc4; + unsigned char skip_buf[16]; + + wc_Arc4SetKey(&arc4, key, keylen); + + while (skip >= sizeof(skip_buf)) { + size_t len = skip; + + if (len > sizeof(skip_buf)) + len = sizeof(skip_buf); + wc_Arc4Process(&arc4, skip_buf, skip_buf, len); + skip -= len; + } + + wc_Arc4Process(&arc4, data, data, data_len); + + return 0; +#else /* NO_RC4 */ + return -1; +#endif /* NO_RC4 */ +} +#endif /* CONFIG_NO_RC4 */ + + +#if defined(EAP_IKEV2) || defined(EAP_IKEV2_DYNAMIC) \ + || defined(EAP_SERVER_IKEV2) +union wolfssl_cipher { + Aes aes; + Des3 des3; + Arc4 arc4; +}; + +struct crypto_cipher { + enum crypto_cipher_alg alg; + union wolfssl_cipher enc; + union wolfssl_cipher dec; +}; + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + switch (alg) { +#ifndef CONFIG_NO_RC4 +#ifndef NO_RC4 + case CRYPTO_CIPHER_ALG_RC4: + wc_Arc4SetKey(&ctx->enc.arc4, key, key_len); + wc_Arc4SetKey(&ctx->dec.arc4, key, key_len); + break; +#endif /* NO_RC4 */ +#endif /* CONFIG_NO_RC4 */ +#ifndef NO_AES + case CRYPTO_CIPHER_ALG_AES: + switch (key_len) { + case 16: + case 24: + case 32: + break; + default: + os_free(ctx); + return NULL; + } + if (wc_AesSetKey(&ctx->enc.aes, key, key_len, iv, + AES_ENCRYPTION) || + wc_AesSetKey(&ctx->dec.aes, key, key_len, iv, + AES_DECRYPTION)) { + os_free(ctx); + return NULL; + } + break; +#endif /* NO_AES */ +#ifndef NO_DES3 + case CRYPTO_CIPHER_ALG_3DES: + if (key_len != DES3_KEYLEN || + wc_Des3_SetKey(&ctx->enc.des3, key, iv, DES_ENCRYPTION) || + wc_Des3_SetKey(&ctx->dec.des3, key, iv, DES_DECRYPTION)) { + os_free(ctx); + return NULL; + } + break; +#endif /* NO_DES3 */ + case CRYPTO_CIPHER_ALG_RC2: + case CRYPTO_CIPHER_ALG_DES: + default: + os_free(ctx); + return NULL; + } + + ctx->alg = alg; + + return ctx; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + switch (ctx->alg) { +#ifndef CONFIG_NO_RC4 +#ifndef NO_RC4 + case CRYPTO_CIPHER_ALG_RC4: + wc_Arc4Process(&ctx->enc.arc4, crypt, plain, len); + return 0; +#endif /* NO_RC4 */ +#endif /* CONFIG_NO_RC4 */ +#ifndef NO_AES + case CRYPTO_CIPHER_ALG_AES: + if (wc_AesCbcEncrypt(&ctx->enc.aes, crypt, plain, len) != 0) + return -1; + return 0; +#endif /* NO_AES */ +#ifndef NO_DES3 + case CRYPTO_CIPHER_ALG_3DES: + if (wc_Des3_CbcEncrypt(&ctx->enc.des3, crypt, plain, len) != 0) + return -1; + return 0; +#endif /* NO_DES3 */ + default: + return -1; + } + return -1; +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + switch (ctx->alg) { +#ifndef CONFIG_NO_RC4 +#ifndef NO_RC4 + case CRYPTO_CIPHER_ALG_RC4: + wc_Arc4Process(&ctx->dec.arc4, plain, crypt, len); + return 0; +#endif /* NO_RC4 */ +#endif /* CONFIG_NO_RC4 */ +#ifndef NO_AES + case CRYPTO_CIPHER_ALG_AES: + if (wc_AesCbcDecrypt(&ctx->dec.aes, plain, crypt, len) != 0) + return -1; + return 0; +#endif /* NO_AES */ +#ifndef NO_DES3 + case CRYPTO_CIPHER_ALG_3DES: + if (wc_Des3_CbcDecrypt(&ctx->dec.des3, plain, crypt, len) != 0) + return -1; + return 0; +#endif /* NO_DES3 */ + default: + return -1; + } + return -1; +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + os_free(ctx); +} + +#endif + + +#ifdef CONFIG_WPS_NFC + +static const unsigned char RFC3526_PRIME_1536[] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, + 0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6, + 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD, + 0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, + 0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, + 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9, + 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED, + 0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, + 0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, + 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36, + 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F, + 0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, + 0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, + 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08, + 0xCA, 0x23, 0x73, 0x27, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static const unsigned char RFC3526_GENERATOR_1536[] = { + 0x02 +}; + +#define RFC3526_LEN sizeof(RFC3526_PRIME_1536) + + +void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) +{ + WC_RNG rng; + DhKey *ret = NULL; + DhKey *dh = NULL; + struct wpabuf *privkey = NULL; + struct wpabuf *pubkey = NULL; + word32 priv_sz, pub_sz; + + *priv = NULL; + wpabuf_free(*publ); + *publ = NULL; + + dh = XMALLOC(sizeof(DhKey), NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (!dh) + return NULL; + wc_InitDhKey(dh); + + if (wc_InitRng(&rng) != 0) { + XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return NULL; + } + + privkey = wpabuf_alloc(RFC3526_LEN); + pubkey = wpabuf_alloc(RFC3526_LEN); + if (!privkey || !pubkey) + goto done; + + if (wc_DhSetKey(dh, RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), + RFC3526_GENERATOR_1536, sizeof(RFC3526_GENERATOR_1536)) + != 0) + goto done; + + if (wc_DhGenerateKeyPair(dh, &rng, wpabuf_mhead(privkey), &priv_sz, + wpabuf_mhead(pubkey), &pub_sz) != 0) + goto done; + + wpabuf_put(privkey, priv_sz); + wpabuf_put(pubkey, pub_sz); + + ret = dh; + *priv = privkey; + *publ = pubkey; + dh = NULL; + privkey = NULL; + pubkey = NULL; +done: + wpabuf_clear_free(pubkey); + wpabuf_clear_free(privkey); + if (dh) { + wc_FreeDhKey(dh); + XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + wc_FreeRng(&rng); + return ret; +} + + +void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ) +{ + DhKey *ret = NULL; + DhKey *dh; + byte *secret; + word32 secret_sz; + + dh = XMALLOC(sizeof(DhKey), NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (!dh) + return NULL; + wc_InitDhKey(dh); + + secret = XMALLOC(RFC3526_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (!secret) + goto done; + + if (wc_DhSetKey(dh, RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), + RFC3526_GENERATOR_1536, sizeof(RFC3526_GENERATOR_1536)) + != 0) + goto done; + + if (wc_DhAgree(dh, secret, &secret_sz, wpabuf_head(priv), + wpabuf_len(priv), RFC3526_GENERATOR_1536, + sizeof(RFC3526_GENERATOR_1536)) != 0) + goto done; + + if (secret_sz != wpabuf_len(publ) || + os_memcmp(secret, wpabuf_head(publ), secret_sz) != 0) + goto done; + + ret = dh; + dh = NULL; +done: + if (dh) { + wc_FreeDhKey(dh); + XFREE(dh, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + XFREE(secret, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return ret; +} + + +struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, + const struct wpabuf *own_private) +{ + struct wpabuf *ret = NULL; + struct wpabuf *secret; + word32 secret_sz; + + secret = wpabuf_alloc(RFC3526_LEN); + if (!secret) + goto done; + + if (wc_DhAgree(ctx, wpabuf_mhead(secret), &secret_sz, + wpabuf_head(own_private), wpabuf_len(own_private), + wpabuf_head(peer_public), wpabuf_len(peer_public)) != 0) + goto done; + + wpabuf_put(secret, secret_sz); + + ret = secret; + secret = NULL; +done: + wpabuf_clear_free(secret); + return ret; +} + + +void dh5_free(void *ctx) +{ + if (!ctx) + return; + + wc_FreeDhKey(ctx); + XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); +} + +#endif /* CONFIG_WPS_NFC */ + + +int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey, + u8 *pubkey) +{ + int ret = -1; + WC_RNG rng; + DhKey *dh = NULL; + word32 priv_sz, pub_sz; + + if (TEST_FAIL()) + return -1; + + dh = os_malloc(sizeof(DhKey)); + if (!dh) + return -1; + wc_InitDhKey(dh); + + if (wc_InitRng(&rng) != 0) { + os_free(dh); + return -1; + } + + if (wc_DhSetKey(dh, prime, prime_len, &generator, 1) != 0) + goto done; + + if (wc_DhGenerateKeyPair(dh, &rng, privkey, &priv_sz, pubkey, &pub_sz) + != 0) + goto done; + + if (priv_sz < prime_len) { + size_t pad_sz = prime_len - priv_sz; + + os_memmove(privkey + pad_sz, privkey, priv_sz); + os_memset(privkey, 0, pad_sz); + } + + if (pub_sz < prime_len) { + size_t pad_sz = prime_len - pub_sz; + + os_memmove(pubkey + pad_sz, pubkey, pub_sz); + os_memset(pubkey, 0, pad_sz); + } + ret = 0; +done: + wc_FreeDhKey(dh); + os_free(dh); + wc_FreeRng(&rng); + return ret; +} + + +int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len, + const u8 *privkey, size_t privkey_len, + const u8 *pubkey, size_t pubkey_len, + u8 *secret, size_t *len) +{ + int ret = -1; + DhKey *dh; + word32 secret_sz; + + dh = os_malloc(sizeof(DhKey)); + if (!dh) + return -1; + wc_InitDhKey(dh); + + if (wc_DhSetKey(dh, prime, prime_len, &generator, 1) != 0) + goto done; + + if (wc_DhAgree(dh, secret, &secret_sz, privkey, privkey_len, pubkey, + pubkey_len) != 0) + goto done; + + *len = secret_sz; + ret = 0; +done: + wc_FreeDhKey(dh); + os_free(dh); + return ret; +} + + +#ifdef CONFIG_FIPS +int crypto_get_random(void *buf, size_t len) +{ + int ret = 0; + WC_RNG rng; + + if (wc_InitRng(&rng) != 0) + return -1; + if (wc_RNG_GenerateBlock(&rng, buf, len) != 0) + ret = -1; + wc_FreeRng(&rng); + return ret; +} +#endif /* CONFIG_FIPS */ + + +#if defined(EAP_PWD) || defined(EAP_SERVER_PWD) +struct crypto_hash { + Hmac hmac; + int size; +}; + + +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len) +{ + struct crypto_hash *ret = NULL; + struct crypto_hash *hash; + int type; + + hash = os_zalloc(sizeof(*hash)); + if (!hash) + goto done; + + switch (alg) { +#ifndef NO_MD5 + case CRYPTO_HASH_ALG_HMAC_MD5: + hash->size = 16; + type = WC_MD5; + break; +#endif /* NO_MD5 */ +#ifndef NO_SHA + case CRYPTO_HASH_ALG_HMAC_SHA1: + type = WC_SHA; + hash->size = 20; + break; +#endif /* NO_SHA */ +#ifdef CONFIG_SHA256 +#ifndef NO_SHA256 + case CRYPTO_HASH_ALG_HMAC_SHA256: + type = WC_SHA256; + hash->size = 32; + break; +#endif /* NO_SHA256 */ +#endif /* CONFIG_SHA256 */ + default: + goto done; + } + + if (wc_HmacSetKey(&hash->hmac, type, key, key_len) != 0) + goto done; + + ret = hash; + hash = NULL; +done: + os_free(hash); + return ret; +} + + +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len) +{ + if (!ctx) + return; + wc_HmacUpdate(&ctx->hmac, data, len); +} + + +int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) +{ + int ret = 0; + + if (!ctx) + return -2; + + if (!mac || !len) + goto done; + + if (wc_HmacFinal(&ctx->hmac, mac) != 0) { + ret = -1; + goto done; + } + + *len = ctx->size; + ret = 0; +done: + bin_clear_free(ctx, sizeof(*ctx)); + return ret; +} + +#endif + + +int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + Cmac cmac; + size_t i; + word32 sz; + + if (TEST_FAIL()) + return -1; + + if (wc_InitCmac(&cmac, key, key_len, WC_CMAC_AES, NULL) != 0) + return -1; + + for (i = 0; i < num_elem; i++) + if (wc_CmacUpdate(&cmac, addr[i], len[i]) != 0) + return -1; + + sz = AES_BLOCK_SIZE; + if (wc_CmacFinal(&cmac, mac, &sz) != 0 || sz != AES_BLOCK_SIZE) + return -1; + + return 0; +} + + +int omac1_aes_128_vector(const u8 *key, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + return omac1_aes_vector(key, 16, num_elem, addr, len, mac); +} + + +int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_128_vector(key, 1, &data, &data_len, mac); +} + + +int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_vector(key, 32, 1, &data, &data_len, mac); +} + + +struct crypto_bignum * crypto_bignum_init(void) +{ + mp_int *a; + + if (TEST_FAIL()) + return NULL; + + a = os_malloc(sizeof(*a)); + if (!a || mp_init(a) != MP_OKAY) { + os_free(a); + a = NULL; + } + + return (struct crypto_bignum *) a; +} + + +struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len) +{ + mp_int *a; + + if (TEST_FAIL()) + return NULL; + + a = (mp_int *) crypto_bignum_init(); + if (!a) + return NULL; + + if (mp_read_unsigned_bin(a, buf, len) != MP_OKAY) { + os_free(a); + a = NULL; + } + + return (struct crypto_bignum *) a; +} + + +void crypto_bignum_deinit(struct crypto_bignum *n, int clear) +{ + if (!n) + return; + + if (clear) + mp_forcezero((mp_int *) n); + mp_clear((mp_int *) n); + os_free((mp_int *) n); +} + + +int crypto_bignum_to_bin(const struct crypto_bignum *a, + u8 *buf, size_t buflen, size_t padlen) +{ + int num_bytes, offset; + + if (TEST_FAIL()) + return -1; + + if (padlen > buflen) + return -1; + + num_bytes = (mp_count_bits((mp_int *) a) + 7) / 8; + if ((size_t) num_bytes > buflen) + return -1; + if (padlen > (size_t) num_bytes) + offset = padlen - num_bytes; + else + offset = 0; + + os_memset(buf, 0, offset); + mp_to_unsigned_bin((mp_int *) a, buf + offset); + + return num_bytes + offset; +} + + +int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m) +{ + int ret = 0; + WC_RNG rng; + + if (wc_InitRng(&rng) != 0) + return -1; + if (mp_rand_prime((mp_int *) r, + (mp_count_bits((mp_int *) m) + 7) / 8 * 2, + &rng, NULL) != 0) + ret = -1; + if (ret == 0 && + mp_mod((mp_int *) r, (mp_int *) m, (mp_int *) r) != 0) + ret = -1; + wc_FreeRng(&rng); + return ret; +} + + +int crypto_bignum_add(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *r) +{ + return mp_add((mp_int *) a, (mp_int *) b, + (mp_int *) r) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_mod(const struct crypto_bignum *a, + const struct crypto_bignum *m, + struct crypto_bignum *r) +{ + return mp_mod((mp_int *) a, (mp_int *) m, + (mp_int *) r) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_exptmod(const struct crypto_bignum *b, + const struct crypto_bignum *e, + const struct crypto_bignum *m, + struct crypto_bignum *r) +{ + if (TEST_FAIL()) + return -1; + + return mp_exptmod((mp_int *) b, (mp_int *) e, (mp_int *) m, + (mp_int *) r) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_inverse(const struct crypto_bignum *a, + const struct crypto_bignum *m, + struct crypto_bignum *r) +{ + if (TEST_FAIL()) + return -1; + + return mp_invmod((mp_int *) a, (mp_int *) m, + (mp_int *) r) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_sub(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *r) +{ + if (TEST_FAIL()) + return -1; + + return mp_add((mp_int *) a, (mp_int *) b, + (mp_int *) r) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_div(const struct crypto_bignum *a, + const struct crypto_bignum *b, + struct crypto_bignum *d) +{ + if (TEST_FAIL()) + return -1; + + return mp_div((mp_int *) a, (mp_int *) b, (mp_int *) d, + NULL) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_mulmod(const struct crypto_bignum *a, + const struct crypto_bignum *b, + const struct crypto_bignum *m, + struct crypto_bignum *d) +{ + if (TEST_FAIL()) + return -1; + + return mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) m, + (mp_int *) d) == MP_OKAY ? 0 : -1; +} + + +int crypto_bignum_rshift(const struct crypto_bignum *a, int n, + struct crypto_bignum *r) +{ + if (mp_copy((mp_int *) a, (mp_int *) r) != MP_OKAY) + return -1; + mp_rshb((mp_int *) r, n); + return 0; +} + + +int crypto_bignum_cmp(const struct crypto_bignum *a, + const struct crypto_bignum *b) +{ + return mp_cmp((mp_int *) a, (mp_int *) b); +} + + +int crypto_bignum_bits(const struct crypto_bignum *a) +{ + return mp_count_bits((mp_int *) a); +} + + +int crypto_bignum_is_zero(const struct crypto_bignum *a) +{ + return mp_iszero((mp_int *) a); +} + + +int crypto_bignum_is_one(const struct crypto_bignum *a) +{ + return mp_isone((const mp_int *) a); +} + +int crypto_bignum_is_odd(const struct crypto_bignum *a) +{ + return mp_isodd((mp_int *) a); +} + + +int crypto_bignum_legendre(const struct crypto_bignum *a, + const struct crypto_bignum *p) +{ + mp_int t; + int ret; + int res = -2; + + if (TEST_FAIL()) + return -2; + + if (mp_init(&t) != MP_OKAY) + return -2; + + /* t = (p-1) / 2 */ + ret = mp_sub_d((mp_int *) p, 1, &t); + if (ret == MP_OKAY) + mp_rshb(&t, 1); + if (ret == MP_OKAY) + ret = mp_exptmod((mp_int *) a, &t, (mp_int *) p, &t); + if (ret == MP_OKAY) { + if (mp_isone(&t)) + res = 1; + else if (mp_iszero(&t)) + res = 0; + else + res = -1; + } + + mp_clear(&t); + return res; +} + + +#ifdef CONFIG_ECC + +int ecc_map(ecc_point *, mp_int *, mp_digit); +int ecc_projective_add_point(ecc_point *P, ecc_point *Q, ecc_point *R, + mp_int *a, mp_int *modulus, mp_digit mp); + +struct crypto_ec { + ecc_key key; + mp_int a; + mp_int prime; + mp_int order; + mp_digit mont_b; + mp_int b; +}; + + +struct crypto_ec * crypto_ec_init(int group) +{ + int built = 0; + struct crypto_ec *e; + int curve_id; + + /* Map from IANA registry for IKE D-H groups to OpenSSL NID */ + switch (group) { + case 19: + curve_id = ECC_SECP256R1; + break; + case 20: + curve_id = ECC_SECP384R1; + break; + case 21: + curve_id = ECC_SECP521R1; + break; + case 25: + curve_id = ECC_SECP192R1; + break; + case 26: + curve_id = ECC_SECP224R1; + break; +#ifdef HAVE_ECC_BRAINPOOL + case 27: + curve_id = ECC_BRAINPOOLP224R1; + break; + case 28: + curve_id = ECC_BRAINPOOLP256R1; + break; + case 29: + curve_id = ECC_BRAINPOOLP384R1; + break; + case 30: + curve_id = ECC_BRAINPOOLP512R1; + break; +#endif /* HAVE_ECC_BRAINPOOL */ + default: + return NULL; + } + + e = os_zalloc(sizeof(*e)); + if (!e) + return NULL; + + if (wc_ecc_init(&e->key) != 0 || + wc_ecc_set_curve(&e->key, 0, curve_id) != 0 || + mp_init(&e->a) != MP_OKAY || + mp_init(&e->prime) != MP_OKAY || + mp_init(&e->order) != MP_OKAY || + mp_init(&e->b) != MP_OKAY || + mp_read_radix(&e->a, e->key.dp->Af, 16) != MP_OKAY || + mp_read_radix(&e->b, e->key.dp->Bf, 16) != MP_OKAY || + mp_read_radix(&e->prime, e->key.dp->prime, 16) != MP_OKAY || + mp_read_radix(&e->order, e->key.dp->order, 16) != MP_OKAY || + mp_montgomery_setup(&e->prime, &e->mont_b) != MP_OKAY) + goto done; + + built = 1; +done: + if (!built) { + crypto_ec_deinit(e); + e = NULL; + } + return e; +} + + +void crypto_ec_deinit(struct crypto_ec* e) +{ + if (!e) + return; + + mp_clear(&e->b); + mp_clear(&e->order); + mp_clear(&e->prime); + mp_clear(&e->a); + wc_ecc_free(&e->key); + os_free(e); +} + + +int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor) +{ + if (!e || !cofactor) + return -1; + + mp_set((mp_int *) cofactor, e->key.dp->cofactor); + return 0; +} + + +struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e) +{ + if (TEST_FAIL()) + return NULL; + if (!e) + return NULL; + return (struct crypto_ec_point *) wc_ecc_new_point(); +} + + +size_t crypto_ec_prime_len(struct crypto_ec *e) +{ + return (mp_count_bits(&e->prime) + 7) / 8; +} + + +size_t crypto_ec_prime_len_bits(struct crypto_ec *e) +{ + return mp_count_bits(&e->prime); +} + + +size_t crypto_ec_order_len(struct crypto_ec *e) +{ + return (mp_count_bits(&e->order) + 7) / 8; +} + + +const struct crypto_bignum * crypto_ec_get_prime(struct crypto_ec *e) +{ + return (const struct crypto_bignum *) &e->prime; +} + + +const struct crypto_bignum * crypto_ec_get_order(struct crypto_ec *e) +{ + return (const struct crypto_bignum *) &e->order; +} + + +void crypto_ec_point_deinit(struct crypto_ec_point *p, int clear) +{ + ecc_point *point = (ecc_point *) p; + + if (!p) + return; + + if (clear) { + mp_forcezero(point->x); + mp_forcezero(point->y); + mp_forcezero(point->z); + } + wc_ecc_del_point(point); +} + + +int crypto_ec_point_x(struct crypto_ec *e, const struct crypto_ec_point *p, + struct crypto_bignum *x) +{ + return mp_copy(((ecc_point *) p)->x, (mp_int *) x) == MP_OKAY ? 0 : -1; +} + + +int crypto_ec_point_to_bin(struct crypto_ec *e, + const struct crypto_ec_point *point, u8 *x, u8 *y) +{ + ecc_point *p = (ecc_point *) point; + + if (TEST_FAIL()) + return -1; + + if (!mp_isone(p->z)) { + if (ecc_map(p, &e->prime, e->mont_b) != MP_OKAY) + return -1; + } + + if (x) { + if (crypto_bignum_to_bin((struct crypto_bignum *)p->x, x, + e->key.dp->size, + e->key.dp->size) <= 0) + return -1; + } + + if (y) { + if (crypto_bignum_to_bin((struct crypto_bignum *) p->y, y, + e->key.dp->size, + e->key.dp->size) <= 0) + return -1; + } + + return 0; +} + + +struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e, + const u8 *val) +{ + ecc_point *point = NULL; + int loaded = 0; + + if (TEST_FAIL()) + return NULL; + + point = wc_ecc_new_point(); + if (!point) + goto done; + + if (mp_read_unsigned_bin(point->x, val, e->key.dp->size) != MP_OKAY) + goto done; + val += e->key.dp->size; + if (mp_read_unsigned_bin(point->y, val, e->key.dp->size) != MP_OKAY) + goto done; + mp_set(point->z, 1); + + loaded = 1; +done: + if (!loaded) { + wc_ecc_del_point(point); + point = NULL; + } + return (struct crypto_ec_point *) point; +} + + +int crypto_ec_point_add(struct crypto_ec *e, const struct crypto_ec_point *a, + const struct crypto_ec_point *b, + struct crypto_ec_point *c) +{ + mp_int mu; + ecc_point *ta = NULL, *tb = NULL; + ecc_point *pa = (ecc_point *) a, *pb = (ecc_point *) b; + mp_int *modulus = &e->prime; + int ret; + + if (TEST_FAIL()) + return -1; + + ret = mp_init(&mu); + if (ret != MP_OKAY) + return -1; + + ret = mp_montgomery_calc_normalization(&mu, modulus); + if (ret != MP_OKAY) { + mp_clear(&mu); + return -1; + } + + if (!mp_isone(&mu)) { + ta = wc_ecc_new_point(); + if (!ta) { + mp_clear(&mu); + return -1; + } + tb = wc_ecc_new_point(); + if (!tb) { + wc_ecc_del_point(ta); + mp_clear(&mu); + return -1; + } + + if (mp_mulmod(pa->x, &mu, modulus, ta->x) != MP_OKAY || + mp_mulmod(pa->y, &mu, modulus, ta->y) != MP_OKAY || + mp_mulmod(pa->z, &mu, modulus, ta->z) != MP_OKAY || + mp_mulmod(pb->x, &mu, modulus, tb->x) != MP_OKAY || + mp_mulmod(pb->y, &mu, modulus, tb->y) != MP_OKAY || + mp_mulmod(pb->z, &mu, modulus, tb->z) != MP_OKAY) { + ret = -1; + goto end; + } + pa = ta; + pb = tb; + } + + ret = ecc_projective_add_point(pa, pb, (ecc_point *) c, &e->a, + &e->prime, e->mont_b); + if (ret != 0) { + ret = -1; + goto end; + } + + if (ecc_map((ecc_point *) c, &e->prime, e->mont_b) != MP_OKAY) + ret = -1; + else + ret = 0; +end: + wc_ecc_del_point(tb); + wc_ecc_del_point(ta); + mp_clear(&mu); + return ret; +} + + +int crypto_ec_point_mul(struct crypto_ec *e, const struct crypto_ec_point *p, + const struct crypto_bignum *b, + struct crypto_ec_point *res) +{ + int ret; + + if (TEST_FAIL()) + return -1; + + ret = wc_ecc_mulmod((mp_int *) b, (ecc_point *) p, (ecc_point *) res, + &e->a, &e->prime, 1); + return ret == 0 ? 0 : -1; +} + + +int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p) +{ + ecc_point *point = (ecc_point *) p; + + if (TEST_FAIL()) + return -1; + + if (mp_sub(&e->prime, point->y, point->y) != MP_OKAY) + return -1; + + return 0; +} + + +int crypto_ec_point_solve_y_coord(struct crypto_ec *e, + struct crypto_ec_point *p, + const struct crypto_bignum *x, int y_bit) +{ + byte buf[1 + 2 * MAX_ECC_BYTES]; + int ret; + int prime_len = crypto_ec_prime_len(e); + + if (TEST_FAIL()) + return -1; + + buf[0] = y_bit ? ECC_POINT_COMP_ODD : ECC_POINT_COMP_EVEN; + ret = crypto_bignum_to_bin(x, buf + 1, prime_len, prime_len); + if (ret <= 0) + return -1; + ret = wc_ecc_import_point_der(buf, 1 + 2 * ret, e->key.idx, + (ecc_point *) p); + if (ret != 0) + return -1; + + return 0; +} + + +struct crypto_bignum * +crypto_ec_point_compute_y_sqr(struct crypto_ec *e, + const struct crypto_bignum *x) +{ + mp_int *y2 = NULL; + mp_int t; + int calced = 0; + + if (TEST_FAIL()) + return NULL; + + if (mp_init(&t) != MP_OKAY) + return NULL; + + y2 = (mp_int *) crypto_bignum_init(); + if (!y2) + goto done; + + if (mp_sqrmod((mp_int *) x, &e->prime, y2) != 0 || + mp_mulmod((mp_int *) x, y2, &e->prime, y2) != 0 || + mp_mulmod((mp_int *) x, &e->a, &e->prime, &t) != 0 || + mp_addmod(y2, &t, &e->prime, y2) != 0 || + mp_addmod(y2, &e->b, &e->prime, y2) != 0) + goto done; + + calced = 1; +done: + if (!calced) { + if (y2) { + mp_clear(y2); + os_free(y2); + } + mp_clear(&t); + } + + return (struct crypto_bignum *) y2; +} + + +int crypto_ec_point_is_at_infinity(struct crypto_ec *e, + const struct crypto_ec_point *p) +{ + return wc_ecc_point_is_at_infinity((ecc_point *) p); +} + + +int crypto_ec_point_is_on_curve(struct crypto_ec *e, + const struct crypto_ec_point *p) +{ + return wc_ecc_is_point((ecc_point *) p, &e->a, &e->b, &e->prime) == + MP_OKAY; +} + + +int crypto_ec_point_cmp(const struct crypto_ec *e, + const struct crypto_ec_point *a, + const struct crypto_ec_point *b) +{ + return wc_ecc_cmp_point((ecc_point *) a, (ecc_point *) b); +} + + +struct crypto_ecdh { + struct crypto_ec *ec; +}; + +struct crypto_ecdh * crypto_ecdh_init(int group) +{ + struct crypto_ecdh *ecdh = NULL; + WC_RNG rng; + int ret; + + if (wc_InitRng(&rng) != 0) + goto fail; + + ecdh = os_zalloc(sizeof(*ecdh)); + if (!ecdh) + goto fail; + + ecdh->ec = crypto_ec_init(group); + if (!ecdh->ec) + goto fail; + + ret = wc_ecc_make_key_ex(&rng, ecdh->ec->key.dp->size, &ecdh->ec->key, + ecdh->ec->key.dp->id); + if (ret < 0) + goto fail; + +done: + wc_FreeRng(&rng); + + return ecdh; +fail: + crypto_ecdh_deinit(ecdh); + ecdh = NULL; + goto done; +} + + +void crypto_ecdh_deinit(struct crypto_ecdh *ecdh) +{ + if (ecdh) { + crypto_ec_deinit(ecdh->ec); + os_free(ecdh); + } +} + + +struct wpabuf * crypto_ecdh_get_pubkey(struct crypto_ecdh *ecdh, int inc_y) +{ + struct wpabuf *buf = NULL; + int ret; + int len = ecdh->ec->key.dp->size; + + buf = wpabuf_alloc(inc_y ? 2 * len : len); + if (!buf) + goto fail; + + ret = crypto_bignum_to_bin((struct crypto_bignum *) + ecdh->ec->key.pubkey.x, wpabuf_put(buf, len), + len, len); + if (ret < 0) + goto fail; + if (inc_y) { + ret = crypto_bignum_to_bin((struct crypto_bignum *) + ecdh->ec->key.pubkey.y, + wpabuf_put(buf, len), len, len); + if (ret < 0) + goto fail; + } + +done: + return buf; +fail: + wpabuf_free(buf); + buf = NULL; + goto done; +} + + +struct wpabuf * crypto_ecdh_set_peerkey(struct crypto_ecdh *ecdh, int inc_y, + const u8 *key, size_t len) +{ + int ret; + struct wpabuf *pubkey = NULL; + struct wpabuf *secret = NULL; + word32 key_len = ecdh->ec->key.dp->size; + ecc_point *point = NULL; + size_t need_key_len = inc_y ? 2 * key_len : key_len; + + if (len < need_key_len) + goto fail; + pubkey = wpabuf_alloc(1 + 2 * key_len); + if (!pubkey) + goto fail; + wpabuf_put_u8(pubkey, inc_y ? ECC_POINT_UNCOMP : ECC_POINT_COMP_EVEN); + wpabuf_put_data(pubkey, key, need_key_len); + + point = wc_ecc_new_point(); + if (!point) + goto fail; + + ret = wc_ecc_import_point_der(wpabuf_mhead(pubkey), 1 + 2 * key_len, + ecdh->ec->key.idx, point); + if (ret != MP_OKAY) + goto fail; + + secret = wpabuf_alloc(key_len); + if (!secret) + goto fail; + + ret = wc_ecc_shared_secret_ex(&ecdh->ec->key, point, + wpabuf_put(secret, key_len), &key_len); + if (ret != MP_OKAY) + goto fail; + +done: + wc_ecc_del_point(point); + wpabuf_free(pubkey); + return secret; +fail: + wpabuf_free(secret); + secret = NULL; + goto done; +} + +#endif /* CONFIG_ECC */ diff --git a/src/crypto/des-internal.c b/src/crypto/des-internal.c index dec39ef8c61d0..4ed6957802b0b 100644 --- a/src/crypto/des-internal.c +++ b/src/crypto/des-internal.c @@ -48,7 +48,7 @@ static const u32 bytebit[8] = { - 0200, 0100, 040, 020, 010, 04, 02, 01 + 0200, 0100, 040, 020, 010, 04, 02, 01 }; static const u32 bigbyte[24] = @@ -58,22 +58,22 @@ static const u32 bigbyte[24] = 0x8000UL, 0x4000UL, 0x2000UL, 0x1000UL, 0x800UL, 0x400UL, 0x200UL, 0x100UL, 0x80UL, 0x40UL, 0x20UL, 0x10UL, - 0x8UL, 0x4UL, 0x2UL, 0x1L + 0x8UL, 0x4UL, 0x2UL, 0x1L }; /* Use the key schedule specific in the standard (ANSI X3.92-1981) */ static const u8 pc1[56] = { - 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, - 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, + 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, + 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, - 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 + 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 }; static const u8 totrot[16] = { 1, 2, 4, 6, - 8, 10, 12, 14, - 15, 17, 19, 21, + 8, 10, 12, 14, + 15, 17, 19, 21, 23, 25, 27, 28 }; @@ -396,7 +396,7 @@ static void desfunc(u32 *block, const u32 *keys) /* wpa_supplicant/hostapd specific wrapper */ -void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +int des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { u8 pkey[8], next, tmp; int i; @@ -421,6 +421,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) os_memset(pkey, 0, sizeof(pkey)); os_memset(ek, 0, sizeof(ek)); + return 0; } diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c index 7912361ff8c6f..a9b770ec1f160 100644 --- a/src/crypto/dh_groups.c +++ b/src/crypto/dh_groups.c @@ -1151,7 +1151,7 @@ static const u8 dh_group24_order[] = { { id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \ dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime), \ dh_group ## id ## _order, sizeof(dh_group ## id ## _order), safe } - + static const struct dh_group dh_groups[] = { DH_GROUP(5, 1), @@ -1203,19 +1203,6 @@ struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv) if (*priv == NULL) return NULL; - if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len)) - { - wpabuf_clear_free(*priv); - *priv = NULL; - return NULL; - } - - if (os_memcmp(wpabuf_head(*priv), dh->prime, dh->prime_len) > 0) { - /* Make sure private value is smaller than prime */ - *(wpabuf_mhead_u8(*priv)) = 0; - } - wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv); - pv_len = dh->prime_len; pv = wpabuf_alloc(pv_len); if (pv == NULL) { @@ -1223,17 +1210,17 @@ struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv) *priv = NULL; return NULL; } - if (crypto_mod_exp(dh->generator, dh->generator_len, - wpabuf_head(*priv), wpabuf_len(*priv), - dh->prime, dh->prime_len, wpabuf_mhead(pv), - &pv_len) < 0) { + if (crypto_dh_init(*dh->generator, dh->prime, dh->prime_len, + wpabuf_mhead(*priv), wpabuf_mhead(pv)) < 0) { wpabuf_clear_free(pv); - wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); + wpa_printf(MSG_INFO, "DH: crypto_dh_init failed"); wpabuf_clear_free(*priv); *priv = NULL; return NULL; } - wpabuf_put(pv, pv_len); + wpabuf_put(*priv, dh->prime_len); + wpabuf_put(pv, dh->prime_len); + wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv); wpa_hexdump_buf(MSG_DEBUG, "DH: public value", pv); return pv; @@ -1261,12 +1248,14 @@ struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public, shared = wpabuf_alloc(shared_len); if (shared == NULL) return NULL; - if (crypto_mod_exp(wpabuf_head(peer_public), wpabuf_len(peer_public), - wpabuf_head(own_private), wpabuf_len(own_private), - dh->prime, dh->prime_len, - wpabuf_mhead(shared), &shared_len) < 0) { + if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len, + wpabuf_head(own_private), + wpabuf_len(own_private), + wpabuf_head(peer_public), + wpabuf_len(peer_public), + wpabuf_mhead(shared), &shared_len) < 0) { wpabuf_clear_free(shared); - wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); + wpa_printf(MSG_INFO, "DH: crypto_dh_derive_secret failed"); return NULL; } wpabuf_put(shared, shared_len); diff --git a/src/crypto/fips_prf_wolfssl.c b/src/crypto/fips_prf_wolfssl.c new file mode 100644 index 0000000000000..feb39db5a6d9c --- /dev/null +++ b/src/crypto/fips_prf_wolfssl.c @@ -0,0 +1,87 @@ +/* + * FIPS 186-2 PRF for libcrypto + * Copyright (c) 2004-2017, 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 <wolfssl/options.h> +#include <wolfssl/wolfcrypt/sha.h> + +#include "common.h" +#include "crypto.h" + + +static void sha1_transform(u32 *state, const u8 data[64]) +{ + wc_Sha sha; + + os_memset(&sha, 0, sizeof(sha)); + sha.digest[0] = state[0]; + sha.digest[1] = state[1]; + sha.digest[2] = state[2]; + sha.digest[3] = state[3]; + sha.digest[4] = state[4]; + wc_ShaUpdate(&sha, data, 64); + state[0] = sha.digest[0]; + state[1] = sha.digest[1]; + state[2] = sha.digest[2]; + state[3] = sha.digest[3]; + state[4] = sha.digest[4]; +} + + +int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) +{ + u8 xkey[64]; + u32 t[5], _t[5]; + int i, j, m, k; + u8 *xpos = x; + u32 carry; + + if (seed_len < sizeof(xkey)) + os_memset(xkey + seed_len, 0, sizeof(xkey) - seed_len); + else + seed_len = sizeof(xkey); + + /* FIPS 186-2 + change notice 1 */ + + os_memcpy(xkey, seed, seed_len); + t[0] = 0x67452301; + t[1] = 0xEFCDAB89; + t[2] = 0x98BADCFE; + t[3] = 0x10325476; + t[4] = 0xC3D2E1F0; + + m = xlen / 40; + for (j = 0; j < m; j++) { + /* XSEED_j = 0 */ + for (i = 0; i < 2; i++) { + /* XVAL = (XKEY + XSEED_j) mod 2^b */ + + /* w_i = G(t, XVAL) */ + os_memcpy(_t, t, 20); + sha1_transform(_t, xkey); + WPA_PUT_BE32(xpos, _t[0]); + WPA_PUT_BE32(xpos + 4, _t[1]); + WPA_PUT_BE32(xpos + 8, _t[2]); + WPA_PUT_BE32(xpos + 12, _t[3]); + WPA_PUT_BE32(xpos + 16, _t[4]); + + /* XKEY = (1 + XKEY + w_i) mod 2^b */ + carry = 1; + for (k = 19; k >= 0; k--) { + carry += xkey[k] + xpos[k]; + xkey[k] = carry & 0xff; + carry >>= 8; + } + + xpos += 20; + } + /* x_j = w_0|w_1 */ + } + + return 0; +} diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c index d0d6a96af2bc2..aff7d33f4ea42 100644 --- a/src/crypto/ms_funcs.c +++ b/src/crypto/ms_funcs.c @@ -140,17 +140,20 @@ int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash) * @challenge: 8-octet Challenge (IN) * @password_hash: 16-octet PasswordHash (IN) * @response: 24-octet Response (OUT) + * Returns: 0 on success, -1 on failure */ -void challenge_response(const u8 *challenge, const u8 *password_hash, - u8 *response) +int challenge_response(const u8 *challenge, const u8 *password_hash, + u8 *response) { u8 zpwd[7]; - des_encrypt(challenge, password_hash, response); - des_encrypt(challenge, password_hash + 7, response + 8); + + if (des_encrypt(challenge, password_hash, response) < 0 || + des_encrypt(challenge, password_hash + 7, response + 8) < 0) + return -1; zpwd[0] = password_hash[14]; zpwd[1] = password_hash[15]; os_memset(zpwd + 2, 0, 5); - des_encrypt(challenge, zpwd, response + 16); + return des_encrypt(challenge, zpwd, response + 16); } @@ -175,9 +178,9 @@ int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, if (challenge_hash(peer_challenge, auth_challenge, username, username_len, challenge) || - nt_password_hash(password, password_len, password_hash)) + nt_password_hash(password, password_len, password_hash) || + challenge_response(challenge, password_hash, response)) return -1; - challenge_response(challenge, password_hash, response); return 0; } @@ -202,9 +205,9 @@ int generate_nt_response_pwhash(const u8 *auth_challenge, if (challenge_hash(peer_challenge, auth_challenge, username, username_len, - challenge)) + challenge) || + challenge_response(challenge, password_hash, response)) return -1; - challenge_response(challenge, password_hash, response); return 0; } @@ -304,9 +307,10 @@ int nt_challenge_response(const u8 *challenge, const u8 *password, size_t password_len, u8 *response) { u8 password_hash[16]; - if (nt_password_hash(password, password_len, password_hash)) + + if (nt_password_hash(password, password_len, password_hash) || + challenge_response(challenge, password_hash, response)) return -1; - challenge_response(challenge, password_hash, response); return 0; } @@ -487,12 +491,15 @@ int new_password_encrypted_with_old_nt_password_hash( * @password_hash: 16-octer PasswordHash (IN) * @block: 16-octet Block (IN) * @cypher: 16-octer Cypher (OUT) + * Returns: 0 on success, -1 on failure */ -void nt_password_hash_encrypted_with_block(const u8 *password_hash, - const u8 *block, u8 *cypher) +int nt_password_hash_encrypted_with_block(const u8 *password_hash, + const u8 *block, u8 *cypher) { - des_encrypt(password_hash, block, cypher); - des_encrypt(password_hash + 8, block + 7, cypher + 8); + if (des_encrypt(password_hash, block, cypher) < 0 || + des_encrypt(password_hash + 8, block + 7, cypher + 8) < 0) + return -1; + return 0; } @@ -515,10 +522,10 @@ int old_nt_password_hash_encrypted_with_new_nt_password_hash( if (nt_password_hash(old_password, old_password_len, old_password_hash) || nt_password_hash(new_password, new_password_len, - new_password_hash)) + new_password_hash) || + nt_password_hash_encrypted_with_block(old_password_hash, + new_password_hash, + encrypted_password_hash)) return -1; - nt_password_hash_encrypted_with_block(old_password_hash, - new_password_hash, - encrypted_password_hash); return 0; } diff --git a/src/crypto/ms_funcs.h b/src/crypto/ms_funcs.h index b5b5918e1a555..b8d55f05326cb 100644 --- a/src/crypto/ms_funcs.h +++ b/src/crypto/ms_funcs.h @@ -31,8 +31,8 @@ int generate_authenticator_response_pwhash( int nt_challenge_response(const u8 *challenge, const u8 *password, size_t password_len, u8 *response); -void challenge_response(const u8 *challenge, const u8 *password_hash, - u8 *response); +int challenge_response(const u8 *challenge, const u8 *password_hash, + u8 *response); int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, const u8 *username, size_t username_len, u8 *challenge); int nt_password_hash(const u8 *password, size_t password_len, @@ -50,8 +50,8 @@ int __must_check new_password_encrypted_with_old_nt_password_hash( const u8 *new_password, size_t new_password_len, const u8 *old_password, size_t old_password_len, u8 *encrypted_pw_block); -void nt_password_hash_encrypted_with_block(const u8 *password_hash, - const u8 *block, u8 *cypher); +int nt_password_hash_encrypted_with_block(const u8 *password_hash, + const u8 *block, u8 *cypher); int old_nt_password_hash_encrypted_with_new_nt_password_hash( const u8 *new_password, size_t new_password_len, const u8 *old_password, size_t old_password_len, diff --git a/src/crypto/random.c b/src/crypto/random.c index 3a86a93a46a8b..c278d9cb9de94 100644 --- a/src/crypto/random.c +++ b/src/crypto/random.c @@ -54,7 +54,6 @@ static int random_fd = -1; static unsigned int own_pool_ready = 0; #define RANDOM_ENTROPY_SIZE 20 static char *random_entropy_file = NULL; -static int random_entropy_file_read = 0; #define MIN_COLLECT_ENTROPY 1000 static unsigned int entropy = 0; @@ -66,6 +65,9 @@ static void random_write_entropy(void); static u32 __ROL32(u32 x, u32 y) { + if (y == 0) + return x; + return (x << (y & 31)) | (x >> (32 - (y & 31))); } @@ -354,7 +356,6 @@ static void random_read_entropy(void) own_pool_ready = (u8) buf[0]; random_add_randomness(buf + 1, RANDOM_ENTROPY_SIZE); - random_entropy_file_read = 1; os_free(buf); wpa_printf(MSG_DEBUG, "random: Added entropy from %s " "(own_pool_ready=%u)", diff --git a/src/crypto/sha1-internal.c b/src/crypto/sha1-internal.c index ffcba66af652f..a4917070f196b 100644 --- a/src/crypto/sha1-internal.c +++ b/src/crypto/sha1-internal.c @@ -53,7 +53,7 @@ By Steve Reid <sreid@sea-to-sky.net> 100% Public Domain ----------------- -Modified 7/98 +Modified 7/98 By James H. Brown <jbrown@burgoyne.com> Still 100% Public Domain @@ -75,7 +75,7 @@ Since the file IO in main() reads 16K at a time, any file 8K or larger would be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million "a"s). -I also changed the declaration of variables i & j in SHA1Update to +I also changed the declaration of variables i & j in SHA1Update to unsigned long from unsigned int for the same reason. These changes should make no difference to any 32 bit implementations since @@ -102,7 +102,7 @@ Still 100% public domain Modified 4/01 By Saul Kravitz <Saul.Kravitz@celera.com> Still 100% PD -Modified to run on Compaq Alpha hardware. +Modified to run on Compaq Alpha hardware. ----------------- Modified 4/01 @@ -162,7 +162,7 @@ void SHAPrintContext(SHA1_CTX *context, char *msg) { printf("%s (%d,%d) %x %x %x %x %x\n", msg, - context->count[0], context->count[1], + context->count[0], context->count[1], context->state[0], context->state[1], context->state[2], diff --git a/src/crypto/sha256-internal.c b/src/crypto/sha256-internal.c index 86a548ee472d1..ff1e2ba1686d3 100644 --- a/src/crypto/sha256-internal.c +++ b/src/crypto/sha256-internal.c @@ -69,7 +69,7 @@ static const unsigned long K[64] = { ( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \ ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL) #define Ch(x,y,z) (z ^ (x & (y ^ z))) -#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) #define S(x, n) RORc((x), (n)) #define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) #define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) @@ -100,7 +100,7 @@ static int sha256_compress(struct sha256_state *md, unsigned char *buf) for (i = 16; i < 64; i++) { W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; - } + } /* Compress */ #define RND(a,b,c,d,e,f,g,h,i) \ @@ -111,7 +111,7 @@ static int sha256_compress(struct sha256_state *md, unsigned char *buf) for (i = 0; i < 64; ++i) { RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i); - t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; + t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; } diff --git a/src/crypto/sha256-kdf.c b/src/crypto/sha256-kdf.c index e7509ce41aba4..af7d954d8a440 100644 --- a/src/crypto/sha256-kdf.c +++ b/src/crypto/sha256-kdf.c @@ -1,6 +1,6 @@ /* - * HMAC-SHA256 KDF (RFC 5295) - * Copyright (c) 2014, Jouni Malinen <j@w1.fi> + * HMAC-SHA256 KDF (RFC 5295) and HKDF-Expand(SHA256) (RFC 5869) + * Copyright (c) 2014-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -16,7 +16,8 @@ * hmac_sha256_kdf - HMAC-SHA256 based KDF (RFC 5295) * @secret: Key for KDF * @secret_len: Length of the key in bytes - * @label: A unique label for each purpose of the KDF + * @label: A unique label for each purpose of the KDF or %NULL to select + * RFC 5869 HKDF-Expand() with arbitrary seed (= info) * @seed: Seed value to bind into the key * @seed_len: Length of the seed * @out: Buffer for the generated pseudo-random key @@ -24,7 +25,9 @@ * Returns: 0 on success, -1 on failure. * * This function is used to derive new, cryptographically separate keys from a - * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. + * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used + * with label = NULL and seed = info, this matches HKDF-Expand() defined in + * RFC 5869, Chapter 2.3. */ int hmac_sha256_kdf(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, @@ -38,8 +41,13 @@ int hmac_sha256_kdf(const u8 *secret, size_t secret_len, addr[0] = T; len[0] = SHA256_MAC_LEN; - addr[1] = (const unsigned char *) label; - len[1] = os_strlen(label) + 1; + if (label) { + addr[1] = (const unsigned char *) label; + len[1] = os_strlen(label) + 1; + } else { + addr[1] = (const u8 *) ""; + len[1] = 0; + } addr[2] = seed; len[2] = seed_len; addr[3] = &iter; diff --git a/src/crypto/sha384-kdf.c b/src/crypto/sha384-kdf.c new file mode 100644 index 0000000000000..1d196279086e0 --- /dev/null +++ b/src/crypto/sha384-kdf.c @@ -0,0 +1,87 @@ +/* + * HMAC-SHA384 KDF (RFC 5295) and HKDF-Expand(SHA384) (RFC 5869) + * Copyright (c) 2014-2017, 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 "common.h" +#include "sha384.h" + + +/** + * hmac_sha384_kdf - HMAC-SHA384 based KDF (RFC 5295) + * @secret: Key for KDF + * @secret_len: Length of the key in bytes + * @label: A unique label for each purpose of the KDF or %NULL to select + * RFC 5869 HKDF-Expand() with arbitrary seed (= info) + * @seed: Seed value to bind into the key + * @seed_len: Length of the seed + * @out: Buffer for the generated pseudo-random key + * @outlen: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure. + * + * This function is used to derive new, cryptographically separate keys from a + * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used + * with label = NULL and seed = info, this matches HKDF-Expand() defined in + * RFC 5869, Chapter 2.3. + */ +int hmac_sha384_kdf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen) +{ + u8 T[SHA384_MAC_LEN]; + u8 iter = 1; + const unsigned char *addr[4]; + size_t len[4]; + size_t pos, clen; + + addr[0] = T; + len[0] = SHA384_MAC_LEN; + if (label) { + addr[1] = (const unsigned char *) label; + len[1] = os_strlen(label) + 1; + } else { + addr[1] = (const u8 *) ""; + len[1] = 0; + } + addr[2] = seed; + len[2] = seed_len; + addr[3] = &iter; + len[3] = 1; + + if (hmac_sha384_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0) + return -1; + + pos = 0; + for (;;) { + clen = outlen - pos; + if (clen > SHA384_MAC_LEN) + clen = SHA384_MAC_LEN; + os_memcpy(out + pos, T, clen); + pos += clen; + + if (pos == outlen) + break; + + if (iter == 255) { + os_memset(out, 0, outlen); + os_memset(T, 0, SHA384_MAC_LEN); + return -1; + } + iter++; + + if (hmac_sha384_vector(secret, secret_len, 4, addr, len, T) < 0) + { + os_memset(out, 0, outlen); + os_memset(T, 0, SHA384_MAC_LEN); + return -1; + } + } + + os_memset(T, 0, SHA384_MAC_LEN); + return 0; +} diff --git a/src/crypto/sha384-prf.c b/src/crypto/sha384-prf.c index 653920ba283d2..03e3cb353a3d0 100644 --- a/src/crypto/sha384-prf.c +++ b/src/crypto/sha384-prf.c @@ -1,6 +1,6 @@ /* * SHA384-based KDF (IEEE 802.11ac) - * 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. @@ -22,14 +22,16 @@ * @data_len: Length of the data * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure * * This function is used to derive new, cryptographically separate keys from a * given key. */ -void sha384_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +int sha384_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) { - sha384_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8); + return sha384_prf_bits(key, key_len, label, data, data_len, buf, + buf_len * 8); } @@ -42,15 +44,16 @@ void sha384_prf(const u8 *key, size_t key_len, const char *label, * @data_len: Length of the data * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bits of key to generate + * Returns: 0 on success, -1 on failure * * This function is used to derive new, cryptographically separate keys from a * given key. If the requested buf_len is not divisible by eight, the least * significant 1-7 bits of the last octet in the output are not part of the * requested output. */ -void sha384_prf_bits(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, - size_t buf_len_bits) +int sha384_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits) { u16 counter = 1; size_t pos, plen; @@ -75,11 +78,14 @@ void sha384_prf_bits(const u8 *key, size_t key_len, const char *label, plen = buf_len - pos; WPA_PUT_LE16(counter_le, counter); if (plen >= SHA384_MAC_LEN) { - hmac_sha384_vector(key, key_len, 4, addr, len, - &buf[pos]); + if (hmac_sha384_vector(key, key_len, 4, addr, len, + &buf[pos]) < 0) + return -1; pos += SHA384_MAC_LEN; } else { - hmac_sha384_vector(key, key_len, 4, addr, len, hash); + if (hmac_sha384_vector(key, key_len, 4, addr, len, + hash) < 0) + return -1; os_memcpy(&buf[pos], hash, plen); pos += plen; break; @@ -97,4 +103,6 @@ void sha384_prf_bits(const u8 *key, size_t key_len, const char *label, } os_memset(hash, 0, sizeof(hash)); + + return 0; } diff --git a/src/crypto/sha384.c b/src/crypto/sha384.c new file mode 100644 index 0000000000000..ee136ce99b7e9 --- /dev/null +++ b/src/crypto/sha384.c @@ -0,0 +1,104 @@ +/* + * SHA-384 hash implementation and interface functions + * 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. + */ + +#include "includes.h" + +#include "common.h" +#include "sha384.h" +#include "crypto.h" + + +/** + * hmac_sha384_vector - HMAC-SHA384 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (48 bytes) + * Returns: 0 on success, -1 on failure + */ +int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + unsigned char k_pad[128]; /* padding - key XORd with ipad/opad */ + unsigned char tk[48]; + const u8 *_addr[6]; + size_t _len[6], i; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return -1; + } + + /* if key is longer than 128 bytes reset it to key = SHA384(key) */ + if (key_len > 128) { + if (sha384_vector(1, &key, &key_len, tk) < 0) + return -1; + key = tk; + key_len = 48; + } + + /* the HMAC_SHA384 transform looks like: + * + * SHA384(K XOR opad, SHA384(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 128 times + * opad is the byte 0x5c repeated 128 times + * and text is the data being protected */ + + /* start out by storing key in ipad */ + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with ipad values */ + for (i = 0; i < 128; i++) + k_pad[i] ^= 0x36; + + /* perform inner SHA384 */ + _addr[0] = k_pad; + _len[0] = 128; + for (i = 0; i < num_elem; i++) { + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; + } + if (sha384_vector(1 + num_elem, _addr, _len, mac) < 0) + return -1; + + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 128; i++) + k_pad[i] ^= 0x5c; + + /* perform outer SHA384 */ + _addr[0] = k_pad; + _len[0] = 128; + _addr[1] = mac; + _len[1] = SHA384_MAC_LEN; + return sha384_vector(2, _addr, _len, mac); +} + + +/** + * hmac_sha384 - HMAC-SHA384 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (48 bytes) + * Returns: 0 on success, -1 on failure + */ +int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac); +} diff --git a/src/crypto/sha384.h b/src/crypto/sha384.h index 3deafa59ec290..2241425385c39 100644 --- a/src/crypto/sha384.h +++ b/src/crypto/sha384.h @@ -1,6 +1,6 @@ /* * SHA384 hash implementation and interface functions - * Copyright (c) 2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2015-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -15,10 +15,13 @@ int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); int hmac_sha384(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac); -void sha384_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len); -void sha384_prf_bits(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, - size_t buf_len_bits); +int sha384_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +int sha384_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits); +int hmac_sha384_kdf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen); #endif /* SHA384_H */ diff --git a/src/crypto/sha512-kdf.c b/src/crypto/sha512-kdf.c new file mode 100644 index 0000000000000..8b71f9b0e4f9d --- /dev/null +++ b/src/crypto/sha512-kdf.c @@ -0,0 +1,87 @@ +/* + * HMAC-SHA512 KDF (RFC 5295) and HKDF-Expand(SHA512) (RFC 5869) + * Copyright (c) 2014-2017, 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 "common.h" +#include "sha512.h" + + +/** + * hmac_sha512_kdf - HMAC-SHA512 based KDF (RFC 5295) + * @secret: Key for KDF + * @secret_len: Length of the key in bytes + * @label: A unique label for each purpose of the KDF or %NULL to select + * RFC 5869 HKDF-Expand() with arbitrary seed (= info) + * @seed: Seed value to bind into the key + * @seed_len: Length of the seed + * @out: Buffer for the generated pseudo-random key + * @outlen: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure. + * + * This function is used to derive new, cryptographically separate keys from a + * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2. When used + * with label = NULL and seed = info, this matches HKDF-Expand() defined in + * RFC 5869, Chapter 2.3. + */ +int hmac_sha512_kdf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen) +{ + u8 T[SHA512_MAC_LEN]; + u8 iter = 1; + const unsigned char *addr[4]; + size_t len[4]; + size_t pos, clen; + + addr[0] = T; + len[0] = SHA512_MAC_LEN; + if (label) { + addr[1] = (const unsigned char *) label; + len[1] = os_strlen(label) + 1; + } else { + addr[1] = (const u8 *) ""; + len[1] = 0; + } + addr[2] = seed; + len[2] = seed_len; + addr[3] = &iter; + len[3] = 1; + + if (hmac_sha512_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0) + return -1; + + pos = 0; + for (;;) { + clen = outlen - pos; + if (clen > SHA512_MAC_LEN) + clen = SHA512_MAC_LEN; + os_memcpy(out + pos, T, clen); + pos += clen; + + if (pos == outlen) + break; + + if (iter == 255) { + os_memset(out, 0, outlen); + os_memset(T, 0, SHA512_MAC_LEN); + return -1; + } + iter++; + + if (hmac_sha512_vector(secret, secret_len, 4, addr, len, T) < 0) + { + os_memset(out, 0, outlen); + os_memset(T, 0, SHA512_MAC_LEN); + return -1; + } + } + + os_memset(T, 0, SHA512_MAC_LEN); + return 0; +} diff --git a/src/crypto/sha512-prf.c b/src/crypto/sha512-prf.c new file mode 100644 index 0000000000000..3b2ad889d6efb --- /dev/null +++ b/src/crypto/sha512-prf.c @@ -0,0 +1,108 @@ +/* + * SHA512-based KDF (IEEE 802.11ac) + * 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. + */ + +#include "includes.h" + +#include "common.h" +#include "sha512.h" +#include "crypto.h" + + +/** + * sha512_prf - SHA512-based Key derivation function (IEEE 802.11ac, 11.6.1.7.2) + * @key: Key for KDF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * Returns: 0 on success, -1 on failure + * + * This function is used to derive new, cryptographically separate keys from a + * given key. + */ +int sha512_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + return sha512_prf_bits(key, key_len, label, data, data_len, buf, + buf_len * 8); +} + + +/** + * sha512_prf_bits - IEEE Std 802.11ac-2013, 11.6.1.7.2 Key derivation function + * @key: Key for KDF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bits of key to generate + * Returns: 0 on success, -1 on failure + * + * This function is used to derive new, cryptographically separate keys from a + * given key. If the requested buf_len is not divisible by eight, the least + * significant 1-7 bits of the last octet in the output are not part of the + * requested output. + */ +int sha512_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits) +{ + u16 counter = 1; + size_t pos, plen; + u8 hash[SHA512_MAC_LEN]; + const u8 *addr[4]; + size_t len[4]; + u8 counter_le[2], length_le[2]; + size_t buf_len = (buf_len_bits + 7) / 8; + + addr[0] = counter_le; + len[0] = 2; + addr[1] = (u8 *) label; + len[1] = os_strlen(label); + addr[2] = data; + len[2] = data_len; + addr[3] = length_le; + len[3] = sizeof(length_le); + + WPA_PUT_LE16(length_le, buf_len_bits); + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + WPA_PUT_LE16(counter_le, counter); + if (plen >= SHA512_MAC_LEN) { + if (hmac_sha512_vector(key, key_len, 4, addr, len, + &buf[pos]) < 0) + return -1; + pos += SHA512_MAC_LEN; + } else { + if (hmac_sha512_vector(key, key_len, 4, addr, len, + hash) < 0) + return -1; + os_memcpy(&buf[pos], hash, plen); + pos += plen; + break; + } + counter++; + } + + /* + * Mask out unused bits in the last octet if it does not use all the + * bits. + */ + if (buf_len_bits % 8) { + u8 mask = 0xff << (8 - buf_len_bits % 8); + buf[pos - 1] &= mask; + } + + os_memset(hash, 0, sizeof(hash)); + + return 0; +} diff --git a/src/crypto/sha512.h b/src/crypto/sha512.h new file mode 100644 index 0000000000000..8e64c8b116fa5 --- /dev/null +++ b/src/crypto/sha512.h @@ -0,0 +1,27 @@ +/* + * SHA512 hash implementation and interface functions + * Copyright (c) 2015-2017, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef SHA512_H +#define SHA512_H + +#define SHA512_MAC_LEN 64 + +int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int hmac_sha512(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac); +int sha512_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); +int sha512_prf_bits(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, + size_t buf_len_bits); +int hmac_sha512_kdf(const u8 *secret, size_t secret_len, + const char *label, const u8 *seed, size_t seed_len, + u8 *out, size_t outlen); + +#endif /* SHA512_H */ diff --git a/src/crypto/tls.h b/src/crypto/tls.h index 11d504a97fc05..481b34681d7ba 100644 --- a/src/crypto/tls.h +++ b/src/crypto/tls.h @@ -41,6 +41,7 @@ enum tls_fail_reason { TLS_FAIL_SERVER_CHAIN_PROBE = 8, TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9, TLS_FAIL_DOMAIN_MISMATCH = 10, + TLS_FAIL_INSUFFICIENT_KEY_LEN = 11, }; @@ -63,6 +64,7 @@ union tls_event_data { size_t hash_len; const char *altsubject[TLS_MAX_ALT_SUBJECT]; int num_altsubject; + const char *serial_num; } peer_cert; struct { @@ -80,6 +82,7 @@ struct tls_config { int cert_in_cb; const char *openssl_ciphers; unsigned int tls_session_lifetime; + unsigned int tls_flags; void (*event_cb)(void *ctx, enum tls_event ev, union tls_event_data *data); @@ -97,6 +100,9 @@ struct tls_config { #define TLS_CONN_DISABLE_TLSv1_0 BIT(8) #define TLS_CONN_EXT_CERT_CHECK BIT(9) #define TLS_CONN_REQUIRE_OCSP_ALL BIT(10) +#define TLS_CONN_SUITEB BIT(11) +#define TLS_CONN_SUITEB_NO_ECDH BIT(12) +#define TLS_CONN_DISABLE_TLSv1_3 BIT(13) /** * struct tls_connection_params - Parameters for TLS connection @@ -248,6 +254,18 @@ void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn); int tls_connection_established(void *tls_ctx, struct tls_connection *conn); /** + * tls_connection_peer_serial_num - Fetch peer certificate serial number + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: Allocated string buffer containing the peer certificate serial + * number or %NULL on error. + * + * The caller is responsible for freeing the returned buffer with os_free(). + */ +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn); + +/** * tls_connection_shutdown - Shutdown TLS connection * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c index 200f0eda931af..36dafd2603f0c 100644 --- a/src/crypto/tls_gnutls.c +++ b/src/crypto/tls_gnutls.c @@ -1,6 +1,6 @@ /* * SSL/TLS interface functions for GnuTLS - * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -295,6 +295,14 @@ int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) } +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn) +{ + /* TODO */ + return NULL; +} + + int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) { struct tls_global *global = ssl_ctx; @@ -346,6 +354,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params) { int ret; + const char *err; + char prio_buf[100]; + const char *prio = NULL; if (conn == NULL || params == NULL) return -1; @@ -397,12 +408,60 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, conn->flags = params->flags; + if (params->flags & (TLS_CONN_DISABLE_TLSv1_0 | + TLS_CONN_DISABLE_TLSv1_1 | + TLS_CONN_DISABLE_TLSv1_2)) { + os_snprintf(prio_buf, sizeof(prio_buf), + "NORMAL:-VERS-SSL3.0%s%s%s", + params->flags & TLS_CONN_DISABLE_TLSv1_0 ? + ":-VERS-TLS1.0" : "", + params->flags & TLS_CONN_DISABLE_TLSv1_1 ? + ":-VERS-TLS1.1" : "", + params->flags & TLS_CONN_DISABLE_TLSv1_2 ? + ":-VERS-TLS1.2" : ""); + prio = prio_buf; + } + if (params->openssl_ciphers) { - wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported"); - return -1; + if (os_strcmp(params->openssl_ciphers, "SUITEB128") == 0) { + prio = "SUITEB128"; + } else if (os_strcmp(params->openssl_ciphers, + "SUITEB192") == 0) { + prio = "SUITEB192"; + } else if ((params->flags & TLS_CONN_SUITEB) && + os_strcmp(params->openssl_ciphers, + "ECDHE-RSA-AES256-GCM-SHA384") == 0) { + prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL"; + } else if (os_strcmp(params->openssl_ciphers, + "ECDHE-RSA-AES256-GCM-SHA384") == 0) { + prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL"; + } else if (os_strcmp(params->openssl_ciphers, + "DHE-RSA-AES256-GCM-SHA384") == 0) { + prio = "NONE:+VERS-TLS1.2:+AEAD:+DHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL:%PROFILE_HIGH"; + } else if (os_strcmp(params->openssl_ciphers, + "ECDHE-ECDSA-AES256-GCM-SHA384") == 0) { + prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-ECDSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL"; + } else { + wpa_printf(MSG_INFO, + "GnuTLS: openssl_ciphers not supported"); + return -1; + } + } else if (params->flags & TLS_CONN_SUITEB) { + prio = "NONE:+VERS-TLS1.2:+AEAD:+ECDHE-ECDSA:+ECDHE-RSA:+DHE-RSA:+AES-256-GCM:+SIGN-RSA-SHA384:+CURVE-SECP384R1:+COMP-NULL:%PROFILE_HIGH"; } - /* TODO: gnutls_certificate_set_verify_flags(xcred, flags); + if (prio) { + wpa_printf(MSG_DEBUG, "GnuTLS: Set priority string: %s", prio); + ret = gnutls_priority_set_direct(conn->session, prio, &err); + if (ret < 0) { + wpa_printf(MSG_ERROR, + "GnuTLS: Priority string failure at '%s'", + err); + return -1; + } + } + + /* TODO: gnutls_certificate_set_verify_flags(xcred, flags); * to force peer validation(?) */ if (params->ca_cert) { @@ -425,6 +484,13 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, gnutls_strerror(ret)); return -1; } + wpa_printf(MSG_DEBUG, + "GnuTLS: Successfully read CA cert '%s' in PEM format", + params->ca_cert); + } else { + wpa_printf(MSG_DEBUG, + "GnuTLS: Successfully read CA cert '%s' in DER format", + params->ca_cert); } } else if (params->ca_cert_blob) { gnutls_datum_t ca; @@ -472,6 +538,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } if (params->client_cert && params->private_key) { + wpa_printf(MSG_DEBUG, + "GnuTLS: Try to parse client cert '%s' and key '%s' in DER format", + params->client_cert, params->private_key); #if GNUTLS_VERSION_NUMBER >= 0x03010b ret = gnutls_certificate_set_x509_key_file2( conn->xcred, params->client_cert, params->private_key, @@ -483,8 +552,9 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, GNUTLS_X509_FMT_DER); #endif if (ret < 0) { - wpa_printf(MSG_DEBUG, "Failed to read client cert/key " - "in DER format: %s", gnutls_strerror(ret)); + wpa_printf(MSG_DEBUG, + "GnuTLS: Failed to read client cert/key in DER format (%s) - try in PEM format", + gnutls_strerror(ret)); #if GNUTLS_VERSION_NUMBER >= 0x03010b ret = gnutls_certificate_set_x509_key_file2( conn->xcred, params->client_cert, @@ -501,11 +571,19 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, gnutls_strerror(ret)); return ret; } + wpa_printf(MSG_DEBUG, + "GnuTLS: Successfully read client cert/key in PEM format"); + } else { + wpa_printf(MSG_DEBUG, + "GnuTLS: Successfully read client cert/key in DER format"); } } else if (params->private_key) { int pkcs12_ok = 0; #ifdef PKCS12_FUNCS /* Try to load in PKCS#12 format */ + wpa_printf(MSG_DEBUG, + "GnuTLS: Try to parse client cert/key '%s'in PKCS#12 DER format", + params->private_key); ret = gnutls_certificate_set_x509_simple_pkcs12_file( conn->xcred, params->private_key, GNUTLS_X509_FMT_DER, params->private_key_passwd); @@ -1333,6 +1411,7 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, ret = gnutls_handshake(conn->session); if (ret < 0) { gnutls_alert_description_t alert; + union tls_event_data ev; switch (ret) { case GNUTLS_E_AGAIN: @@ -1343,14 +1422,29 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx, conn->push_buf = wpabuf_alloc(0); } break; + case GNUTLS_E_DH_PRIME_UNACCEPTABLE: + wpa_printf(MSG_DEBUG, "GnuTLS: Unacceptable DH prime"); + if (conn->global->event_cb) { + os_memset(&ev, 0, sizeof(ev)); + ev.alert.is_local = 1; + ev.alert.type = "fatal"; + ev.alert.description = "insufficient security"; + conn->global->event_cb(conn->global->cb_ctx, + TLS_ALERT, &ev); + } + /* + * Could send a TLS Alert to the server, but for now, + * simply terminate handshake. + */ + conn->failed++; + conn->write_alerts++; + break; case GNUTLS_E_FATAL_ALERT_RECEIVED: alert = gnutls_alert_get(conn->session); wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert", __func__, gnutls_alert_get_name(alert)); conn->read_alerts++; if (conn->global->event_cb != NULL) { - union tls_event_data ev; - os_memset(&ev, 0, sizeof(ev)); ev.alert.is_local = 0; ev.alert.type = gnutls_alert_get_name(alert); @@ -1501,16 +1595,53 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, int tls_get_version(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { - /* TODO */ - return -1; + gnutls_protocol_t ver; + + ver = gnutls_protocol_get_version(conn->session); + if (ver == GNUTLS_TLS1_0) + os_strlcpy(buf, "TLSv1", buflen); + else if (ver == GNUTLS_TLS1_1) + os_strlcpy(buf, "TLSv1.1", buflen); + else if (ver == GNUTLS_TLS1_2) + os_strlcpy(buf, "TLSv1.2", buflen); + else + return -1; + return 0; } int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, char *buf, size_t buflen) { - /* TODO */ - buf[0] = '\0'; + gnutls_cipher_algorithm_t cipher; + gnutls_kx_algorithm_t kx; + gnutls_mac_algorithm_t mac; + const char *kx_str, *cipher_str, *mac_str; + int res; + + cipher = gnutls_cipher_get(conn->session); + cipher_str = gnutls_cipher_get_name(cipher); + if (!cipher_str) + cipher_str = ""; + + kx = gnutls_kx_get(conn->session); + kx_str = gnutls_kx_get_name(kx); + if (!kx_str) + kx_str = ""; + + mac = gnutls_mac_get(conn->session); + mac_str = gnutls_mac_get_name(mac); + if (!mac_str) + mac_str = ""; + + if (kx == GNUTLS_KX_RSA) + res = os_snprintf(buf, buflen, "%s-%s", cipher_str, mac_str); + else + res = os_snprintf(buf, buflen, "%s-%s-%s", + kx_str, cipher_str, mac_str); + if (os_snprintf_error(buflen, res)) + return -1; + return 0; } diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c index c7cb5ded331f2..d289c9442ceb3 100644 --- a/src/crypto/tls_internal.c +++ b/src/crypto/tls_internal.c @@ -177,6 +177,14 @@ int tls_connection_established(void *tls_ctx, struct tls_connection *conn) } +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn) +{ + /* TODO */ + return NULL; +} + + int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) { #ifdef CONFIG_TLS_INTERNAL_CLIENT diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c index dd5681e9ca3c3..5d0c6bda15479 100644 --- a/src/crypto/tls_none.c +++ b/src/crypto/tls_none.c @@ -45,6 +45,13 @@ int tls_connection_established(void *tls_ctx, struct tls_connection *conn) } +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn) +{ + return NULL; +} + + int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) { return -1; diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c index 23ac64b48cd9b..0d5ebda699f02 100644 --- a/src/crypto/tls_openssl.c +++ b/src/crypto/tls_openssl.c @@ -59,7 +59,8 @@ typedef int stack_index_t; #endif /* SSL_set_tlsext_status_type */ #if (OPENSSL_VERSION_NUMBER < 0x10100000L || \ - defined(LIBRESSL_VERSION_NUMBER)) && \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L)) && \ !defined(BORINGSSL_API_VERSION) /* * SSL_get_client_random() and SSL_get_server_random() were added in OpenSSL @@ -103,6 +104,21 @@ static size_t SSL_SESSION_get_master_key(const SSL_SESSION *session, #endif +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#ifdef CONFIG_SUITEB +static int RSA_bits(const RSA *r) +{ + return BN_num_bits(r->n); +} +#endif /* CONFIG_SUITEB */ + + +static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x) +{ + return ASN1_STRING_data((ASN1_STRING *) x); +} +#endif + #ifdef ANDROID #include <openssl/pem.h> #include <keystore/keystore_get.h> @@ -222,6 +238,8 @@ struct tls_connection { unsigned int server_cert_only:1; unsigned int invalid_hb_used:1; unsigned int success_data:1; + unsigned int client_hello_generated:1; + unsigned int server:1; u8 srv_cert_hash[32]; @@ -233,6 +251,9 @@ struct tls_connection { unsigned char client_random[SSL3_RANDOM_SIZE]; unsigned char server_random[SSL3_RANDOM_SIZE]; + + u16 cipher_suite; + int server_dh_prime_len; }; @@ -919,7 +940,9 @@ void * tls_init(const struct tls_config *conf) } #endif /* OPENSSL_FIPS */ #endif /* CONFIG_FIPS */ -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) SSL_load_error_strings(); SSL_library_init(); #ifndef OPENSSL_NO_SHA256 @@ -972,6 +995,14 @@ void * tls_init(const struct tls_config *conf) SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3); +#ifdef SSL_MODE_NO_AUTO_CHAIN + /* Number of deployed use cases assume the default OpenSSL behavior of + * auto chaining the local certificate is in use. BoringSSL removed this + * functionality by default, so we need to restore it here to avoid + * breaking existing use cases. */ + SSL_CTX_clear_mode(ssl, SSL_MODE_NO_AUTO_CHAIN); +#endif /* SSL_MODE_NO_AUTO_CHAIN */ + SSL_CTX_set_info_callback(ssl, ssl_info_cb); SSL_CTX_set_app_data(ssl, context); if (data->tls_session_lifetime > 0) { @@ -999,8 +1030,10 @@ void * tls_init(const struct tls_config *conf) #ifndef OPENSSL_NO_ENGINE wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine"); +#if OPENSSL_VERSION_NUMBER < 0x10100000L ERR_load_ENGINE_strings(); ENGINE_load_dynamic(); +#endif /* OPENSSL_VERSION_NUMBER */ if (conf && (conf->opensc_engine_path || conf->pkcs11_engine_path || @@ -1017,7 +1050,7 @@ void * tls_init(const struct tls_config *conf) if (conf && conf->openssl_ciphers) ciphers = conf->openssl_ciphers; else - ciphers = "DEFAULT:!EXP:!LOW"; + ciphers = TLS_DEFAULT_CIPHERS; if (SSL_CTX_set_cipher_list(ssl, ciphers) != 1) { wpa_printf(MSG_ERROR, "OpenSSL: Failed to set cipher string '%s'", @@ -1043,7 +1076,9 @@ void tls_deinit(void *ssl_ctx) tls_openssl_ref_count--; if (tls_openssl_ref_count == 0) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) #ifndef OPENSSL_NO_ENGINE ENGINE_cleanup(); #endif /* OPENSSL_NO_ENGINE */ @@ -1296,6 +1331,95 @@ static const char * openssl_handshake_type(int content_type, const u8 *buf, } +#ifdef CONFIG_SUITEB + +static void check_server_hello(struct tls_connection *conn, + const u8 *pos, const u8 *end) +{ + size_t payload_len, id_len; + + /* + * Parse ServerHello to get the selected cipher suite since OpenSSL does + * not make it cleanly available during handshake and we need to know + * whether DHE was selected. + */ + + if (end - pos < 3) + return; + payload_len = WPA_GET_BE24(pos); + pos += 3; + + if ((size_t) (end - pos) < payload_len) + return; + end = pos + payload_len; + + /* Skip Version and Random */ + if (end - pos < 2 + SSL3_RANDOM_SIZE) + return; + pos += 2 + SSL3_RANDOM_SIZE; + + /* Skip Session ID */ + if (end - pos < 1) + return; + id_len = *pos++; + if ((size_t) (end - pos) < id_len) + return; + pos += id_len; + + if (end - pos < 2) + return; + conn->cipher_suite = WPA_GET_BE16(pos); + wpa_printf(MSG_DEBUG, "OpenSSL: Server selected cipher suite 0x%x", + conn->cipher_suite); +} + + +static void check_server_key_exchange(SSL *ssl, struct tls_connection *conn, + const u8 *pos, const u8 *end) +{ + size_t payload_len; + u16 dh_len; + BIGNUM *p; + int bits; + + if (!(conn->flags & TLS_CONN_SUITEB)) + return; + + /* DHE is enabled only with DHE-RSA-AES256-GCM-SHA384 */ + if (conn->cipher_suite != 0x9f) + return; + + if (end - pos < 3) + return; + payload_len = WPA_GET_BE24(pos); + pos += 3; + + if ((size_t) (end - pos) < payload_len) + return; + end = pos + payload_len; + + if (end - pos < 2) + return; + dh_len = WPA_GET_BE16(pos); + pos += 2; + + if ((size_t) (end - pos) < dh_len) + return; + p = BN_bin2bn(pos, dh_len, NULL); + if (!p) + return; + + bits = BN_num_bits(p); + BN_free(p); + + conn->server_dh_prime_len = bits; + wpa_printf(MSG_DEBUG, "OpenSSL: Server DH prime length: %d bits", + conn->server_dh_prime_len); +} + +#endif /* CONFIG_SUITEB */ + + static void tls_msg_cb(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg) { @@ -1322,6 +1446,18 @@ static void tls_msg_cb(int write_p, int version, int content_type, conn->invalid_hb_used = 1; } } + +#ifdef CONFIG_SUITEB + /* + * Need to parse these handshake messages to be able to check DH prime + * length since OpenSSL does not expose the new cipher suite and DH + * parameters during handshake (e.g., for cert_cb() callback). + */ + if (content_type == 22 && pos && len > 0 && pos[0] == 2) + check_server_hello(conn, pos + 1, pos + len); + if (content_type == 22 && pos && len > 0 && pos[0] == 12) + check_server_key_exchange(ssl, conn, pos + 1, pos + len); +#endif /* CONFIG_SUITEB */ } @@ -1410,6 +1546,31 @@ int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) } +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn) +{ + ASN1_INTEGER *ser; + char *serial_num; + size_t len; + + if (!conn->peer_cert) + return NULL; + + ser = X509_get_serialNumber(conn->peer_cert); + if (!ser) + return NULL; + + len = ASN1_STRING_length(ser) * 2 + 1; + serial_num = os_malloc(len); + if (!serial_num) + return NULL; + wpa_snprintf_hex_uppercase(serial_num, len, + ASN1_STRING_get0_data(ser), + ASN1_STRING_length(ser)); + return serial_num; +} + + int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) @@ -1694,6 +1855,8 @@ static void openssl_tls_cert_event(struct tls_connection *conn, GENERAL_NAME *gen; void *ext; stack_index_t i; + ASN1_INTEGER *ser; + char serial_num[128]; #ifdef CONFIG_SHA256 u8 hash[32]; #endif /* CONFIG_SHA256 */ @@ -1722,6 +1885,14 @@ static void openssl_tls_cert_event(struct tls_connection *conn, ev.peer_cert.depth = depth; ev.peer_cert.subject = subject; + ser = X509_get_serialNumber(err_cert); + if (ser) { + wpa_snprintf_hex_uppercase(serial_num, sizeof(serial_num), + ASN1_STRING_get0_data(ser), + ASN1_STRING_length(ser)); + ev.peer_cert.serial_num = serial_num; + } + ext = X509_get_ext_d2i(err_cert, NID_subject_alt_name, NULL, NULL); for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { char *pos; @@ -1916,6 +2087,37 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) TLS_FAIL_SERVER_CHAIN_PROBE); } +#ifdef CONFIG_SUITEB + if (conn->flags & TLS_CONN_SUITEB) { + EVP_PKEY *pk; + RSA *rsa; + int len = -1; + + pk = X509_get_pubkey(err_cert); + if (pk) { + rsa = EVP_PKEY_get1_RSA(pk); + if (rsa) { + len = RSA_bits(rsa); + RSA_free(rsa); + } + EVP_PKEY_free(pk); + } + + if (len >= 0) { + wpa_printf(MSG_DEBUG, + "OpenSSL: RSA modulus size: %d bits", len); + if (len < 3072) { + preverify_ok = 0; + openssl_tls_fail_event( + conn, err_cert, err, + depth, buf, + "Insufficient RSA modulus size", + TLS_FAIL_INSUFFICIENT_KEY_LEN); + } + } + } +#endif /* CONFIG_SUITEB */ + #ifdef OPENSSL_IS_BORINGSSL if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) && preverify_ok) { @@ -2249,15 +2451,48 @@ static int tls_connection_set_subject_match(struct tls_connection *conn, } -static void tls_set_conn_flags(SSL *ssl, unsigned int flags) +#ifdef CONFIG_SUITEB +#if OPENSSL_VERSION_NUMBER >= 0x10002000L +static int suiteb_cert_cb(SSL *ssl, void *arg) +{ + struct tls_connection *conn = arg; + + /* + * This cert_cb() is not really the best location for doing a + * constraint check for the ServerKeyExchange message, but this seems to + * be the only place where the current OpenSSL sequence can be + * terminated cleanly with an TLS alert going out to the server. + */ + + if (!(conn->flags & TLS_CONN_SUITEB)) + return 1; + + /* DHE is enabled only with DHE-RSA-AES256-GCM-SHA384 */ + if (conn->cipher_suite != 0x9f) + return 1; + + if (conn->server_dh_prime_len >= 3072) + return 1; + + wpa_printf(MSG_DEBUG, + "OpenSSL: Server DH prime length (%d bits) not sufficient for Suite B RSA - reject handshake", + conn->server_dh_prime_len); + return 0; +} +#endif /* OPENSSL_VERSION_NUMBER */ +#endif /* CONFIG_SUITEB */ + + +static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags, + const char *openssl_ciphers) { + SSL *ssl = conn->ssl; + #ifdef SSL_OP_NO_TICKET if (flags & TLS_CONN_DISABLE_SESSION_TICKET) SSL_set_options(ssl, SSL_OP_NO_TICKET); -#ifdef SSL_clear_options else SSL_clear_options(ssl, SSL_OP_NO_TICKET); -#endif /* SSL_clear_options */ #endif /* SSL_OP_NO_TICKET */ #ifdef SSL_OP_NO_TLSv1 @@ -2278,6 +2513,118 @@ static void tls_set_conn_flags(SSL *ssl, unsigned int flags) else SSL_clear_options(ssl, SSL_OP_NO_TLSv1_2); #endif /* SSL_OP_NO_TLSv1_2 */ +#ifdef SSL_OP_NO_TLSv1_3 + if (flags & TLS_CONN_DISABLE_TLSv1_3) + SSL_set_options(ssl, SSL_OP_NO_TLSv1_3); + else + SSL_clear_options(ssl, SSL_OP_NO_TLSv1_3); +#endif /* SSL_OP_NO_TLSv1_3 */ +#ifdef CONFIG_SUITEB +#ifdef OPENSSL_IS_BORINGSSL + /* Start with defaults from BoringSSL */ + SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, NULL, 0); +#endif /* OPENSSL_IS_BORINGSSL */ +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + if (flags & TLS_CONN_SUITEB_NO_ECDH) { + const char *ciphers = "DHE-RSA-AES256-GCM-SHA384"; + + if (openssl_ciphers) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Override ciphers for Suite B (no ECDH): %s", + openssl_ciphers); + ciphers = openssl_ciphers; + } + if (SSL_set_cipher_list(ssl, ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B ciphers"); + return -1; + } + } else if (flags & TLS_CONN_SUITEB) { + EC_KEY *ecdh; + const char *ciphers = + "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384"; + int nid[1] = { NID_secp384r1 }; + + if (openssl_ciphers) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Override ciphers for Suite B: %s", + openssl_ciphers); + ciphers = openssl_ciphers; + } + if (SSL_set_cipher_list(ssl, ciphers) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B ciphers"); + return -1; + } + + if (SSL_set1_curves(ssl, nid, 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B curves"); + return -1; + } + + ecdh = EC_KEY_new_by_curve_name(NID_secp384r1); + if (!ecdh || SSL_set_tmp_ecdh(ssl, ecdh) != 1) { + EC_KEY_free(ecdh); + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set ECDH parameter"); + return -1; + } + EC_KEY_free(ecdh); + } + if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) { +#ifdef OPENSSL_IS_BORINGSSL + uint16_t sigalgs[1] = { SSL_SIGN_RSA_PKCS1_SHA384 }; + + if (SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, sigalgs, + 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B sigalgs"); + return -1; + } +#else /* OPENSSL_IS_BORINGSSL */ + /* ECDSA+SHA384 if need to add EC support here */ + if (SSL_set1_sigalgs_list(ssl, "RSA+SHA384") != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B sigalgs"); + return -1; + } +#endif /* OPENSSL_IS_BORINGSSL */ + + SSL_set_options(ssl, SSL_OP_NO_TLSv1); + SSL_set_options(ssl, SSL_OP_NO_TLSv1_1); + SSL_set_cert_cb(ssl, suiteb_cert_cb, conn); + } +#else /* OPENSSL_VERSION_NUMBER < 0x10002000L */ + if (flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) { + wpa_printf(MSG_ERROR, + "OpenSSL: Suite B RSA case not supported with this OpenSSL version"); + return -1; + } +#endif /* OPENSSL_VERSION_NUMBER */ + +#ifdef OPENSSL_IS_BORINGSSL + if (openssl_ciphers && os_strcmp(openssl_ciphers, "SUITEB192") == 0) { + uint16_t sigalgs[1] = { SSL_SIGN_ECDSA_SECP384R1_SHA384 }; + int nid[1] = { NID_secp384r1 }; + + if (SSL_set1_curves(ssl, nid, 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B curves"); + return -1; + } + + if (SSL_CTX_set_verify_algorithm_prefs(conn->ssl_ctx, sigalgs, + 1) != 1) { + wpa_printf(MSG_INFO, + "OpenSSL: Failed to set Suite B sigalgs"); + return -1; + } + } +#endif /* OPENSSL_IS_BORINGSSL */ +#endif /* CONFIG_SUITEB */ + + return 0; } @@ -2301,7 +2648,8 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); } - tls_set_conn_flags(conn->ssl, flags); + if (tls_set_conn_flags(conn, flags, NULL) < 0) + return -1; conn->flags = flags; SSL_set_accept_state(conn->ssl); @@ -2334,7 +2682,7 @@ static int tls_connection_client_cert(struct tls_connection *conn, return 0; #ifdef PKCS12_FUNCS -#if OPENSSL_VERSION_NUMBER < 0x10002000L +#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER) /* * Clear previously set extra chain certificates, if any, from PKCS#12 * processing in tls_parse_pkcs12() to allow OpenSSL to build a new @@ -2365,13 +2713,24 @@ static int tls_connection_client_cert(struct tls_connection *conn, int ret = -1; if (bio) { x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); - BIO_free(bio); } if (x509) { if (SSL_use_certificate(conn->ssl, x509) == 1) ret = 0; X509_free(x509); } + + /* Read additional certificates into the chain. */ + while (bio) { + x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL); + if (x509) { + /* Takes ownership of x509 */ + SSL_add0_chain_cert(conn->ssl, x509); + } else { + BIO_free(bio); + bio = NULL; + } + } return ret; } #endif /* ANDROID */ @@ -2430,16 +2789,6 @@ static int tls_global_client_cert(struct tls_data *data, } -static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) -{ - if (password == NULL) { - return 0; - } - os_strlcpy(buf, (char *) password, size); - return os_strlen(buf); -} - - #ifdef PKCS12_FUNCS static int tls_parse_pkcs12(struct tls_data *data, SSL *ssl, PKCS12 *p12, const char *passwd) @@ -2758,6 +3107,64 @@ static int tls_connection_engine_private_key(struct tls_connection *conn) } +#ifndef OPENSSL_NO_STDIO +static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) +{ + if (!password) + return 0; + os_strlcpy(buf, (const char *) password, size); + return os_strlen(buf); +} +#endif /* OPENSSL_NO_STDIO */ + + +static int tls_use_private_key_file(struct tls_data *data, SSL *ssl, + const char *private_key, + const char *private_key_passwd) +{ +#ifndef OPENSSL_NO_STDIO + BIO *bio; + EVP_PKEY *pkey; + int ret; + + /* First try ASN.1 (DER). */ + bio = BIO_new_file(private_key, "r"); + if (!bio) + return -1; + pkey = d2i_PrivateKey_bio(bio, NULL); + BIO_free(bio); + + if (pkey) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s (DER) --> loaded", __func__); + } else { + /* Try PEM with the provided password. */ + bio = BIO_new_file(private_key, "r"); + if (!bio) + return -1; + pkey = PEM_read_bio_PrivateKey(bio, NULL, tls_passwd_cb, + (void *) private_key_passwd); + BIO_free(bio); + if (!pkey) + return -1; + wpa_printf(MSG_DEBUG, "OpenSSL: %s (PEM) --> loaded", __func__); + /* Clear errors from the previous failed load. */ + ERR_clear_error(); + } + + if (ssl) + ret = SSL_use_PrivateKey(ssl, pkey); + else + ret = SSL_CTX_use_PrivateKey(data->ssl, pkey); + + EVP_PKEY_free(pkey); + return ret == 1 ? 0 : -1; +#else /* OPENSSL_NO_STDIO */ + wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); + return -1; +#endif /* OPENSSL_NO_STDIO */ +} + + static int tls_connection_private_key(struct tls_data *data, struct tls_connection *conn, const char *private_key, @@ -2765,23 +3172,11 @@ static int tls_connection_private_key(struct tls_data *data, const u8 *private_key_blob, size_t private_key_blob_len) { - SSL_CTX *ssl_ctx = data->ssl; - char *passwd; int ok; if (private_key == NULL && private_key_blob == NULL) return 0; - if (private_key_passwd) { - passwd = os_strdup(private_key_passwd); - if (passwd == NULL) - return -1; - } else - passwd = NULL; - - SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); - SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); - ok = 0; while (private_key_blob) { if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl, @@ -2812,7 +3207,8 @@ static int tls_connection_private_key(struct tls_data *data, } if (tls_read_pkcs12_blob(data, conn->ssl, private_key_blob, - private_key_blob_len, passwd) == 0) { + private_key_blob_len, + private_key_passwd) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> " "OK"); ok = 1; @@ -2823,29 +3219,14 @@ static int tls_connection_private_key(struct tls_data *data, } while (!ok && private_key) { -#ifndef OPENSSL_NO_STDIO - if (SSL_use_PrivateKey_file(conn->ssl, private_key, - SSL_FILETYPE_ASN1) == 1) { - wpa_printf(MSG_DEBUG, "OpenSSL: " - "SSL_use_PrivateKey_File (DER) --> OK"); + if (tls_use_private_key_file(data, conn->ssl, private_key, + private_key_passwd) == 0) { ok = 1; break; } - if (SSL_use_PrivateKey_file(conn->ssl, private_key, - SSL_FILETYPE_PEM) == 1) { - wpa_printf(MSG_DEBUG, "OpenSSL: " - "SSL_use_PrivateKey_File (PEM) --> OK"); - ok = 1; - break; - } -#else /* OPENSSL_NO_STDIO */ - wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", - __func__); -#endif /* OPENSSL_NO_STDIO */ - - if (tls_read_pkcs12(data, conn->ssl, private_key, passwd) - == 0) { + if (tls_read_pkcs12(data, conn->ssl, private_key, + private_key_passwd) == 0) { wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file " "--> OK"); ok = 1; @@ -2865,12 +3246,9 @@ static int tls_connection_private_key(struct tls_data *data, if (!ok) { tls_show_errors(MSG_INFO, __func__, "Failed to load private key"); - os_free(passwd); return -1; } ERR_clear_error(); - SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); - os_free(passwd); if (!SSL_check_private_key(conn->ssl)) { tls_show_errors(MSG_INFO, __func__, "Private key failed " @@ -2888,37 +3266,19 @@ static int tls_global_private_key(struct tls_data *data, const char *private_key_passwd) { SSL_CTX *ssl_ctx = data->ssl; - char *passwd; if (private_key == NULL) return 0; - if (private_key_passwd) { - passwd = os_strdup(private_key_passwd); - if (passwd == NULL) - return -1; - } else - passwd = NULL; - - SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); - SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); - if ( -#ifndef OPENSSL_NO_STDIO - SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, - SSL_FILETYPE_ASN1) != 1 && - SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, - SSL_FILETYPE_PEM) != 1 && -#endif /* OPENSSL_NO_STDIO */ - tls_read_pkcs12(data, NULL, private_key, passwd)) { + if (tls_use_private_key_file(data, NULL, private_key, + private_key_passwd) && + tls_read_pkcs12(data, NULL, private_key, private_key_passwd)) { tls_show_errors(MSG_INFO, __func__, "Failed to load private key"); - os_free(passwd); ERR_clear_error(); return -1; } - os_free(passwd); ERR_clear_error(); - SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); if (!SSL_CTX_check_private_key(ssl_ctx)) { tls_show_errors(MSG_INFO, __func__, @@ -3105,7 +3465,9 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, #ifdef OPENSSL_NEED_EAP_FAST_PRF static int openssl_get_keyblock_size(SSL *ssl) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) const EVP_CIPHER *c; const EVP_MD *h; int md_size; @@ -3252,8 +3614,7 @@ int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, static struct wpabuf * -openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data, - int server) +openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data) { int res; struct wpabuf *out_data; @@ -3271,7 +3632,7 @@ openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data, } /* Initiate TLS handshake or continue the existing handshake */ - if (server) + if (conn->server) res = SSL_accept(conn->ssl); else res = SSL_connect(conn->ssl); @@ -3286,8 +3647,57 @@ openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data, else { tls_show_errors(MSG_INFO, __func__, "SSL_connect"); conn->failed++; + if (!conn->server && !conn->client_hello_generated) { + /* The server would not understand TLS Alert + * before ClientHello, so simply terminate + * handshake on this type of error case caused + * by a likely internal error like no ciphers + * available. */ + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not generate ClientHello"); + conn->write_alerts++; + return NULL; + } + } + } + + if (!conn->server && !conn->failed) + conn->client_hello_generated = 1; + +#ifdef CONFIG_SUITEB + if ((conn->flags & TLS_CONN_SUITEB) && !conn->server && + os_strncmp(SSL_get_cipher(conn->ssl), "DHE-", 4) == 0 && + conn->server_dh_prime_len < 3072) { + struct tls_context *context = conn->context; + + /* + * This should not be reached since earlier cert_cb should have + * terminated the handshake. Keep this check here for extra + * protection if anything goes wrong with the more low-level + * checks based on having to parse the TLS handshake messages. + */ + wpa_printf(MSG_DEBUG, + "OpenSSL: Server DH prime length: %d bits", + conn->server_dh_prime_len); + + if (context->event_cb) { + union tls_event_data ev; + + os_memset(&ev, 0, sizeof(ev)); + ev.alert.is_local = 1; + ev.alert.type = "fatal"; + ev.alert.description = "insufficient security"; + context->event_cb(context->cb_ctx, TLS_ALERT, &ev); } + /* + * Could send a TLS Alert to the server, but for now, simply + * terminate handshake. + */ + conn->failed++; + conn->write_alerts++; + return NULL; } +#endif /* CONFIG_SUITEB */ /* Get the TLS handshake data to be sent to the server */ res = BIO_ctrl_pending(conn->ssl_out); @@ -3358,14 +3768,14 @@ openssl_get_appl_data(struct tls_connection *conn, size_t max_len) static struct wpabuf * openssl_connection_handshake(struct tls_connection *conn, const struct wpabuf *in_data, - struct wpabuf **appl_data, int server) + struct wpabuf **appl_data) { struct wpabuf *out_data; if (appl_data) *appl_data = NULL; - out_data = openssl_handshake(conn, in_data, server); + out_data = openssl_handshake(conn, in_data); if (out_data == NULL) return NULL; if (conn->invalid_hb_used) { @@ -3402,7 +3812,7 @@ tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, const struct wpabuf *in_data, struct wpabuf **appl_data) { - return openssl_connection_handshake(conn, in_data, appl_data, 0); + return openssl_connection_handshake(conn, in_data, appl_data); } @@ -3411,7 +3821,8 @@ struct wpabuf * tls_connection_server_handshake(void *tls_ctx, const struct wpabuf *in_data, struct wpabuf **appl_data) { - return openssl_connection_handshake(conn, in_data, appl_data, 1); + conn->server = 1; + return openssl_connection_handshake(conn, in_data, appl_data); } @@ -3506,7 +3917,7 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx, int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) { - return conn ? SSL_cache_hit(conn->ssl) : 0; + return conn ? SSL_session_reused(conn->ssl) : 0; } @@ -3747,7 +4158,7 @@ static int ocsp_resp_cb(SSL *s, void *arg) { struct tls_connection *conn = arg; const unsigned char *p; - int len, status, reason; + int len, status, reason, res; OCSP_RESPONSE *rsp; OCSP_BASICRESP *basic; OCSP_CERTID *id; @@ -3842,16 +4253,33 @@ static int ocsp_resp_cb(SSL *s, void *arg) return 0; } - id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer); + id = OCSP_cert_to_id(EVP_sha256(), conn->peer_cert, conn->peer_issuer); if (!id) { - wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier"); + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not create OCSP certificate identifier (SHA256)"); OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(rsp); return 0; } - if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, - &this_update, &next_update)) { + res = OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, + &this_update, &next_update); + if (!res) { + id = OCSP_cert_to_id(NULL, conn->peer_cert, conn->peer_issuer); + if (!id) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not create OCSP certificate identifier (SHA1)"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + return 0; + } + + res = OCSP_resp_find_status(basic, id, &status, &reason, + &produced_at, &this_update, + &next_update); + } + + if (!res) { wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s", (conn->flags & TLS_CONN_REQUIRE_OCSP) ? "" : " (OCSP not required)"); @@ -3935,6 +4363,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const char *cert_id = params->cert_id; const char *ca_cert_id = params->ca_cert_id; const char *engine_id = params->engine ? params->engine_id : NULL; + const char *ciphers; if (conn == NULL) return -1; @@ -3976,7 +4405,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, engine_id = "pkcs11"; #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) -#if OPENSSL_VERSION_NUMBER < 0x10100000L +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) if (params->flags & TLS_CONN_EAP_FAST) { wpa_printf(MSG_DEBUG, "OpenSSL: Use TLSv1_method() for EAP-FAST"); @@ -3987,6 +4416,17 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } } #endif +#if OPENSSL_VERSION_NUMBER >= 0x10101000L +#ifdef SSL_OP_NO_TLSv1_3 + if (params->flags & TLS_CONN_EAP_FAST) { + /* Need to disable TLS v1.3 at least for now since OpenSSL 1.1.1 + * refuses to start the handshake with the modified ciphersuite + * list (no TLS v1.3 ciphersuites included) for EAP-FAST. */ + wpa_printf(MSG_DEBUG, "OpenSSL: Disable TLSv1.3 for EAP-FAST"); + SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_3); + } +#endif /* SSL_OP_NO_TLSv1_3 */ +#endif #endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ while ((err = ERR_get_error())) { @@ -4045,15 +4485,27 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } - if (params->openssl_ciphers && - SSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) { + ciphers = params->openssl_ciphers; +#ifdef CONFIG_SUITEB +#ifdef OPENSSL_IS_BORINGSSL + if (ciphers && os_strcmp(ciphers, "SUITEB192") == 0) { + /* BoringSSL removed support for SUITEB192, so need to handle + * this with hardcoded ciphersuite and additional checks for + * other parameters. */ + ciphers = "ECDHE-ECDSA-AES256-GCM-SHA384"; + } +#endif /* OPENSSL_IS_BORINGSSL */ +#endif /* CONFIG_SUITEB */ + if (ciphers && SSL_set_cipher_list(conn->ssl, ciphers) != 1) { wpa_printf(MSG_INFO, "OpenSSL: Failed to set cipher string '%s'", - params->openssl_ciphers); + ciphers); return -1; } - tls_set_conn_flags(conn->ssl, params->flags); + if (tls_set_conn_flags(conn, params->flags, + params->openssl_ciphers) < 0) + return -1; #ifdef OPENSSL_IS_BORINGSSL if (params->flags & TLS_CONN_REQUEST_OCSP) { @@ -4120,10 +4572,8 @@ int tls_global_set_params(void *tls_ctx, #ifdef SSL_OP_NO_TICKET if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET) SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET); -#ifdef SSL_CTX_clear_options else SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET); -#endif /* SSL_clear_options */ #endif /* SSL_OP_NO_TICKET */ #ifdef HAVE_OCSP @@ -4159,7 +4609,9 @@ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, struct tls_connection *conn = arg; int ret; -#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ + (defined(LIBRESSL_VERSION_NUMBER) && \ + LIBRESSL_VERSION_NUMBER < 0x20700000L) if (conn == NULL || conn->session_ticket_cb == NULL) return 0; @@ -4212,11 +4664,10 @@ static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data, wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket " "extension", data, len); - conn->session_ticket = os_malloc(len); + conn->session_ticket = os_memdup(data, len); if (conn->session_ticket == NULL) return 0; - os_memcpy(conn->session_ticket, data, len); conn->session_ticket_len = len; return 1; diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c new file mode 100644 index 0000000000000..cc8c704466d2b --- /dev/null +++ b/src/crypto/tls_wolfssl.c @@ -0,0 +1,2175 @@ +/* + * SSL/TLS interface functions for wolfSSL TLS case + * Copyright (c) 2004-2017, 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 "common.h" +#include "crypto.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "tls.h" + +/* wolfSSL includes */ +#include <wolfssl/options.h> +#include <wolfssl/ssl.h> +#include <wolfssl/error-ssl.h> +#include <wolfssl/wolfcrypt/asn.h> + +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) +#define HAVE_AESGCM +#include <wolfssl/wolfcrypt/aes.h> +#endif + +#if !defined(CONFIG_FIPS) && \ + (defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || \ + defined(EAP_SERVER_FAST)) +#define WOLFSSL_NEED_EAP_FAST_PRF +#endif + +#define SECRET_LEN 48 +#define RAN_LEN 32 +#define SESSION_TICKET_LEN 256 + +static int tls_ref_count = 0; + +static int tls_ex_idx_session = 0; + + +/* tls input data for wolfSSL Read Callback */ +struct tls_in_data { + const struct wpabuf *in_data; + size_t consumed; /* how many bytes have we used already */ +}; + +/* tls output data for wolfSSL Write Callback */ +struct tls_out_data { + struct wpabuf *out_data; +}; + +struct tls_context { + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data); + void *cb_ctx; + int cert_in_cb; + char *ocsp_stapling_response; +}; + +static struct tls_context *tls_global = NULL; + +/* wolfssl tls_connection */ +struct tls_connection { + struct tls_context *context; + WOLFSSL *ssl; + int read_alerts; + int write_alerts; + int failed; + struct tls_in_data input; + struct tls_out_data output; + char *subject_match; + char *alt_subject_match; + char *suffix_match; + char *domain_match; + + u8 srv_cert_hash[32]; + + unsigned char client_random[RAN_LEN]; + unsigned char server_random[RAN_LEN]; + unsigned int flags; +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) + tls_session_ticket_cb session_ticket_cb; + void *session_ticket_cb_ctx; + byte session_ticket[SESSION_TICKET_LEN]; +#endif + unsigned int ca_cert_verify:1; + unsigned int cert_probe:1; + unsigned int server_cert_only:1; + unsigned int success_data:1; + + WOLFSSL_X509 *peer_cert; + WOLFSSL_X509 *peer_issuer; + WOLFSSL_X509 *peer_issuer_issuer; +}; + + +static struct tls_context * tls_context_new(const struct tls_config *conf) +{ + struct tls_context *context = os_zalloc(sizeof(*context)); + + if (!context) + return NULL; + + if (conf) { + context->event_cb = conf->event_cb; + context->cb_ctx = conf->cb_ctx; + context->cert_in_cb = conf->cert_in_cb; + } + + return context; +} + + +static void wolfssl_reset_in_data(struct tls_in_data *in, + const struct wpabuf *buf) +{ + /* old one not owned by us so don't free */ + in->in_data = buf; + in->consumed = 0; +} + + +static void wolfssl_reset_out_data(struct tls_out_data *out) +{ + /* old one not owned by us so don't free */ + out->out_data = wpabuf_alloc_copy("", 0); +} + + +/* wolfSSL I/O Receive CallBack */ +static int wolfssl_receive_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx) +{ + size_t get = sz; + struct tls_in_data *data = ctx; + + if (!data) + return -1; + + if (get > (wpabuf_len(data->in_data) - data->consumed)) + get = wpabuf_len(data->in_data) - data->consumed; + + os_memcpy(buf, wpabuf_head(data->in_data) + data->consumed, get); + data->consumed += get; + + if (get == 0) + return -2; /* WANT_READ */ + + return (int) get; +} + + +/* wolfSSL I/O Send CallBack */ +static int wolfssl_send_cb(WOLFSSL *ssl, char *buf, int sz, void *ctx) +{ + struct wpabuf *tmp; + struct tls_out_data *data = ctx; + + if (!data) + return -1; + + wpa_printf(MSG_DEBUG, "SSL: adding %d bytes", sz); + + tmp = wpabuf_alloc_copy(buf, sz); + if (!tmp) + return -1; + data->out_data = wpabuf_concat(data->out_data, tmp); + if (!data->out_data) + return -1; + + return sz; +} + + +static void remove_session_cb(WOLFSSL_CTX *ctx, WOLFSSL_SESSION *sess) +{ + struct wpabuf *buf; + + buf = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + if (!buf) + return; + wpa_printf(MSG_DEBUG, + "wolfSSL: Free application session data %p (sess %p)", + buf, sess); + wpabuf_free(buf); + + wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, NULL); +} + + +void * tls_init(const struct tls_config *conf) +{ + WOLFSSL_CTX *ssl_ctx; + struct tls_context *context; + const char *ciphers; + +#ifdef DEBUG_WOLFSSL + wolfSSL_Debugging_ON(); +#endif /* DEBUG_WOLFSSL */ + + context = tls_context_new(conf); + if (!context) + return NULL; + + if (tls_ref_count == 0) { + tls_global = context; + + if (wolfSSL_Init() < 0) + return NULL; + /* wolfSSL_Debugging_ON(); */ + } + + tls_ref_count++; + + /* start as client */ + ssl_ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()); + if (!ssl_ctx) { + tls_ref_count--; + if (context != tls_global) + os_free(context); + if (tls_ref_count == 0) { + os_free(tls_global); + tls_global = NULL; + } + } + wolfSSL_SetIORecv(ssl_ctx, wolfssl_receive_cb); + wolfSSL_SetIOSend(ssl_ctx, wolfssl_send_cb); + wolfSSL_CTX_set_ex_data(ssl_ctx, 0, context); + + if (conf->tls_session_lifetime > 0) { + wolfSSL_CTX_set_quiet_shutdown(ssl_ctx, 1); + wolfSSL_CTX_set_session_cache_mode(ssl_ctx, + SSL_SESS_CACHE_SERVER); + wolfSSL_CTX_set_timeout(ssl_ctx, conf->tls_session_lifetime); + wolfSSL_CTX_sess_set_remove_cb(ssl_ctx, remove_session_cb); + } else { + wolfSSL_CTX_set_session_cache_mode(ssl_ctx, + SSL_SESS_CACHE_CLIENT); + } + + if (conf && conf->openssl_ciphers) + ciphers = conf->openssl_ciphers; + else + ciphers = "ALL"; + if (wolfSSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) { + wpa_printf(MSG_ERROR, + "wolfSSL: Failed to set cipher string '%s'", + ciphers); + tls_deinit(ssl_ctx); + return NULL; + } + + return ssl_ctx; +} + + +void tls_deinit(void *ssl_ctx) +{ + struct tls_context *context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0); + + if (context != tls_global) + os_free(context); + + wolfSSL_CTX_free((WOLFSSL_CTX *) ssl_ctx); + + tls_ref_count--; + if (tls_ref_count == 0) { + wolfSSL_Cleanup(); + os_free(tls_global); + tls_global = NULL; + } +} + + +int tls_get_errors(void *tls_ctx) +{ +#ifdef DEBUG_WOLFSSL +#if 0 + unsigned long err; + + err = wolfSSL_ERR_peek_last_error_line(NULL, NULL); + if (err != 0) { + wpa_printf(MSG_INFO, "TLS - SSL error: %s", + wolfSSL_ERR_error_string(err, NULL)); + return 1; + } +#endif +#endif /* DEBUG_WOLFSSL */ + return 0; +} + + +struct tls_connection * tls_connection_init(void *tls_ctx) +{ + WOLFSSL_CTX *ssl_ctx = tls_ctx; + struct tls_connection *conn; + + wpa_printf(MSG_DEBUG, "SSL: connection init"); + + conn = os_zalloc(sizeof(*conn)); + if (!conn) + return NULL; + conn->ssl = wolfSSL_new(ssl_ctx); + if (!conn->ssl) { + os_free(conn); + return NULL; + } + + wolfSSL_SetIOReadCtx(conn->ssl, &conn->input); + wolfSSL_SetIOWriteCtx(conn->ssl, &conn->output); + wolfSSL_set_ex_data(conn->ssl, 0, conn); + conn->context = wolfSSL_CTX_get_ex_data(ssl_ctx, 0); + + /* Need randoms post-hanshake for EAP-FAST, export key and deriving + * session ID in EAP methods. */ + wolfSSL_KeepArrays(conn->ssl); + wolfSSL_KeepHandshakeResources(conn->ssl); + wolfSSL_UseClientSuites(conn->ssl); + + return conn; +} + + +void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn) +{ + if (!conn) + return; + + wpa_printf(MSG_DEBUG, "SSL: connection deinit"); + + /* parts */ + wolfSSL_free(conn->ssl); + os_free(conn->subject_match); + os_free(conn->alt_subject_match); + os_free(conn->suffix_match); + os_free(conn->domain_match); + + /* self */ + os_free(conn); +} + + +int tls_connection_established(void *tls_ctx, struct tls_connection *conn) +{ + return conn ? wolfSSL_is_init_finished(conn->ssl) : 0; +} + + +char * tls_connection_peer_serial_num(void *tls_ctx, + struct tls_connection *conn) +{ + /* TODO */ + return NULL; +} + + +int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) +{ + WOLFSSL_SESSION *session; + + if (!conn) + return -1; + + wpa_printf(MSG_DEBUG, "SSL: connection shutdown"); + + /* Set quiet as OpenSSL does */ + wolfSSL_set_quiet_shutdown(conn->ssl, 1); + wolfSSL_shutdown(conn->ssl); + + session = wolfSSL_get_session(conn->ssl); + if (wolfSSL_clear(conn->ssl) != 1) + return -1; + wolfSSL_set_session(conn->ssl, session); + + return 0; +} + + +static int tls_connection_set_subject_match(struct tls_connection *conn, + const char *subject_match, + const char *alt_subject_match, + const char *suffix_match, + const char *domain_match) +{ + os_free(conn->subject_match); + conn->subject_match = NULL; + if (subject_match) { + conn->subject_match = os_strdup(subject_match); + if (!conn->subject_match) + return -1; + } + + os_free(conn->alt_subject_match); + conn->alt_subject_match = NULL; + if (alt_subject_match) { + conn->alt_subject_match = os_strdup(alt_subject_match); + if (!conn->alt_subject_match) + return -1; + } + + os_free(conn->suffix_match); + conn->suffix_match = NULL; + if (suffix_match) { + conn->suffix_match = os_strdup(suffix_match); + if (!conn->suffix_match) + return -1; + } + + os_free(conn->domain_match); + conn->domain_match = NULL; + if (domain_match) { + conn->domain_match = os_strdup(domain_match); + if (!conn->domain_match) + return -1; + } + + return 0; +} + + +static int tls_connection_dh(struct tls_connection *conn, const char *dh_file, + const u8 *dh_blob, size_t blob_len) +{ + if (!dh_file && !dh_blob) + return 0; + + wolfSSL_set_accept_state(conn->ssl); + + if (dh_blob) { + if (wolfSSL_SetTmpDH_buffer(conn->ssl, dh_blob, blob_len, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, "SSL: use DH DER blob failed"); + return -1; + } + wpa_printf(MSG_DEBUG, "SSL: use DH blob OK"); + return 0; + } + + if (dh_file) { + wpa_printf(MSG_INFO, "SSL: use DH PEM file: %s", dh_file); + if (wolfSSL_SetTmpDH_file(conn->ssl, dh_file, + SSL_FILETYPE_PEM) < 0) { + wpa_printf(MSG_INFO, "SSL: use DH PEM file failed"); + if (wolfSSL_SetTmpDH_file(conn->ssl, dh_file, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, + "SSL: use DH DER file failed"); + return -1; + } + } + wpa_printf(MSG_DEBUG, "SSL: use DH file OK"); + return 0; + } + + return 0; +} + + +static int tls_connection_client_cert(struct tls_connection *conn, + const char *client_cert, + const u8 *client_cert_blob, + size_t blob_len) +{ + if (!client_cert && !client_cert_blob) + return 0; + + if (client_cert_blob) { + if (wolfSSL_use_certificate_chain_buffer_format( + conn->ssl, client_cert_blob, blob_len, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, + "SSL: use client cert DER blob failed"); + return -1; + } + wpa_printf(MSG_DEBUG, "SSL: use client cert blob OK"); + return 0; + } + + if (client_cert) { + if (wolfSSL_use_certificate_chain_file(conn->ssl, + client_cert) < 0) { + wpa_printf(MSG_INFO, + "SSL: use client cert PEM file failed"); + if (wolfSSL_use_certificate_chain_file_format( + conn->ssl, client_cert, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, + "SSL: use client cert DER file failed"); + return -1; + } + } + wpa_printf(MSG_DEBUG, "SSL: use client cert file OK"); + return 0; + } + + return 0; +} + + +static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) +{ + if (!password) + return 0; + os_strlcpy(buf, (char *) password, size); + return os_strlen(buf); +} + + +static int tls_connection_private_key(void *tls_ctx, + struct tls_connection *conn, + const char *private_key, + const char *private_key_passwd, + const u8 *private_key_blob, + size_t blob_len) +{ + WOLFSSL_CTX *ctx = tls_ctx; + char *passwd = NULL; + int ok = 0; + + if (!private_key && !private_key_blob) + return 0; + + if (private_key_passwd) { + passwd = os_strdup(private_key_passwd); + if (!passwd) + return -1; + } + + wolfSSL_CTX_set_default_passwd_cb(ctx, tls_passwd_cb); + wolfSSL_CTX_set_default_passwd_cb_userdata(ctx, passwd); + + if (private_key_blob) { + if (wolfSSL_use_PrivateKey_buffer(conn->ssl, + private_key_blob, blob_len, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, + "SSL: use private DER blob failed"); + } else { + wpa_printf(MSG_DEBUG, "SSL: use private key blob OK"); + ok = 1; + } + } + + if (!ok && private_key) { + if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key, + SSL_FILETYPE_PEM) < 0) { + wpa_printf(MSG_INFO, + "SSL: use private key PEM file failed"); + if (wolfSSL_use_PrivateKey_file(conn->ssl, private_key, + SSL_FILETYPE_ASN1) < 0) + { + wpa_printf(MSG_INFO, + "SSL: use private key DER file failed"); + } else { + ok = 1; + } + } else { + ok = 1; + } + + if (ok) + wpa_printf(MSG_DEBUG, "SSL: use private key file OK"); + } + + wolfSSL_CTX_set_default_passwd_cb(ctx, NULL); + os_free(passwd); + + if (!ok) + return -1; + + return 0; +} + + +static int tls_match_alt_subject_component(WOLFSSL_X509 *cert, int type, + const char *value, size_t len) +{ + WOLFSSL_ASN1_OBJECT *gen; + void *ext; + int found = 0; + int i; + + ext = wolfSSL_X509_get_ext_d2i(cert, ALT_NAMES_OID, NULL, NULL); + + for (i = 0; ext && i < wolfSSL_sk_num(ext); i++) { + gen = wolfSSL_sk_value(ext, i); + if (gen->type != type) + continue; + if (os_strlen((char *) gen->obj) == len && + os_memcmp(value, gen->obj, len) == 0) + found++; + } + + wolfSSL_sk_ASN1_OBJECT_free(ext); + + return found; +} + + +static int tls_match_alt_subject(WOLFSSL_X509 *cert, const char *match) +{ + int type; + const char *pos, *end; + size_t len; + + pos = match; + do { + if (os_strncmp(pos, "EMAIL:", 6) == 0) { + type = GEN_EMAIL; + pos += 6; + } else if (os_strncmp(pos, "DNS:", 4) == 0) { + type = GEN_DNS; + pos += 4; + } else if (os_strncmp(pos, "URI:", 4) == 0) { + type = GEN_URI; + pos += 4; + } else { + wpa_printf(MSG_INFO, + "TLS: Invalid altSubjectName match '%s'", + pos); + return 0; + } + end = os_strchr(pos, ';'); + while (end) { + if (os_strncmp(end + 1, "EMAIL:", 6) == 0 || + os_strncmp(end + 1, "DNS:", 4) == 0 || + os_strncmp(end + 1, "URI:", 4) == 0) + break; + end = os_strchr(end + 1, ';'); + } + if (end) + len = end - pos; + else + len = os_strlen(pos); + if (tls_match_alt_subject_component(cert, type, pos, len) > 0) + return 1; + pos = end + 1; + } while (end); + + return 0; +} + + +static int domain_suffix_match(const char *val, size_t len, const char *match, + int full) +{ + size_t i, match_len; + + /* Check for embedded nuls that could mess up suffix matching */ + for (i = 0; i < len; i++) { + if (val[i] == '\0') { + wpa_printf(MSG_DEBUG, + "TLS: Embedded null in a string - reject"); + return 0; + } + } + + match_len = os_strlen(match); + if (match_len > len || (full && match_len != len)) + return 0; + + if (os_strncasecmp(val + len - match_len, match, match_len) != 0) + return 0; /* no match */ + + if (match_len == len) + return 1; /* exact match */ + + if (val[len - match_len - 1] == '.') + return 1; /* full label match completes suffix match */ + + wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match"); + return 0; +} + + +static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full) +{ + WOLFSSL_ASN1_OBJECT *gen; + void *ext; + int i; + int j; + int dns_name = 0; + WOLFSSL_X509_NAME *name; + + wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s", + full ? "" : "suffix ", match); + + ext = wolfSSL_X509_get_ext_d2i(cert, ALT_NAMES_OID, NULL, NULL); + + for (j = 0; ext && j < wolfSSL_sk_num(ext); j++) { + gen = wolfSSL_sk_value(ext, j); + if (gen->type != ALT_NAMES_OID) + continue; + dns_name++; + wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName", + gen->obj, os_strlen((char *)gen->obj)); + if (domain_suffix_match((const char *) gen->obj, + os_strlen((char *) gen->obj), match, + full) == 1) { + wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found", + full ? "Match" : "Suffix match"); + wolfSSL_sk_ASN1_OBJECT_free(ext); + return 1; + } + } + wolfSSL_sk_ASN1_OBJECT_free(ext); + + if (dns_name) { + wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched"); + return 0; + } + + name = wolfSSL_X509_get_subject_name(cert); + i = -1; + for (;;) { + WOLFSSL_X509_NAME_ENTRY *e; + WOLFSSL_ASN1_STRING *cn; + + i = wolfSSL_X509_NAME_get_index_by_NID(name, ASN_COMMON_NAME, + i); + if (i == -1) + break; + e = wolfSSL_X509_NAME_get_entry(name, i); + if (!e) + continue; + cn = wolfSSL_X509_NAME_ENTRY_get_data(e); + if (!cn) + continue; + wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName", + cn->data, cn->length); + if (domain_suffix_match(cn->data, cn->length, match, full) == 1) + { + wpa_printf(MSG_DEBUG, "TLS: %s in commonName found", + full ? "Match" : "Suffix match"); + return 1; + } + } + + wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found", + full ? "" : "suffix "); + return 0; +} + + +static enum tls_fail_reason wolfssl_tls_fail_reason(int err) +{ + switch (err) { + case X509_V_ERR_CERT_REVOKED: + return TLS_FAIL_REVOKED; + case ASN_BEFORE_DATE_E: + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_CRL_NOT_YET_VALID: + return TLS_FAIL_NOT_YET_VALID; + case ASN_AFTER_DATE_E: + case X509_V_ERR_CERT_HAS_EXPIRED: + case X509_V_ERR_CRL_HAS_EXPIRED: + return TLS_FAIL_EXPIRED; + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + case X509_V_ERR_UNABLE_TO_GET_CRL: + case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: + case X509_V_ERR_CERT_CHAIN_TOO_LONG: + case X509_V_ERR_PATH_LENGTH_EXCEEDED: + case X509_V_ERR_INVALID_CA: + return TLS_FAIL_UNTRUSTED; + case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: + case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: + case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: + case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: + case X509_V_ERR_CERT_UNTRUSTED: + case X509_V_ERR_CERT_REJECTED: + return TLS_FAIL_BAD_CERTIFICATE; + default: + return TLS_FAIL_UNSPECIFIED; + } +} + + +static const char * wolfssl_tls_err_string(int err, const char *err_str) +{ + switch (err) { + case ASN_BEFORE_DATE_E: + return "certificate is not yet valid"; + case ASN_AFTER_DATE_E: + return "certificate has expired"; + default: + return err_str; + } +} + + +static struct wpabuf * get_x509_cert(WOLFSSL_X509 *cert) +{ + struct wpabuf *buf = NULL; + const u8 *data; + int cert_len; + + data = wolfSSL_X509_get_der(cert, &cert_len); + if (!data) + buf = wpabuf_alloc_copy(data, cert_len); + + return buf; +} + + +static void wolfssl_tls_fail_event(struct tls_connection *conn, + WOLFSSL_X509 *err_cert, int err, int depth, + const char *subject, const char *err_str, + enum tls_fail_reason reason) +{ + union tls_event_data ev; + struct wpabuf *cert = NULL; + struct tls_context *context = conn->context; + + if (!context->event_cb) + return; + + cert = get_x509_cert(err_cert); + os_memset(&ev, 0, sizeof(ev)); + ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ? + reason : wolfssl_tls_fail_reason(err); + ev.cert_fail.depth = depth; + ev.cert_fail.subject = subject; + ev.cert_fail.reason_txt = wolfssl_tls_err_string(err, err_str); + ev.cert_fail.cert = cert; + context->event_cb(context->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); + wpabuf_free(cert); +} + + +static void wolfssl_tls_cert_event(struct tls_connection *conn, + WOLFSSL_X509 *err_cert, int depth, + const char *subject) +{ + struct wpabuf *cert = NULL; + union tls_event_data ev; + struct tls_context *context = conn->context; + char *alt_subject[TLS_MAX_ALT_SUBJECT]; + int alt, num_alt_subject = 0; + WOLFSSL_ASN1_OBJECT *gen; + void *ext; + int i; +#ifdef CONFIG_SHA256 + u8 hash[32]; +#endif /* CONFIG_SHA256 */ + + if (!context->event_cb) + return; + + os_memset(&ev, 0, sizeof(ev)); + if (conn->cert_probe || (conn->flags & TLS_CONN_EXT_CERT_CHECK) || + context->cert_in_cb) { + cert = get_x509_cert(err_cert); + ev.peer_cert.cert = cert; + } + +#ifdef CONFIG_SHA256 + if (cert) { + const u8 *addr[1]; + size_t len[1]; + + addr[0] = wpabuf_head(cert); + len[0] = wpabuf_len(cert); + if (sha256_vector(1, addr, len, hash) == 0) { + ev.peer_cert.hash = hash; + ev.peer_cert.hash_len = sizeof(hash); + } + } +#endif /* CONFIG_SHA256 */ + + ev.peer_cert.depth = depth; + ev.peer_cert.subject = subject; + + ext = wolfSSL_X509_get_ext_d2i(err_cert, ALT_NAMES_OID, NULL, NULL); + for (i = 0; ext && i < wolfSSL_sk_num(ext); i++) { + char *pos; + + if (num_alt_subject == TLS_MAX_ALT_SUBJECT) + break; + gen = wolfSSL_sk_value((void *) ext, i); + if (gen->type != GEN_EMAIL && + gen->type != GEN_DNS && + gen->type != GEN_URI) + continue; + + pos = os_malloc(10 + os_strlen((char *) gen->obj) + 1); + if (!pos) + break; + alt_subject[num_alt_subject++] = pos; + + switch (gen->type) { + case GEN_EMAIL: + os_memcpy(pos, "EMAIL:", 6); + pos += 6; + break; + case GEN_DNS: + os_memcpy(pos, "DNS:", 4); + pos += 4; + break; + case GEN_URI: + os_memcpy(pos, "URI:", 4); + pos += 4; + break; + } + + os_memcpy(pos, gen->obj, os_strlen((char *)gen->obj)); + pos += os_strlen((char *)gen->obj); + *pos = '\0'; + } + wolfSSL_sk_ASN1_OBJECT_free(ext); + + for (alt = 0; alt < num_alt_subject; alt++) + ev.peer_cert.altsubject[alt] = alt_subject[alt]; + ev.peer_cert.num_altsubject = num_alt_subject; + + context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev); + wpabuf_free(cert); + for (alt = 0; alt < num_alt_subject; alt++) + os_free(alt_subject[alt]); +} + + +static int tls_verify_cb(int preverify_ok, WOLFSSL_X509_STORE_CTX *x509_ctx) +{ + char buf[256]; + WOLFSSL_X509 *err_cert; + int err, depth; + WOLFSSL *ssl; + struct tls_connection *conn; + struct tls_context *context; + char *match, *altmatch, *suffix_match, *domain_match; + const char *err_str; + + err_cert = wolfSSL_X509_STORE_CTX_get_current_cert(x509_ctx); + if (!err_cert) { + wpa_printf(MSG_DEBUG, "wolfSSL: No Cert"); + return 0; + } + + err = wolfSSL_X509_STORE_CTX_get_error(x509_ctx); + depth = wolfSSL_X509_STORE_CTX_get_error_depth(x509_ctx); + ssl = wolfSSL_X509_STORE_CTX_get_ex_data( + x509_ctx, wolfSSL_get_ex_data_X509_STORE_CTX_idx()); + wolfSSL_X509_NAME_oneline(wolfSSL_X509_get_subject_name(err_cert), buf, + sizeof(buf)); + + conn = wolfSSL_get_ex_data(ssl, 0); + if (!conn) { + wpa_printf(MSG_DEBUG, "wolfSSL: No ex_data"); + return 0; + } + + if (depth == 0) + conn->peer_cert = err_cert; + else if (depth == 1) + conn->peer_issuer = err_cert; + else if (depth == 2) + conn->peer_issuer_issuer = err_cert; + + context = conn->context; + match = conn->subject_match; + altmatch = conn->alt_subject_match; + suffix_match = conn->suffix_match; + domain_match = conn->domain_match; + + if (!preverify_ok && !conn->ca_cert_verify) + preverify_ok = 1; + if (!preverify_ok && depth > 0 && conn->server_cert_only) + preverify_ok = 1; + if (!preverify_ok && (conn->flags & TLS_CONN_DISABLE_TIME_CHECKS) && + (err == X509_V_ERR_CERT_HAS_EXPIRED || + err == ASN_AFTER_DATE_E || err == ASN_BEFORE_DATE_E || + err == X509_V_ERR_CERT_NOT_YET_VALID)) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Ignore certificate validity time mismatch"); + preverify_ok = 1; + } + + err_str = wolfSSL_X509_verify_cert_error_string(err); + +#ifdef CONFIG_SHA256 + /* + * Do not require preverify_ok so we can explicity allow otherwise + * invalid pinned server certificates. + */ + if (depth == 0 && conn->server_cert_only) { + struct wpabuf *cert; + + cert = get_x509_cert(err_cert); + if (!cert) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Could not fetch server certificate data"); + preverify_ok = 0; + } else { + u8 hash[32]; + const u8 *addr[1]; + size_t len[1]; + + addr[0] = wpabuf_head(cert); + len[0] = wpabuf_len(cert); + if (sha256_vector(1, addr, len, hash) < 0 || + os_memcmp(conn->srv_cert_hash, hash, 32) != 0) { + err_str = "Server certificate mismatch"; + err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN; + preverify_ok = 0; + } else if (!preverify_ok) { + /* + * Certificate matches pinned certificate, allow + * regardless of other problems. + */ + wpa_printf(MSG_DEBUG, + "wolfSSL: Ignore validation issues for a pinned server certificate"); + preverify_ok = 1; + } + wpabuf_free(cert); + } + } +#endif /* CONFIG_SHA256 */ + + if (!preverify_ok) { + wpa_printf(MSG_WARNING, + "TLS: Certificate verification failed, error %d (%s) depth %d for '%s'", + err, err_str, depth, buf); + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + err_str, TLS_FAIL_UNSPECIFIED); + return preverify_ok; + } + + wpa_printf(MSG_DEBUG, + "TLS: %s - preverify_ok=%d err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'", + __func__, preverify_ok, err, err_str, + conn->ca_cert_verify, depth, buf); + if (depth == 0 && match && os_strstr(buf, match) == NULL) { + wpa_printf(MSG_WARNING, + "TLS: Subject '%s' did not match with '%s'", + buf, match); + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Subject mismatch", + TLS_FAIL_SUBJECT_MISMATCH); + } else if (depth == 0 && altmatch && + !tls_match_alt_subject(err_cert, altmatch)) { + wpa_printf(MSG_WARNING, + "TLS: altSubjectName match '%s' not found", + altmatch); + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "AltSubject mismatch", + TLS_FAIL_ALTSUBJECT_MISMATCH); + } else if (depth == 0 && suffix_match && + !tls_match_suffix(err_cert, suffix_match, 0)) { + wpa_printf(MSG_WARNING, + "TLS: Domain suffix match '%s' not found", + suffix_match); + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Domain suffix mismatch", + TLS_FAIL_DOMAIN_SUFFIX_MISMATCH); + } else if (depth == 0 && domain_match && + !tls_match_suffix(err_cert, domain_match, 1)) { + wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found", + domain_match); + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Domain mismatch", + TLS_FAIL_DOMAIN_MISMATCH); + } else { + wolfssl_tls_cert_event(conn, err_cert, depth, buf); + } + + if (conn->cert_probe && preverify_ok && depth == 0) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Reject server certificate on probe-only run"); + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Server certificate chain probe", + TLS_FAIL_SERVER_CHAIN_PROBE); + } + +#ifdef HAVE_OCSP_WOLFSSL + if (depth == 0 && (conn->flags & TLS_CONN_REQUEST_OCSP) && + preverify_ok) { + enum ocsp_result res; + + res = check_ocsp_resp(conn->ssl_ctx, conn->ssl, err_cert, + conn->peer_issuer, + conn->peer_issuer_issuer); + if (res == OCSP_REVOKED) { + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "certificate revoked", + TLS_FAIL_REVOKED); + if (err == X509_V_OK) + X509_STORE_CTX_set_error( + x509_ctx, X509_V_ERR_CERT_REVOKED); + } else if (res != OCSP_GOOD && + (conn->flags & TLS_CONN_REQUIRE_OCSP)) { + preverify_ok = 0; + wolfssl_tls_fail_event(conn, err_cert, err, depth, buf, + "bad certificate status response", + TLS_FAIL_UNSPECIFIED); + } + } +#endif /* HAVE_OCSP_WOLFSSL */ + if (depth == 0 && preverify_ok && context->event_cb != NULL) + context->event_cb(context->cb_ctx, + TLS_CERT_CHAIN_SUCCESS, NULL); + + return preverify_ok; +} + + +static int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn, + const char *ca_cert, + const u8 *ca_cert_blob, size_t blob_len, + const char *ca_path) +{ + WOLFSSL_CTX *ctx = tls_ctx; + + wolfSSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + conn->ca_cert_verify = 1; + + if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Probe for server certificate chain"); + conn->cert_probe = 1; + conn->ca_cert_verify = 0; + return 0; + } + + if (ca_cert && os_strncmp(ca_cert, "hash://", 7) == 0) { +#ifdef CONFIG_SHA256 + const char *pos = ca_cert + 7; + + if (os_strncmp(pos, "server/sha256/", 14) != 0) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Unsupported ca_cert hash value '%s'", + ca_cert); + return -1; + } + pos += 14; + if (os_strlen(pos) != 32 * 2) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Unexpected SHA256 hash length in ca_cert '%s'", + ca_cert); + return -1; + } + if (hexstr2bin(pos, conn->srv_cert_hash, 32) < 0) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Invalid SHA256 hash value in ca_cert '%s'", + ca_cert); + return -1; + } + conn->server_cert_only = 1; + wpa_printf(MSG_DEBUG, + "wolfSSL: Checking only server certificate match"); + return 0; +#else /* CONFIG_SHA256 */ + wpa_printf(MSG_INFO, + "No SHA256 included in the build - cannot validate server certificate hash"); + return -1; +#endif /* CONFIG_SHA256 */ + } + + if (ca_cert_blob) { + if (wolfSSL_CTX_load_verify_buffer(ctx, ca_cert_blob, blob_len, + SSL_FILETYPE_ASN1) != + SSL_SUCCESS) { + wpa_printf(MSG_INFO, "SSL: failed to load CA blob"); + return -1; + } + wpa_printf(MSG_DEBUG, "SSL: use CA cert blob OK"); + return 0; + } + + if (ca_cert || ca_path) { + WOLFSSL_X509_STORE *cm = wolfSSL_X509_STORE_new(); + + if (!cm) { + wpa_printf(MSG_INFO, + "SSL: failed to create certificate store"); + return -1; + } + wolfSSL_CTX_set_cert_store(ctx, cm); + + if (wolfSSL_CTX_load_verify_locations(ctx, ca_cert, ca_path) != + SSL_SUCCESS) { + wpa_printf(MSG_INFO, + "SSL: failed to load ca_cert as PEM"); + + if (!ca_cert) + return -1; + + if (wolfSSL_CTX_der_load_verify_locations( + ctx, ca_cert, SSL_FILETYPE_ASN1) != + SSL_SUCCESS) { + wpa_printf(MSG_INFO, + "SSL: failed to load ca_cert as DER"); + return -1; + } + } + return 0; + } + + conn->ca_cert_verify = 0; + return 0; +} + + +static void tls_set_conn_flags(WOLFSSL *ssl, unsigned int flags) +{ +#ifdef HAVE_SESSION_TICKET +#if 0 + if (!(flags & TLS_CONN_DISABLE_SESSION_TICKET)) + wolfSSL_UseSessionTicket(ssl); +#endif +#endif /* HAVE_SESSION_TICKET */ + + if (flags & TLS_CONN_DISABLE_TLSv1_0) + wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1); + if (flags & TLS_CONN_DISABLE_TLSv1_1) + wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_1); + if (flags & TLS_CONN_DISABLE_TLSv1_2) + wolfSSL_set_options(ssl, SSL_OP_NO_TLSv1_2); +} + + +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params) +{ + wpa_printf(MSG_DEBUG, "SSL: set params"); + + if (tls_connection_set_subject_match(conn, params->subject_match, + params->altsubject_match, + params->suffix_match, + params->domain_match) < 0) { + wpa_printf(MSG_INFO, "Error setting subject match"); + return -1; + } + + if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert, + params->ca_cert_blob, + params->ca_cert_blob_len, + params->ca_path) < 0) { + wpa_printf(MSG_INFO, "Error setting CA cert"); + return -1; + } + + if (tls_connection_client_cert(conn, params->client_cert, + params->client_cert_blob, + params->client_cert_blob_len) < 0) { + wpa_printf(MSG_INFO, "Error setting client cert"); + return -1; + } + + if (tls_connection_private_key(tls_ctx, conn, params->private_key, + params->private_key_passwd, + params->private_key_blob, + params->private_key_blob_len) < 0) { + wpa_printf(MSG_INFO, "Error setting private key"); + return -1; + } + + if (tls_connection_dh(conn, params->dh_file, params->dh_blob, + params->dh_blob_len) < 0) { + wpa_printf(MSG_INFO, "Error setting DH"); + return -1; + } + + if (params->openssl_ciphers && + wolfSSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "wolfSSL: Failed to set cipher string '%s'", + params->openssl_ciphers); + return -1; + } + + tls_set_conn_flags(conn->ssl, params->flags); + +#ifdef HAVE_CERTIFICATE_STATUS_REQUEST + if (params->flags & TLS_CONN_REQUEST_OCSP) { + if (wolfSSL_UseOCSPStapling(conn->ssl, WOLFSSL_CSR_OCSP, + WOLFSSL_CSR_OCSP_USE_NONCE) != + SSL_SUCCESS) + return -1; + wolfSSL_CTX_EnableOCSP(tls_ctx, 0); + } +#endif /* HAVE_CERTIFICATE_STATUS_REQUEST */ +#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2 + if (params->flags & TLS_CONN_REQUEST_OCSP) { + if (wolfSSL_UseOCSPStaplingV2(conn->ssl, + WOLFSSL_CSR2_OCSP_MULTI, 0) != + SSL_SUCCESS) + return -1; + wolfSSL_CTX_EnableOCSP(tls_ctx, 0); + } +#endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */ +#if !defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \ + !defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) +#ifdef HAVE_OCSP + if (params->flags & TLS_CONN_REQUEST_OCSP) + wolfSSL_CTX_EnableOCSP(ctx, 0); +#else /* HAVE_OCSP */ + if (params->flags & TLS_CONN_REQUIRE_OCSP) { + wpa_printf(MSG_INFO, + "wolfSSL: No OCSP support included - reject configuration"); + return -1; + } + if (params->flags & TLS_CONN_REQUEST_OCSP) { + wpa_printf(MSG_DEBUG, + "wolfSSL: No OCSP support included - allow optional OCSP case to continue"); + } +#endif /* HAVE_OCSP */ +#endif /* !HAVE_CERTIFICATE_STATUS_REQUEST && + * !HAVE_CERTIFICATE_STATUS_REQUEST_V2 */ + + conn->flags = params->flags; + + return 0; +} + + +static int tls_global_ca_cert(void *ssl_ctx, const char *ca_cert) +{ + WOLFSSL_CTX *ctx = ssl_ctx; + + if (ca_cert) { + if (wolfSSL_CTX_load_verify_locations(ctx, ca_cert, NULL) != 1) + { + wpa_printf(MSG_WARNING, + "Failed to load root certificates"); + return -1; + } + + wpa_printf(MSG_DEBUG, + "TLS: Trusted root certificate(s) loaded"); + } + + return 0; +} + + +static int tls_global_client_cert(void *ssl_ctx, const char *client_cert) +{ + WOLFSSL_CTX *ctx = ssl_ctx; + + if (!client_cert) + return 0; + + if (wolfSSL_CTX_use_certificate_chain_file_format(ctx, client_cert, + SSL_FILETYPE_ASN1) != + SSL_SUCCESS && + wolfSSL_CTX_use_certificate_chain_file(ctx, client_cert) != + SSL_SUCCESS) { + wpa_printf(MSG_INFO, "Failed to load client certificate"); + return -1; + } + + wpa_printf(MSG_DEBUG, "SSL: Loaded global client certificate: %s", + client_cert); + + return 0; +} + + +static int tls_global_private_key(void *ssl_ctx, const char *private_key, + const char *private_key_passwd) +{ + WOLFSSL_CTX *ctx = ssl_ctx; + char *passwd = NULL; + int ret = 0; + + if (!private_key) + return 0; + + if (private_key_passwd) { + passwd = os_strdup(private_key_passwd); + if (!passwd) + return -1; + } + + wolfSSL_CTX_set_default_passwd_cb(ctx, tls_passwd_cb); + wolfSSL_CTX_set_default_passwd_cb_userdata(ctx, passwd); + + if (wolfSSL_CTX_use_PrivateKey_file(ctx, private_key, + SSL_FILETYPE_ASN1) != 1 && + wolfSSL_CTX_use_PrivateKey_file(ctx, private_key, + SSL_FILETYPE_PEM) != 1) { + wpa_printf(MSG_INFO, "Failed to load private key"); + ret = -1; + } + + wpa_printf(MSG_DEBUG, "SSL: Loaded global private key"); + + os_free(passwd); + wolfSSL_CTX_set_default_passwd_cb(ctx, NULL); + + return ret; +} + + +static int tls_global_dh(void *ssl_ctx, const char *dh_file, + const u8 *dh_blob, size_t blob_len) +{ + WOLFSSL_CTX *ctx = ssl_ctx; + + if (!dh_file && !dh_blob) + return 0; + + if (dh_blob) { + if (wolfSSL_CTX_SetTmpDH_buffer(ctx, dh_blob, blob_len, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, + "SSL: global use DH DER blob failed"); + return -1; + } + wpa_printf(MSG_DEBUG, "SSL: global use DH blob OK"); + return 0; + } + + if (dh_file) { + if (wolfSSL_CTX_SetTmpDH_file(ctx, dh_file, SSL_FILETYPE_PEM) < + 0) { + wpa_printf(MSG_INFO, + "SSL: global use DH PEM file failed"); + if (wolfSSL_CTX_SetTmpDH_file(ctx, dh_file, + SSL_FILETYPE_ASN1) < 0) { + wpa_printf(MSG_INFO, + "SSL: global use DH DER file failed"); + return -1; + } + } + wpa_printf(MSG_DEBUG, "SSL: global use DH file OK"); + return 0; + } + + return 0; +} + + +#ifdef HAVE_OCSP + +int ocsp_status_cb(void *unused, const char *url, int url_sz, + unsigned char *request, int request_sz, + unsigned char **response) +{ + size_t len; + + (void) unused; + + if (!url) { + wpa_printf(MSG_DEBUG, + "wolfSSL: OCSP status callback - no response configured"); + *response = NULL; + return 0; + } + + *response = (unsigned char *) os_readfile(url, &len); + if (!*response) { + wpa_printf(MSG_DEBUG, + "wolfSSL: OCSP status callback - could not read response file"); + return -1; + } + wpa_printf(MSG_DEBUG, + "wolfSSL: OCSP status callback - send cached response"); + return len; +} + + +void ocsp_resp_free_cb(void *ocsp_stapling_response, unsigned char *response) +{ + os_free(response); +} + +#endif /* HAVE_OCSP */ + + +int tls_global_set_params(void *tls_ctx, + const struct tls_connection_params *params) +{ + wpa_printf(MSG_DEBUG, "SSL: global set params"); + + if (tls_global_ca_cert(tls_ctx, params->ca_cert) < 0) { + wpa_printf(MSG_INFO, "SSL: Failed to load ca cert file '%s'", + params->ca_cert); + return -1; + } + + if (tls_global_client_cert(tls_ctx, params->client_cert) < 0) { + wpa_printf(MSG_INFO, + "SSL: Failed to load client cert file '%s'", + params->client_cert); + return -1; + } + + if (tls_global_private_key(tls_ctx, params->private_key, + params->private_key_passwd) < 0) { + wpa_printf(MSG_INFO, + "SSL: Failed to load private key file '%s'", + params->private_key); + return -1; + } + + if (tls_global_dh(tls_ctx, params->dh_file, params->dh_blob, + params->dh_blob_len) < 0) { + wpa_printf(MSG_INFO, "SSL: Failed to load DH file '%s'", + params->dh_file); + return -1; + } + + if (params->openssl_ciphers && + wolfSSL_CTX_set_cipher_list(tls_ctx, + params->openssl_ciphers) != 1) { + wpa_printf(MSG_INFO, + "wolfSSL: Failed to set cipher string '%s'", + params->openssl_ciphers); + return -1; + } + +#ifdef HAVE_SESSION_TICKET + /* Session ticket is off by default - can't disable once on. */ + if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET)) + wolfSSL_CTX_UseSessionTicket(tls_ctx); +#endif /* HAVE_SESSION_TICKET */ + +#ifdef HAVE_OCSP + if (params->ocsp_stapling_response) { + wolfSSL_CTX_SetOCSP_OverrideURL(tls_ctx, + params->ocsp_stapling_response); + wolfSSL_CTX_SetOCSP_Cb(tls_ctx, ocsp_status_cb, + ocsp_resp_free_cb, NULL); + } +#endif /* HAVE_OCSP */ + + return 0; +} + + +int tls_global_set_verify(void *tls_ctx, int check_crl) +{ + wpa_printf(MSG_DEBUG, "SSL: global set verify: %d", check_crl); + + if (check_crl) { + /* Hack to Enable CRLs. */ + wolfSSL_CTX_LoadCRLBuffer(tls_ctx, NULL, 0, SSL_FILETYPE_PEM); + } + + return 0; +} + + +int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, + int verify_peer, unsigned int flags, + const u8 *session_ctx, size_t session_ctx_len) +{ + if (!conn) + return -1; + + wpa_printf(MSG_DEBUG, "SSL: set verify: %d", verify_peer); + + if (verify_peer) { + conn->ca_cert_verify = 1; + wolfSSL_set_verify(conn->ssl, SSL_VERIFY_PEER | + SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + tls_verify_cb); + } else { + conn->ca_cert_verify = 0; + wolfSSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); + } + + wolfSSL_set_accept_state(conn->ssl); + + /* TODO: do we need to fake a session like OpenSSL does here? */ + + return 0; +} + + +static struct wpabuf * wolfssl_handshake(struct tls_connection *conn, + const struct wpabuf *in_data, + int server) +{ + int res; + + wolfssl_reset_out_data(&conn->output); + + /* Initiate TLS handshake or continue the existing handshake */ + if (server) { + wolfSSL_set_accept_state(conn->ssl); + res = wolfSSL_accept(conn->ssl); + wpa_printf(MSG_DEBUG, "SSL: wolfSSL_accept: %d", res); + } else { + wolfSSL_set_connect_state(conn->ssl); + res = wolfSSL_connect(conn->ssl); + wpa_printf(MSG_DEBUG, "SSL: wolfSSL_connect: %d", res); + } + + if (res != 1) { + int err = wolfSSL_get_error(conn->ssl, res); + + if (err == SSL_ERROR_WANT_READ) { + wpa_printf(MSG_DEBUG, + "SSL: wolfSSL_connect - want more data"); + } else if (err == SSL_ERROR_WANT_WRITE) { + wpa_printf(MSG_DEBUG, + "SSL: wolfSSL_connect - want to write"); + } else { + char msg[80]; + + wpa_printf(MSG_DEBUG, + "SSL: wolfSSL_connect - failed %s", + wolfSSL_ERR_error_string(err, msg)); + conn->failed++; + } + } + + return conn->output.out_data; +} + + +static struct wpabuf * wolfssl_get_appl_data(struct tls_connection *conn, + size_t max_len) +{ + int res; + struct wpabuf *appl_data = wpabuf_alloc(max_len + 100); + + if (!appl_data) + return NULL; + + res = wolfSSL_read(conn->ssl, wpabuf_mhead(appl_data), + wpabuf_size(appl_data)); + if (res < 0) { + int err = wolfSSL_get_error(conn->ssl, res); + + if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { + wpa_printf(MSG_DEBUG, + "SSL: No Application Data included"); + } else { + char msg[80]; + + wpa_printf(MSG_DEBUG, + "Failed to read possible Application Data %s", + wolfSSL_ERR_error_string(err, msg)); + } + + wpabuf_free(appl_data); + return NULL; + } + + wpabuf_put(appl_data, res); + wpa_hexdump_buf_key(MSG_MSGDUMP, + "SSL: Application Data in Finished message", + appl_data); + return appl_data; +} + + +static struct wpabuf * +wolfssl_connection_handshake(struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data, int server) +{ + struct wpabuf *out_data; + + wolfssl_reset_in_data(&conn->input, in_data); + + if (appl_data) + *appl_data = NULL; + + out_data = wolfssl_handshake(conn, in_data, server); + if (!out_data) + return NULL; + + if (wolfSSL_is_init_finished(conn->ssl)) { + wpa_printf(MSG_DEBUG, + "wolfSSL: Handshake finished - resumed=%d", + tls_connection_resumed(NULL, conn)); + if (appl_data && in_data) + *appl_data = wolfssl_get_appl_data(conn, + wpabuf_len(in_data)); + } + + return out_data; +} + + +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + return wolfssl_connection_handshake(conn, in_data, appl_data, 0); +} + + +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + return wolfssl_connection_handshake(conn, in_data, appl_data, 1); +} + + +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ + int res; + + if (!conn) + return NULL; + + wpa_printf(MSG_DEBUG, "SSL: encrypt: %ld bytes", wpabuf_len(in_data)); + + wolfssl_reset_out_data(&conn->output); + + res = wolfSSL_write(conn->ssl, wpabuf_head(in_data), + wpabuf_len(in_data)); + if (res < 0) { + int err = wolfSSL_get_error(conn->ssl, res); + char msg[80]; + + wpa_printf(MSG_INFO, "Encryption failed - SSL_write: %s", + wolfSSL_ERR_error_string(err, msg)); + return NULL; + } + + return conn->output.out_data; +} + + +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ + int res; + struct wpabuf *buf; + + if (!conn) + return NULL; + + wpa_printf(MSG_DEBUG, "SSL: decrypt"); + + wolfssl_reset_in_data(&conn->input, in_data); + + /* Read decrypted data for further processing */ + /* + * Even though we try to disable TLS compression, it is possible that + * this cannot be done with all TLS libraries. Add extra buffer space + * to handle the possibility of the decrypted data being longer than + * input data. + */ + buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); + if (!buf) + return NULL; + res = wolfSSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf)); + if (res < 0) { + wpa_printf(MSG_INFO, "Decryption failed - SSL_read"); + wpabuf_free(buf); + return NULL; + } + wpabuf_put(buf, res); + + wpa_printf(MSG_DEBUG, "SSL: decrypt: %ld bytes", wpabuf_len(buf)); + + return buf; +} + + +int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn) +{ + return conn ? wolfSSL_session_reused(conn->ssl) : 0; +} + + +int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, + u8 *ciphers) +{ + char buf[128], *pos, *end; + u8 *c; + int ret; + + if (!conn || !conn->ssl || !ciphers) + return -1; + + buf[0] = '\0'; + pos = buf; + end = pos + sizeof(buf); + + c = ciphers; + while (*c != TLS_CIPHER_NONE) { + const char *suite; + + switch (*c) { + case TLS_CIPHER_RC4_SHA: + suite = "RC4-SHA"; + break; + case TLS_CIPHER_AES128_SHA: + suite = "AES128-SHA"; + break; + case TLS_CIPHER_RSA_DHE_AES128_SHA: + suite = "DHE-RSA-AES128-SHA"; + break; + case TLS_CIPHER_ANON_DH_AES128_SHA: + suite = "ADH-AES128-SHA"; + break; + case TLS_CIPHER_RSA_DHE_AES256_SHA: + suite = "DHE-RSA-AES256-SHA"; + break; + case TLS_CIPHER_AES256_SHA: + suite = "AES256-SHA"; + break; + default: + wpa_printf(MSG_DEBUG, + "TLS: Unsupported cipher selection: %d", *c); + return -1; + } + ret = os_snprintf(pos, end - pos, ":%s", suite); + if (os_snprintf_error(end - pos, ret)) + break; + pos += ret; + + c++; + } + + wpa_printf(MSG_DEBUG, "wolfSSL: cipher suites: %s", buf + 1); + + if (wolfSSL_set_cipher_list(conn->ssl, buf + 1) != 1) { + wpa_printf(MSG_DEBUG, "Cipher suite configuration failed"); + return -1; + } + + return 0; +} + + +int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + WOLFSSL_CIPHER *cipher; + const char *name; + + if (!conn || !conn->ssl) + return -1; + + cipher = wolfSSL_get_current_cipher(conn->ssl); + if (!cipher) + return -1; + + name = wolfSSL_CIPHER_get_name(cipher); + if (!name) + return -1; + + if (os_strcmp(name, "SSL_RSA_WITH_RC4_128_SHA") == 0) + os_strlcpy(buf, "RC4-SHA", buflen); + else if (os_strcmp(name, "TLS_RSA_WITH_AES_128_CBC_SHA") == 0) + os_strlcpy(buf, "AES128-SHA", buflen); + else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA") == 0) + os_strlcpy(buf, "DHE-RSA-AES128-SHA", buflen); + else if (os_strcmp(name, "TLS_DH_anon_WITH_AES_128_CBC_SHA") == 0) + os_strlcpy(buf, "ADH-AES128-SHA", buflen); + else if (os_strcmp(name, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA") == 0) + os_strlcpy(buf, "DHE-RSA-AES256-SHA", buflen); + else if (os_strcmp(name, "TLS_RSA_WITH_AES_256_CBC_SHA") == 0) + os_strlcpy(buf, "AES256-SHA", buflen); + else + os_strlcpy(buf, name, buflen); + + return 0; +} + + +int tls_connection_enable_workaround(void *tls_ctx, + struct tls_connection *conn) +{ + /* no empty fragments in wolfSSL for now */ + return 0; +} + + +int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn) +{ + if (!conn) + return -1; + + return conn->failed; +} + + +int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) +{ + if (!conn) + return -1; + + /* TODO: this is not incremented anywhere */ + return conn->read_alerts; +} + + +int tls_connection_get_write_alerts(void *tls_ctx, + struct tls_connection *conn) +{ + if (!conn) + return -1; + + /* TODO: this is not incremented anywhere */ + return conn->write_alerts; +} + + + +int tls_get_library_version(char *buf, size_t buf_len) +{ + return os_snprintf(buf, buf_len, "wolfSSL build=%s run=%s", + WOLFSSL_VERSION, wolfSSL_lib_version()); +} + +int tls_get_version(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + const char *name; + + if (!conn || !conn->ssl) + return -1; + + name = wolfSSL_get_version(conn->ssl); + if (!name) + return -1; + + os_strlcpy(buf, name, buflen); + return 0; +} + + +int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn, + struct tls_random *keys) +{ + WOLFSSL *ssl; + + if (!conn || !keys) + return -1; + ssl = conn->ssl; + if (!ssl) + return -1; + + os_memset(keys, 0, sizeof(*keys)); + keys->client_random = conn->client_random; + keys->client_random_len = wolfSSL_get_client_random( + ssl, conn->client_random, sizeof(conn->client_random)); + keys->server_random = conn->server_random; + keys->server_random_len = wolfSSL_get_server_random( + ssl, conn->server_random, sizeof(conn->server_random)); + + return 0; +} + + +int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn, + const char *label, u8 *out, size_t out_len) +{ + if (!conn || wolfSSL_make_eap_keys(conn->ssl, out, out_len, label) != 0) + return -1; + return 0; +} + + +#define SEED_LEN (RAN_LEN + RAN_LEN) + +int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn, + u8 *out, size_t out_len) +{ + byte seed[SEED_LEN]; + int ret = -1; + WOLFSSL *ssl; + byte *tmp_out; + byte *_out; + int skip = 0; + byte *master_key; + unsigned int master_key_len; + byte *server_random; + unsigned int server_len; + byte *client_random; + unsigned int client_len; + + if (!conn || !conn->ssl) + return -1; + ssl = conn->ssl; + + skip = 2 * (wolfSSL_GetKeySize(ssl) + wolfSSL_GetHmacSize(ssl) + + wolfSSL_GetIVSize(ssl)); + + tmp_out = os_malloc(skip + out_len); + if (!tmp_out) + return -1; + _out = tmp_out; + + wolfSSL_get_keys(ssl, &master_key, &master_key_len, &server_random, + &server_len, &client_random, &client_len); + os_memcpy(seed, server_random, RAN_LEN); + os_memcpy(seed + RAN_LEN, client_random, RAN_LEN); + + if (wolfSSL_GetVersion(ssl) == WOLFSSL_TLSV1_2) { + tls_prf_sha256(master_key, master_key_len, + "key expansion", seed, sizeof(seed), + _out, skip + out_len); + ret = 0; + } else { + ret = tls_prf_sha1_md5(master_key, master_key_len, + "key expansion", seed, sizeof(seed), + _out, skip + out_len); + } + + os_memset(master_key, 0, master_key_len); + if (ret == 0) + os_memcpy(out, _out + skip, out_len); + bin_clear_free(tmp_out, skip + out_len); + + return ret; +} + + +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) + +int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len) +{ + (void) ssl_ctx; + + if (!conn || !conn->ssl || ext_type != 35) + return -1; + + if (wolfSSL_set_SessionTicket(conn->ssl, data, + (unsigned int) data_len) != 1) + return -1; + + return 0; +} + + +static int tls_sess_sec_cb(WOLFSSL *s, void *secret, int *secret_len, void *arg) +{ + struct tls_connection *conn = arg; + int ret; + unsigned char client_random[RAN_LEN]; + unsigned char server_random[RAN_LEN]; + word32 ticket_len = sizeof(conn->session_ticket); + + if (!conn || !conn->session_ticket_cb) + return 1; + + if (wolfSSL_get_client_random(s, client_random, + sizeof(client_random)) == 0 || + wolfSSL_get_server_random(s, server_random, + sizeof(server_random)) == 0 || + wolfSSL_get_SessionTicket(s, conn->session_ticket, + &ticket_len) != 1) + return 1; + + if (ticket_len == 0) + return 0; + + ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx, + conn->session_ticket, ticket_len, + client_random, server_random, secret); + if (ret <= 0) + return 1; + + *secret_len = SECRET_LEN; + return 0; +} + +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ + + +int tls_connection_set_session_ticket_cb(void *tls_ctx, + struct tls_connection *conn, + tls_session_ticket_cb cb, + void *ctx) +{ +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) + conn->session_ticket_cb = cb; + conn->session_ticket_cb_ctx = ctx; + + if (cb) { + if (wolfSSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb, + conn) != 1) + return -1; + } else { + if (wolfSSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1) + return -1; + } + + return 0; +#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ + return -1; +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ +} + + +void tls_connection_set_success_data_resumed(struct tls_connection *conn) +{ + wpa_printf(MSG_DEBUG, + "wolfSSL: Success data accepted for resumed session"); +} + + +void tls_connection_remove_session(struct tls_connection *conn) +{ + WOLFSSL_SESSION *sess; + + sess = wolfSSL_get_session(conn->ssl); + if (!sess) + return; + + wolfSSL_SSL_SESSION_set_timeout(sess, 0); + wpa_printf(MSG_DEBUG, + "wolfSSL: Removed cached session to disable session resumption"); +} + + +void tls_connection_set_success_data(struct tls_connection *conn, + struct wpabuf *data) +{ + WOLFSSL_SESSION *sess; + struct wpabuf *old; + + wpa_printf(MSG_DEBUG, "wolfSSL: Set success data"); + + sess = wolfSSL_get_session(conn->ssl); + if (!sess) { + wpa_printf(MSG_DEBUG, + "wolfSSL: No session found for success data"); + goto fail; + } + + old = wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session); + if (old) { + wpa_printf(MSG_DEBUG, "wolfSSL: Replacing old success data %p", + old); + wpabuf_free(old); + } + if (wolfSSL_SESSION_set_ex_data(sess, tls_ex_idx_session, data) != 1) + goto fail; + + wpa_printf(MSG_DEBUG, "wolfSSL: Stored success data %p", data); + conn->success_data = 1; + return; + +fail: + wpa_printf(MSG_INFO, "wolfSSL: Failed to store success data"); + wpabuf_free(data); +} + + +const struct wpabuf * +tls_connection_get_success_data(struct tls_connection *conn) +{ + WOLFSSL_SESSION *sess; + + wpa_printf(MSG_DEBUG, "wolfSSL: Get success data"); + + sess = wolfSSL_get_session(conn->ssl); + if (!sess) + return NULL; + return wolfSSL_SESSION_get_ex_data(sess, tls_ex_idx_session); +} diff --git a/src/drivers/driver.h b/src/drivers/driver.h index a449cc9347352..4ac9f16a0efcf 100644 --- a/src/drivers/driver.h +++ b/src/drivers/driver.h @@ -1,6 +1,6 @@ /* * Driver interface definition - * 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. @@ -21,6 +21,10 @@ #include "common/defs.h" #include "common/ieee802_11_defs.h" +#include "common/wpa_common.h" +#ifdef CONFIG_MACSEC +#include "pae/ieee802_1x_kay.h" +#endif /* CONFIG_MACSEC */ #include "utils/list.h" #define HOSTAPD_CHAN_DISABLED 0x00000001 @@ -61,6 +65,10 @@ /* Filter unicast IP packets encrypted using the GTK */ #define WPA_DATA_FRAME_FILTER_FLAG_GTK BIT(2) +#define HOSTAPD_DFS_REGION_FCC 1 +#define HOSTAPD_DFS_REGION_ETSI 2 +#define HOSTAPD_DFS_REGION_JP 3 + /** * enum reg_change_initiator - Regulatory change initiator */ @@ -133,6 +141,29 @@ struct hostapd_channel_data { unsigned int dfs_cac_ms; }; +#define HE_MAX_NUM_SS 8 +#define HE_MAX_PHY_CAPAB_SIZE 3 + +/** + * struct he_ppe_threshold - IEEE 802.11ax HE PPE Threshold + */ +struct he_ppe_threshold { + u32 numss_m1; + u32 ru_count; + u32 ppet16_ppet8_ru3_ru0[HE_MAX_NUM_SS]; +}; + +/** + * struct he_capabilities - IEEE 802.11ax HE capabilities + */ +struct he_capabilities { + u8 he_supported; + u32 phy_cap[HE_MAX_PHY_CAPAB_SIZE]; + u32 mac_cap; + u32 mcs; + struct he_ppe_threshold ppet; +}; + #define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0) #define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1) @@ -191,6 +222,11 @@ struct hostapd_hw_modes { u8 vht_mcs_set[8]; unsigned int flags; /* HOSTAPD_MODE_FLAG_* */ + + /** + * he_capab - HE (IEEE 802.11ax) capabilities + */ + struct he_capabilities he_capab; }; @@ -233,6 +269,9 @@ struct hostapd_hw_modes { * @est_throughput: Estimated throughput in kbps (this is calculated during * scan result processing if left zero by the driver wrapper) * @snr: Signal-to-noise ratio in dB (calculated during scan result processing) + * @parent_tsf: Time when the Beacon/Probe Response frame was received in terms + * of TSF of the BSS specified by %tsf_bssid. + * @tsf_bssid: The BSS that %parent_tsf TSF time refers to. * @ie_len: length of the following IE field in octets * @beacon_ie_len: length of the following Beacon IE field in octets * @@ -263,6 +302,8 @@ struct wpa_scan_res { unsigned int age; unsigned int est_throughput; int snr; + u64 parent_tsf; + u8 tsf_bssid[ETH_ALEN]; size_t ie_len; size_t beacon_ie_len; /* Followed by ie_len + beacon_ie_len octets of IE data */ @@ -447,6 +488,15 @@ struct wpa_driver_scan_params { unsigned int sched_scan_plans_num; /** + * sched_scan_start_delay - Delay to use before starting the 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. + */ + u32 sched_scan_start_delay; + + /** * bssid - Specific BSSID to scan for * * This optional parameter can be used to replace the default wildcard @@ -455,6 +505,80 @@ struct wpa_driver_scan_params { */ const u8 *bssid; + /** + * scan_cookie - Unique identification representing the scan request + * + * This scan_cookie carries a unique identification representing the + * scan request if the host driver/kernel supports concurrent scan + * requests. This cookie is returned from the corresponding driver + * interface. + * + * Note: Unlike other parameters in this structure, scan_cookie is used + * only to return information instead of setting parameters for the + * scan. + */ + u64 scan_cookie; + + /** + * duration - Dwell time on each channel + * + * This optional parameter can be used to set the dwell time on each + * channel. In TUs. + */ + u16 duration; + + /** + * duration_mandatory - Whether the specified duration is mandatory + * + * If this is set, the duration specified by the %duration field is + * mandatory (and the driver should reject the scan request if it is + * unable to comply with the specified duration), otherwise it is the + * maximum duration and the actual duration may be shorter. + */ + unsigned int duration_mandatory:1; + + /** + * relative_rssi_set - Whether relative RSSI parameters are set + */ + unsigned int relative_rssi_set:1; + + /** + * relative_rssi - Relative RSSI for reporting better BSSs + * + * Amount of RSSI by which a BSS should be better than the current + * connected BSS to report the new BSS to user space. + */ + s8 relative_rssi; + + /** + * relative_adjust_band - Band to which RSSI should be adjusted + * + * The relative_adjust_rssi should be added to the band specified + * by relative_adjust_band. + */ + enum set_band relative_adjust_band; + + /** + * relative_adjust_rssi - RSSI to be added to relative_adjust_band + * + * An amount of relative_band_rssi should be added to the BSSs that + * belong to the band specified by relative_adjust_band while comparing + * with other bands for BSS reporting. + */ + s8 relative_adjust_rssi; + + /** + * oce_scan + * + * Enable the following OCE scan features: (WFA OCE TechSpec v1.0) + * - Accept broadcast Probe Response frame. + * - Probe Request frame deferral and suppression. + * - Max Channel Time - driver fills FILS request params IE with + * Maximum Channel Time. + * - Send 1st Probe Request frame in rate of minimum 5.5 Mbps. + */ + unsigned int oce_scan:1; + /* * NOTE: Whenever adding new parameters here, please make sure * wpa_scan_clone_params() and wpa_scan_free_params() get updated with @@ -485,17 +609,18 @@ struct wpa_driver_auth_params { int p2p; /** - * sae_data - SAE elements for Authentication frame + * auth_data - Additional elements for Authentication frame * * This buffer starts with the Authentication transaction sequence - * number field. If SAE is not used, this pointer is %NULL. + * number field. If no special handling of such elements is needed, this + * pointer is %NULL. This is used with SAE and FILS. */ - const u8 *sae_data; + const u8 *auth_data; /** - * sae_data_len - Length of sae_data buffer in octets + * auth_data_len - Length of auth_data buffer in octets */ - size_t sae_data_len; + size_t auth_data_len; }; /** @@ -577,6 +702,68 @@ struct hostapd_freq_params { }; /** + * struct wpa_driver_sta_auth_params - Authentication parameters + * Data for struct wpa_driver_ops::sta_auth(). + */ +struct wpa_driver_sta_auth_params { + + /** + * own_addr - Source address and BSSID for authentication frame + */ + const u8 *own_addr; + + /** + * addr - MAC address of the station to associate + */ + const u8 *addr; + + /** + * seq - authentication sequence number + */ + u16 seq; + + /** + * status - authentication response status code + */ + u16 status; + + /** + * ie - authentication frame ie buffer + */ + const u8 *ie; + + /** + * len - ie buffer length + */ + size_t len; + + /** + * fils_auth - Indicates whether FILS authentication is being performed + */ + int fils_auth; + + /** + * fils_anonce - ANonce (required for FILS) + */ + u8 fils_anonce[WPA_NONCE_LEN]; + + /** + * fils_snonce - SNonce (required for FILS) + */ + u8 fils_snonce[WPA_NONCE_LEN]; + + /** + * fils_kek - key for encryption (required for FILS) + */ + u8 fils_kek[WPA_KEK_MAX_LEN]; + + /** + * fils_kek_len - Length of the fils_kek in octets (required for FILS) + */ + size_t fils_kek_len; +}; + +/** * struct wpa_driver_associate_params - Association parameters * Data for struct wpa_driver_ops::associate(). */ @@ -639,7 +826,7 @@ struct wpa_driver_associate_params { * WPA information element to be included in (Re)Association * Request (including information element id and length). Use * of this WPA IE is optional. If the driver generates the WPA - * IE, it can use pairwise_suite, group_suite, and + * IE, it can use pairwise_suite, group_suite, group_mgmt_suite, and * key_mgmt_suite to select proper algorithms. In this case, * the driver has to notify wpa_supplicant about the used WPA * IE by generating an event that the interface code will @@ -679,6 +866,13 @@ struct wpa_driver_associate_params { unsigned int group_suite; /** + * mgmt_group_suite - Selected group management cipher suite (WPA_CIPHER_*) + * + * This is usually ignored if @wpa_ie is used. + */ + unsigned int mgmt_group_suite; + + /** * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*) * * This is usually ignored if @wpa_ie is used. @@ -717,43 +911,6 @@ struct wpa_driver_associate_params { enum mfp_options mgmt_frame_protection; /** - * ft_ies - IEEE 802.11r / FT information elements - * If the supplicant is using IEEE 802.11r (FT) and has the needed keys - * for fast transition, this parameter is set to include the IEs that - * are to be sent in the next FT Authentication Request message. - * update_ft_ies() handler is called to update the IEs for further - * FT messages in the sequence. - * - * The driver should use these IEs only if the target AP is advertising - * the same mobility domain as the one included in the MDIE here. - * - * In ap_scan=2 mode, the driver can use these IEs when moving to a new - * AP after the initial association. These IEs can only be used if the - * target AP is advertising support for FT and is using the same MDIE - * and SSID as the current AP. - * - * The driver is responsible for reporting the FT IEs received from the - * AP's response using wpa_supplicant_event() with EVENT_FT_RESPONSE - * type. update_ft_ies() handler will then be called with the FT IEs to - * include in the next frame in the authentication sequence. - */ - const u8 *ft_ies; - - /** - * ft_ies_len - Length of ft_ies in bytes - */ - size_t ft_ies_len; - - /** - * ft_md - FT Mobility domain (6 octets) (also included inside ft_ies) - * - * This value is provided to allow the driver interface easier access - * to the current mobility domain. This value is set to %NULL if no - * mobility domain is currently active. - */ - const u8 *ft_md; - - /** * passphrase - RSN passphrase for PSK * * This value is made available only for WPA/WPA2-Personal (PSK) and @@ -882,6 +1039,64 @@ struct wpa_driver_associate_params { * AP as usual. Valid for DMG network only. */ int pbss; + + /** + * fils_kek - KEK for FILS association frame protection (AES-SIV) + */ + const u8 *fils_kek; + + /** + * fils_kek_len: Length of fils_kek in bytes + */ + size_t fils_kek_len; + + /** + * fils_nonces - Nonces for FILS association frame protection + * (AES-SIV AAD) + */ + const u8 *fils_nonces; + + /** + * fils_nonces_len: Length of fils_nonce in bytes + */ + size_t fils_nonces_len; + + /** + * fils_erp_username - Username part of keyName-NAI + */ + const u8 *fils_erp_username; + + /** + * fils_erp_username_len - Length of fils_erp_username in bytes + */ + size_t fils_erp_username_len; + + /** + * fils_erp_realm - Realm/domain name to use in FILS ERP + */ + const u8 *fils_erp_realm; + + /** + * fils_erp_realm_len - Length of fils_erp_realm in bytes + */ + size_t fils_erp_realm_len; + + /** + * fils_erp_next_seq_num - The next sequence number to use in FILS ERP + * messages + */ + u16 fils_erp_next_seq_num; + + /** + * fils_erp_rrk - Re-authentication root key (rRK) for the keyName-NAI + * specified by fils_erp_username@fils_erp_realm. + */ + const u8 *fils_erp_rrk; + + /** + * fils_erp_rrk_len - Length of fils_erp_rrk in bytes + */ + size_t fils_erp_rrk_len; }; enum hide_ssid { @@ -940,6 +1155,22 @@ struct wpa_driver_ap_params { int *basic_rates; /** + * beacon_rate: Beacon frame data rate + * + * This parameter can be used to set a specific Beacon frame data rate + * for the BSS. The interpretation of this value depends on the + * rate_type (legacy: in 100 kbps units, HT: HT-MCS, VHT: VHT-MCS). If + * beacon_rate == 0 and rate_type == 0 (BEACON_RATE_LEGACY), the default + * Beacon frame data rate is used. + */ + unsigned int beacon_rate; + + /** + * beacon_rate_type: Beacon data rate type (legacy/HT/VHT) + */ + enum beacon_rate_type rate_type; + + /** * proberesp - Probe Response template * * This is used by drivers that reply to Probe Requests internally in @@ -1115,6 +1346,27 @@ struct wpa_driver_ap_params { * infrastructure BSS. Valid only for DMG network. */ int pbss; + + /** + * multicast_to_unicast - Whether to use multicast_to_unicast + * + * If this is non-zero, the AP is requested to perform multicast to + * unicast conversion for ARP, IPv4, and IPv6 frames (possibly within + * 802.1Q). If enabled, such frames are to be sent to each station + * separately, with the DA replaced by their own MAC address rather + * than the group address. + * + * Note that this may break certain expectations of the receiver, such + * as the ability to drop unicast IP packets received within multicast + * L2 frames, or the ability to not send ICMP destination unreachable + * messages for packets received in L2 multicast (which is required, + * but the receiver can't tell the difference if this new option is + * enabled.) + * + * This also doesn't implement the 802.11 DMS (directed multicast + * service). + */ + int multicast_to_unicast; }; struct wpa_driver_mesh_bss_params { @@ -1122,6 +1374,7 @@ struct wpa_driver_mesh_bss_params { #define WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT 0x00000002 #define WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS 0x00000004 #define WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE 0x00000008 +#define WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD 0x00000010 /* * TODO: Other mesh configuration parameters would go here. * See NL80211_MESHCONF_* for all the mesh config parameters. @@ -1130,6 +1383,7 @@ struct wpa_driver_mesh_bss_params { int auto_plinks; int peer_link_timeout; int max_peer_links; + int rssi_threshold; u16 ht_opmode; }; @@ -1164,6 +1418,12 @@ struct wpa_driver_capa { #define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK 0x00000080 #define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B 0x00000100 #define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192 0x00000200 +#define WPA_DRIVER_CAPA_KEY_MGMT_OWE 0x00000400 +#define WPA_DRIVER_CAPA_KEY_MGMT_DPP 0x00000800 +#define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 0x00001000 +#define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 0x00002000 +#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 0x00004000 +#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 0x00008000 /** Bitfield of supported key management suites */ unsigned int key_mgmt; @@ -1286,6 +1546,39 @@ struct wpa_driver_capa { #define WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE 0x0000010000000000ULL /** Driver supports P2P Listen offload */ #define WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD 0x0000020000000000ULL +/** Driver supports FILS */ +#define WPA_DRIVER_FLAGS_SUPPORT_FILS 0x0000040000000000ULL +/** Driver supports Beacon frame TX rate configuration (legacy rates) */ +#define WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY 0x0000080000000000ULL +/** Driver supports Beacon frame TX rate configuration (HT rates) */ +#define WPA_DRIVER_FLAGS_BEACON_RATE_HT 0x0000100000000000ULL +/** Driver supports Beacon frame TX rate configuration (VHT rates) */ +#define WPA_DRIVER_FLAGS_BEACON_RATE_VHT 0x0000200000000000ULL +/** Driver supports mgmt_tx with random TX address in non-connected state */ +#define WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA 0x0000400000000000ULL +/** Driver supports mgmt_tx with random TX addr in connected state */ +#define WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED 0x0000800000000000ULL +/** Driver supports better BSS reporting with sched_scan in connected mode */ +#define WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI 0x0001000000000000ULL +/** Driver supports HE capabilities */ +#define WPA_DRIVER_FLAGS_HE_CAPABILITIES 0x0002000000000000ULL +/** Driver supports FILS shared key offload */ +#define WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD 0x0004000000000000ULL +/** Driver supports all OCE STA specific mandatory features */ +#define WPA_DRIVER_FLAGS_OCE_STA 0x0008000000000000ULL +/** Driver supports all OCE AP specific mandatory features */ +#define WPA_DRIVER_FLAGS_OCE_AP 0x0010000000000000ULL +/** + * Driver supports all OCE STA-CFON specific mandatory features only. + * If a driver sets this bit but not the %WPA_DRIVER_FLAGS_OCE_AP, the + * userspace shall assume that this driver may not support all OCE AP + * functionality but can support only OCE STA-CFON functionality. + */ +#define WPA_DRIVER_FLAGS_OCE_STA_CFON 0x0020000000000000ULL +/** Driver supports MFP-optional in the connect command */ +#define WPA_DRIVER_FLAGS_MFP_OPTIONAL 0x0040000000000000ULL +/** Driver is a self-managed regulatory device */ +#define WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY 0x0080000000000000ULL u64 flags; #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \ @@ -1386,6 +1679,11 @@ struct wpa_driver_capa { */ #define WPA_DRIVER_FLAGS_SUPPORT_RRM 0x00000010 +/** Driver supports setting the scan dwell time */ +#define WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL 0x00000020 +/** Driver supports Beacon Report Measurement */ +#define WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT 0x00000040 + u32 rrm_flags; /* Driver concurrency capabilities */ @@ -1402,18 +1700,35 @@ struct wpa_driver_capa { struct hostapd_data; +#define STA_DRV_DATA_TX_MCS BIT(0) +#define STA_DRV_DATA_RX_MCS BIT(1) +#define STA_DRV_DATA_TX_VHT_MCS BIT(2) +#define STA_DRV_DATA_RX_VHT_MCS BIT(3) +#define STA_DRV_DATA_TX_VHT_NSS BIT(4) +#define STA_DRV_DATA_RX_VHT_NSS BIT(5) +#define STA_DRV_DATA_TX_SHORT_GI BIT(6) +#define STA_DRV_DATA_RX_SHORT_GI BIT(7) +#define STA_DRV_DATA_LAST_ACK_RSSI BIT(8) + struct hostap_sta_driver_data { unsigned long rx_packets, tx_packets; unsigned long long rx_bytes, tx_bytes; int bytes_64bit; /* whether 64-bit byte counters are supported */ unsigned long current_tx_rate; + unsigned long current_rx_rate; unsigned long inactive_msec; - unsigned long flags; + unsigned long flags; /* bitfield of STA_DRV_DATA_* */ unsigned long num_ps_buf_frames; unsigned long tx_retry_failed; unsigned long tx_retry_count; - int last_rssi; - int last_ack_rssi; + s8 last_ack_rssi; + s8 signal; + u8 rx_vhtmcs; + u8 tx_vhtmcs; + u8 rx_mcs; + u8 tx_mcs; + u8 rx_vht_nss; + u8 tx_vht_nss; }; struct hostapd_sta_add_params { @@ -1576,6 +1891,17 @@ enum wnm_oper { WNM_SLEEP_TFS_IE_DEL /* AP delete the TFS IE */ }; +/* enum smps_mode - SMPS mode definitions */ +enum smps_mode { + SMPS_AUTOMATIC, + SMPS_OFF, + SMPS_DYNAMIC, + SMPS_STATIC, + + /* Keep last */ + SMPS_INVALID, +}; + /* enum chan_width - Channel width definitions */ enum chan_width { CHAN_WIDTH_20_NOHT, @@ -1587,8 +1913,21 @@ enum chan_width { CHAN_WIDTH_UNKNOWN }; +#define WPA_INVALID_NOISE 9999 + /** * struct wpa_signal_info - Information about channel signal quality + * @frequency: control frequency + * @above_threshold: true if the above threshold was crossed + * (relevant for a CQM event) + * @current_signal: in dBm + * @avg_signal: in dBm + * @avg_beacon_signal: in dBm + * @current_noise: %WPA_INVALID_NOISE if not supported + * @current_txrate: current TX rate + * @chanwidth: channel width + * @center_frq1: center frequency for the first segment + * @center_frq2: center frequency for the second segment (if relevant) */ struct wpa_signal_info { u32 frequency; @@ -1720,6 +2059,67 @@ struct drv_acs_params { const int *freq_list; }; +struct wpa_bss_trans_info { + u8 mbo_transition_reason; + u8 n_candidates; + u8 *bssid; +}; + +struct wpa_bss_candidate_info { + u8 num; + struct candidate_list { + u8 bssid[ETH_ALEN]; + u8 is_accept; + u32 reject_reason; + } *candidates; +}; + +struct wpa_pmkid_params { + const u8 *bssid; + const u8 *ssid; + size_t ssid_len; + const u8 *fils_cache_id; + const u8 *pmkid; + const u8 *pmk; + size_t pmk_len; +}; + +/* Mask used to specify which connection parameters have to be updated */ +enum wpa_drv_update_connect_params_mask { + WPA_DRV_UPDATE_ASSOC_IES = BIT(0), + WPA_DRV_UPDATE_FILS_ERP_INFO = BIT(1), + WPA_DRV_UPDATE_AUTH_TYPE = BIT(2), +}; + +/** + * struct external_auth - External authentication trigger parameters + * + * These are used across the external authentication request and event + * interfaces. + * @action: Action type / trigger for external authentication. Only significant + * for the event interface. + * @bssid: BSSID of the peer with which the authentication has to happen. Used + * by both the request and event interface. + * @ssid: SSID of the AP. Used by both the request and event interface. + * @ssid_len: SSID length in octets. + * @key_mgmt_suite: AKM suite of the respective authentication. Optional for + * the request interface. + * @status: Status code, %WLAN_STATUS_SUCCESS for successful authentication, + * use %WLAN_STATUS_UNSPECIFIED_FAILURE if wpa_supplicant cannot give + * the real status code for failures. Used only for the request interface + * from user space to the driver. + */ +struct external_auth { + enum { + EXT_AUTH_START, + EXT_AUTH_ABORT, + } action; + u8 bssid[ETH_ALEN]; + u8 ssid[SSID_MAX_LEN]; + size_t ssid_len; + unsigned int key_mgmt_suite; + u16 status; +}; /** * struct wpa_driver_ops - Driver interface API definition @@ -1902,13 +2302,14 @@ struct wpa_driver_ops { /** * add_pmkid - Add PMKSA cache entry to the driver * @priv: private driver interface data - * @bssid: BSSID for the PMKSA cache entry - * @pmkid: PMKID for the PMKSA cache entry + * @params: PMKSA parameters * * Returns: 0 on success, -1 on failure * * This function is called when a new PMK is received, as a result of - * either normal authentication or RSN pre-authentication. + * either normal authentication or RSN pre-authentication. The PMKSA + * parameters are either a set of bssid, pmkid, and pmk; or a set of + * ssid, fils_cache_id, pmkid, and pmk. * * If the driver generates RSN IE, i.e., it does not use wpa_ie in * associate(), add_pmkid() can be used to add new PMKSA cache entries @@ -1916,18 +2317,18 @@ struct wpa_driver_ops { * driver_ops function does not need to be implemented. Likewise, if * the driver does not support WPA, this function is not needed. */ - int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid); + int (*add_pmkid)(void *priv, struct wpa_pmkid_params *params); /** * remove_pmkid - Remove PMKSA cache entry to the driver * @priv: private driver interface data - * @bssid: BSSID for the PMKSA cache entry - * @pmkid: PMKID for the PMKSA cache entry + * @params: PMKSA parameters * * Returns: 0 on success, -1 on failure * * This function is called when the supplicant drops a PMKSA cache - * entry for any reason. + * entry for any reason. The PMKSA parameters are either a set of + * bssid and pmkid; or a set of ssid, fils_cache_id, and pmkid. * * If the driver generates RSN IE, i.e., it does not use wpa_ie in * associate(), remove_pmkid() can be used to synchronize PMKSA caches @@ -1936,7 +2337,7 @@ struct wpa_driver_ops { * implemented. Likewise, if the driver does not support WPA, this * function is not needed. */ - int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid); + int (*remove_pmkid)(void *priv, struct wpa_pmkid_params *params); /** * flush_pmkid - Flush PMKSA cache @@ -2051,12 +2452,13 @@ struct wpa_driver_ops { * @priv: Private driver interface data * @num_modes: Variable for returning the number of returned modes * flags: Variable for returning hardware feature flags + * @dfs: Variable for returning DFS region (HOSTAPD_DFS_REGION_*) * Returns: Pointer to allocated hardware data on success or %NULL on * failure. Caller is responsible for freeing this. */ struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv, u16 *num_modes, - u16 *flags); + u16 *flags, u8 *dfs); /** * send_mlme - Send management frame from MLME @@ -2673,6 +3075,9 @@ struct wpa_driver_ops { * transmitted on that channel; alternatively the frame may be sent on * the current operational channel (if in associated state in station * mode or while operating as an AP.) + * + * If @src differs from the device MAC address, use of a random + * transmitter address is requested for this message exchange. */ int (*send_action)(void *priv, unsigned int freq, unsigned int wait, const u8 *dst, const u8 *src, const u8 *bssid, @@ -3058,19 +3463,13 @@ struct wpa_driver_ops { /** * sta_auth - Station authentication indication - * @priv: Private driver interface data - * @own_addr: Source address and BSSID for authentication frame - * @addr: MAC address of the station to associate - * @seq: authentication sequence number - * @status: authentication response status code - * @ie: authentication frame ie buffer - * @len: ie buffer length + * @priv: private driver interface data + * @params: Station authentication parameters * - * This function indicates the driver to send Authentication frame - * to the station. + * Returns: 0 on success, -1 on failure */ - int (*sta_auth)(void *priv, const u8 *own_addr, const u8 *addr, - u16 seq, u16 status, const u8 *ie, size_t len); + int (*sta_auth)(void *priv, + struct wpa_driver_sta_auth_params *params); /** * add_tspec - Add traffic stream @@ -3282,6 +3681,17 @@ struct wpa_driver_ops { int (*roaming)(void *priv, int allowed, const u8 *bssid); /** + * disable_fils - Enable/disable FILS feature + * @priv: Private driver interface data + * @disable: 0-enable and 1-disable FILS feature + * Returns: 0 on success, -1 on failure + * + * This callback can be used to configure driver and below layers to + * enable/disable all FILS features. + */ + int (*disable_fils)(void *priv, int disable); + + /** * set_mac_addr - Set MAC address * @priv: Private driver interface data * @addr: MAC address to use or %NULL for setting back to permanent @@ -3295,6 +3705,14 @@ struct wpa_driver_ops { int (*macsec_deinit)(void *priv); /** + * macsec_get_capability - Inform MKA of this driver's capability + * @priv: Private driver interface data + * @cap: Driver's capability + * Returns: 0 on success, -1 on failure + */ + int (*macsec_get_capability)(void *priv, enum macsec_cap *cap); + + /** * enable_protect_frames - Set protect frames status * @priv: Private driver interface data * @enabled: TRUE = protect frames enabled @@ -3304,6 +3722,15 @@ struct wpa_driver_ops { int (*enable_protect_frames)(void *priv, Boolean enabled); /** + * enable_encrypt - Set encryption status + * @priv: Private driver interface data + * @enabled: TRUE = encrypt outgoing traffic + * FALSE = integrity-only protection on outgoing traffic + * Returns: 0 on success, -1 on failure (or if not supported) + */ + int (*enable_encrypt)(void *priv, Boolean enabled); + + /** * set_replay_protect - Set replay protect status and window size * @priv: Private driver interface data * @enabled: TRUE = replay protect enabled @@ -3333,155 +3760,129 @@ struct wpa_driver_ops { /** * get_receive_lowest_pn - Get receive lowest pn * @priv: Private driver interface data - * @channel: secure channel - * @an: association number - * @lowest_pn: lowest accept pn + * @sa: secure association * Returns: 0 on success, -1 on failure (or if not supported) */ - int (*get_receive_lowest_pn)(void *priv, u32 channel, u8 an, - u32 *lowest_pn); + int (*get_receive_lowest_pn)(void *priv, struct receive_sa *sa); /** * get_transmit_next_pn - Get transmit next pn * @priv: Private driver interface data - * @channel: secure channel - * @an: association number - * @next_pn: next pn + * @sa: secure association * Returns: 0 on success, -1 on failure (or if not supported) */ - int (*get_transmit_next_pn)(void *priv, u32 channel, u8 an, - u32 *next_pn); + int (*get_transmit_next_pn)(void *priv, struct transmit_sa *sa); /** * set_transmit_next_pn - Set transmit next pn * @priv: Private driver interface data - * @channel: secure channel - * @an: association number - * @next_pn: next pn + * @sa: secure association * Returns: 0 on success, -1 on failure (or if not supported) */ - int (*set_transmit_next_pn)(void *priv, u32 channel, u8 an, - u32 next_pn); - - /** - * get_available_receive_sc - get available receive channel - * @priv: Private driver interface data - * @channel: secure channel - * Returns: 0 on success, -1 on failure (or if not supported) - */ - int (*get_available_receive_sc)(void *priv, u32 *channel); + int (*set_transmit_next_pn)(void *priv, struct transmit_sa *sa); /** * create_receive_sc - create secure channel for receiving * @priv: Private driver interface data - * @channel: secure channel - * @sci_addr: secure channel identifier - address - * @sci_port: secure channel identifier - port + * @sc: secure channel * @conf_offset: confidentiality offset (0, 30, or 50) * @validation: frame validation policy (0 = Disabled, 1 = Checked, * 2 = Strict) * Returns: 0 on success, -1 on failure (or if not supported) */ - int (*create_receive_sc)(void *priv, u32 channel, const u8 *sci_addr, - u16 sci_port, unsigned int conf_offset, + int (*create_receive_sc)(void *priv, struct receive_sc *sc, + unsigned int conf_offset, int validation); /** * delete_receive_sc - delete secure connection for receiving * @priv: private driver interface data from init() - * @channel: secure channel + * @sc: secure channel * Returns: 0 on success, -1 on failure */ - int (*delete_receive_sc)(void *priv, u32 channel); + int (*delete_receive_sc)(void *priv, struct receive_sc *sc); /** * create_receive_sa - create secure association for receive * @priv: private driver interface data from init() - * @channel: secure channel - * @an: association number - * @lowest_pn: the lowest packet number can be received - * @sak: the secure association key + * @sa: secure association * Returns: 0 on success, -1 on failure */ - int (*create_receive_sa)(void *priv, u32 channel, u8 an, - u32 lowest_pn, const u8 *sak); + int (*create_receive_sa)(void *priv, struct receive_sa *sa); /** - * enable_receive_sa - enable the SA for receive - * @priv: private driver interface data from init() - * @channel: secure channel - * @an: association number + * delete_receive_sa - Delete secure association for receive + * @priv: Private driver interface data from init() + * @sa: Secure association * Returns: 0 on success, -1 on failure */ - int (*enable_receive_sa)(void *priv, u32 channel, u8 an); + int (*delete_receive_sa)(void *priv, struct receive_sa *sa); /** - * disable_receive_sa - disable SA for receive + * enable_receive_sa - enable the SA for receive * @priv: private driver interface data from init() - * @channel: secure channel index - * @an: association number + * @sa: secure association * Returns: 0 on success, -1 on failure */ - int (*disable_receive_sa)(void *priv, u32 channel, u8 an); + int (*enable_receive_sa)(void *priv, struct receive_sa *sa); /** - * get_available_transmit_sc - get available transmit channel - * @priv: Private driver interface data - * @channel: secure channel - * Returns: 0 on success, -1 on failure (or if not supported) + * disable_receive_sa - disable SA for receive + * @priv: private driver interface data from init() + * @sa: secure association + * Returns: 0 on success, -1 on failure */ - int (*get_available_transmit_sc)(void *priv, u32 *channel); + int (*disable_receive_sa)(void *priv, struct receive_sa *sa); /** * create_transmit_sc - create secure connection for transmit * @priv: private driver interface data from init() - * @channel: secure channel - * @sci_addr: secure channel identifier - address - * @sci_port: secure channel identifier - port + * @sc: secure channel + * @conf_offset: confidentiality offset (0, 30, or 50) * Returns: 0 on success, -1 on failure */ - int (*create_transmit_sc)(void *priv, u32 channel, const u8 *sci_addr, - u16 sci_port, unsigned int conf_offset); + int (*create_transmit_sc)(void *priv, struct transmit_sc *sc, + unsigned int conf_offset); /** * delete_transmit_sc - delete secure connection for transmit * @priv: private driver interface data from init() - * @channel: secure channel + * @sc: secure channel * Returns: 0 on success, -1 on failure */ - int (*delete_transmit_sc)(void *priv, u32 channel); + int (*delete_transmit_sc)(void *priv, struct transmit_sc *sc); /** * create_transmit_sa - create secure association for transmit * @priv: private driver interface data from init() - * @channel: secure channel index - * @an: association number - * @next_pn: the packet number used as next transmit packet - * @confidentiality: True if the SA is to provide confidentiality - * as well as integrity - * @sak: the secure association key + * @sa: secure association + * Returns: 0 on success, -1 on failure + */ + int (*create_transmit_sa)(void *priv, struct transmit_sa *sa); + + /** + * delete_transmit_sa - Delete secure association for transmit + * @priv: Private driver interface data from init() + * @sa: Secure association * Returns: 0 on success, -1 on failure */ - int (*create_transmit_sa)(void *priv, u32 channel, u8 an, u32 next_pn, - Boolean confidentiality, const u8 *sak); + int (*delete_transmit_sa)(void *priv, struct transmit_sa *sa); /** * enable_transmit_sa - enable SA for transmit * @priv: private driver interface data from init() - * @channel: secure channel - * @an: association number + * @sa: secure association * Returns: 0 on success, -1 on failure */ - int (*enable_transmit_sa)(void *priv, u32 channel, u8 an); + int (*enable_transmit_sa)(void *priv, struct transmit_sa *sa); /** * disable_transmit_sa - disable SA for transmit * @priv: private driver interface data from init() - * @channel: secure channel - * @an: association number + * @sa: secure association * Returns: 0 on success, -1 on failure */ - int (*disable_transmit_sa)(void *priv, u32 channel, u8 an); + int (*disable_transmit_sa)(void *priv, struct transmit_sa *sa); #endif /* CONFIG_MACSEC */ /** @@ -3555,9 +3956,12 @@ struct wpa_driver_ops { /** * abort_scan - Request the driver to abort an ongoing scan * @priv: Private driver interface data + * @scan_cookie: Cookie identifying the scan request. This is used only + * when the vendor interface QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN + * was used to trigger scan. Otherwise, 0 is used. * Returns 0 on success, -1 on failure */ - int (*abort_scan)(void *priv); + int (*abort_scan)(void *priv, u64 scan_cookie); /** * configure_data_frame_filters - Request to configure frame filters @@ -3623,8 +4027,72 @@ struct wpa_driver_ops { */ int (*set_default_scan_ies)(void *priv, const u8 *ies, size_t ies_len); -}; + /** + * set_tdls_mode - Set TDLS trigger mode to the host driver + * @priv: Private driver interface data + * @tdls_external_control: Represents if TDLS external trigger control + * mode is enabled/disabled. + * + * This optional callback can be used to configure the TDLS external + * trigger control mode to the host driver. + */ + int (*set_tdls_mode)(void *priv, int tdls_external_control); + /** + * get_bss_transition_status - Get candidate BSS's transition status + * @priv: Private driver interface data + * @params: Candidate BSS list + * + * Get the accept or reject reason code for a list of BSS transition + * candidates. + */ + struct wpa_bss_candidate_info * + (*get_bss_transition_status)(void *priv, + struct wpa_bss_trans_info *params); + /** + * ignore_assoc_disallow - Configure driver to ignore assoc_disallow + * @priv: Private driver interface data + * @ignore_disallow: 0 to not ignore, 1 to ignore + * Returns: 0 on success, -1 on failure + */ + int (*ignore_assoc_disallow)(void *priv, int ignore_disallow); + + /** + * set_bssid_blacklist - Set blacklist of BSSIDs to the driver + * @priv: Private driver interface data + * @num_bssid: Number of blacklist BSSIDs + * @bssids: List of blacklisted BSSIDs + */ + int (*set_bssid_blacklist)(void *priv, unsigned int num_bssid, + const u8 *bssid); + + /** + * update_connect_params - Update the connection parameters + * @priv: Private driver interface data + * @params: Association parameters + * @mask: Bit mask indicating which parameters in @params have to be + * updated + * Returns: 0 on success, -1 on failure + * + * Update the connection parameters when in connected state so that the + * driver uses the updated parameters for subsequent roaming. This is + * used only with drivers that implement internal BSS selection and + * roaming. + */ + int (*update_connect_params)( + void *priv, struct wpa_driver_associate_params *params, + enum wpa_drv_update_connect_params_mask mask); + + /** + * send_external_auth_status - Indicate the status of external + * authentication processing to the host driver. + * @priv: Private driver interface data + * @params: Status of authentication processing. + * Returns: 0 on success, -1 on failure + */ + int (*send_external_auth_status)(void *priv, + struct external_auth *params); +}; /** * enum wpa_event_type - Event type for wpa_supplicant_event() calls @@ -3734,17 +4202,6 @@ enum wpa_event_type { EVENT_PMKID_CANDIDATE, /** - * EVENT_STKSTART - Request STK handshake (MLME-STKSTART.request) - * - * This event can be used to inform wpa_supplicant about desire to set - * up secure direct link connection between two stations as defined in - * IEEE 802.11e with a new PeerKey mechanism that replaced the original - * STAKey negotiation. The caller will need to set peer address for the - * event. - */ - EVENT_STKSTART, - - /** * EVENT_TDLS - Request TDLS operation * * This event can be used to request a TDLS operation to be performed. @@ -4043,7 +4500,7 @@ enum wpa_event_type { * EVENT_DFS_CAC_ABORTED - Notify that channel availability check has been aborted * * The CAC was not successful, and the channel remains in the previous - * state. This may happen due to a radar beeing detected or other + * state. This may happen due to a radar being detected or other * external influences. */ EVENT_DFS_CAC_ABORTED, @@ -4112,6 +4569,65 @@ enum wpa_event_type { * EVENT_P2P_LO_STOP - Notify that P2P listen offload is stopped */ EVENT_P2P_LO_STOP, + + /** + * EVENT_BEACON_LOSS - Beacon loss detected + * + * This event indicates that no Beacon frames has been received from + * the current AP. This may indicate that the AP is not anymore in + * range. + */ + EVENT_BEACON_LOSS, + + /** + * EVENT_DFS_PRE_CAC_EXPIRED - Notify that channel availability check + * done previously (Pre-CAC) on the channel has expired. This would + * normally be on a non-ETSI DFS regulatory domain. DFS state of the + * channel will be moved from available to usable. A new CAC has to be + * performed before start operating on this channel. + */ + EVENT_DFS_PRE_CAC_EXPIRED, + + /** + * EVENT_EXTERNAL_AUTH - This event interface is used by host drivers + * that do not define separate commands for authentication and + * association (~WPA_DRIVER_FLAGS_SME) but offload the 802.11 + * authentication to wpa_supplicant. This event carries all the + * necessary information from the host driver for the authentication to + * happen. + */ + EVENT_EXTERNAL_AUTH, + + /** + * EVENT_PORT_AUTHORIZED - Notification that a connection is authorized + * + * This event should be indicated when the driver completes the 4-way + * handshake. This event should be preceded by an EVENT_ASSOC that + * indicates the completion of IEEE 802.11 association. + */ + EVENT_PORT_AUTHORIZED, + + /** + * EVENT_STATION_OPMODE_CHANGED - Notify STA's HT/VHT operation mode + * change event. + */ + EVENT_STATION_OPMODE_CHANGED, + + /** + * EVENT_INTERFACE_MAC_CHANGED - Notify that interface MAC changed + * + * This event is emitted when the MAC changes while the interface is + * enabled. When an interface was disabled and becomes enabled, it + * must be always assumed that the MAC possibly changed. + */ + EVENT_INTERFACE_MAC_CHANGED, + + /** + * EVENT_WDS_STA_INTERFACE_STATUS - Notify WDS STA interface status + * + * This event is emitted when an interface is added/removed for WDS STA. + */ + EVENT_WDS_STA_INTERFACE_STATUS, }; @@ -4204,6 +4720,16 @@ union wpa_event_data { size_t resp_ies_len; /** + * resp_frame - (Re)Association Response frame + */ + const u8 *resp_frame; + + /** + * resp_frame_len - (Re)Association Response frame length + */ + size_t resp_frame_len; + + /** * beacon_ies - Beacon or Probe Response IEs * * Optional Beacon/ProbeResp data: IEs included in Beacon or @@ -4280,6 +4806,8 @@ union wpa_event_data { /** * ptk_kek - The derived PTK KEK + * This is used in key management offload and also in FILS SK + * offload. */ const u8 *ptk_kek; @@ -4293,6 +4821,36 @@ union wpa_event_data { * 0 = unknown, 1 = unchanged, 2 = changed */ u8 subnet_status; + + /** + * The following information is used in FILS SK offload + * @fils_erp_next_seq_num + * @fils_pmk + * @fils_pmk_len + * @fils_pmkid + */ + + /** + * fils_erp_next_seq_num - The next sequence number to use in + * FILS ERP messages + */ + u16 fils_erp_next_seq_num; + + /** + * fils_pmk - A new PMK if generated in case of FILS + * authentication + */ + const u8 *fils_pmk; + + /** + * fils_pmk_len - Length of fils_pmk + */ + size_t fils_pmk_len; + + /** + * fils_pmkid - PMKID used or generated in FILS authentication + */ + const u8 *fils_pmkid; } assoc_info; /** @@ -4389,13 +4947,6 @@ union wpa_event_data { } pmkid_candidate; /** - * struct stkstart - Data for EVENT_STKSTART - */ - struct stkstart { - u8 peer[ETH_ALEN]; - } stkstart; - - /** * struct tdls - Data for EVENT_TDLS */ struct tdls { @@ -4503,6 +5054,17 @@ union wpa_event_data { * than explicit rejection response from the AP. */ int timed_out; + + /** + * timeout_reason - Reason for the timeout + */ + const char *timeout_reason; + + /** + * fils_erp_next_seq_num - The next sequence number to use in + * FILS ERP messages + */ + u16 fils_erp_next_seq_num; } assoc_reject; struct timeout_event { @@ -4586,6 +5148,11 @@ union wpa_event_data { * @external_scan: Whether the scan info is for an external scan * @nl_scan_event: 1 if the source of this scan event is a normal scan, * 0 if the source of the scan event is a vendor scan + * @scan_start_tsf: Time when the scan started in terms of TSF of the + * BSS that the interface that requested the scan is connected to + * (if available). + * @scan_start_tsf_bssid: The BSSID according to which %scan_start_tsf + * is set. */ struct scan_info { int aborted; @@ -4595,6 +5162,8 @@ union wpa_event_data { size_t num_ssids; int external_scan; int nl_scan_event; + u64 scan_start_tsf; + u8 scan_start_tsf_bssid[ETH_ALEN]; } scan_info; /** @@ -4684,9 +5253,12 @@ union wpa_event_data { /** * struct low_ack - Data for EVENT_STATION_LOW_ACK events * @addr: station address + * @num_packets: Number of packets lost (consecutive packets not + * acknowledged) */ struct low_ack { u8 addr[ETH_ALEN]; + u32 num_packets; } low_ack; /** @@ -4858,6 +5430,37 @@ union wpa_event_data { P2P_LO_STOPPED_REASON_NOT_SUPPORTED, } reason_code; } p2p_lo_stop; + + /* For EVENT_EXTERNAL_AUTH */ + struct external_auth external_auth; + + /** + * struct sta_opmode - Station's operation mode change event + * @addr: The station MAC address + * @smps_mode: SMPS mode of the station + * @chan_width: Channel width of the station + * @rx_nss: RX_NSS of the station + * + * This is used as data with EVENT_STATION_OPMODE_CHANGED. + */ + struct sta_opmode { + const u8 *addr; + enum smps_mode smps_mode; + enum chan_width chan_width; + u8 rx_nss; + } sta_opmode; + + /** + * struct wds_sta_interface - Data for EVENT_WDS_STA_INTERFACE_STATUS. + */ + struct wds_sta_interface { + const u8 *sta_addr; + const char *ifname; + enum { + INTERFACE_ADDED, + INTERFACE_REMOVED + } istatus; + } wds_sta_interface; }; /** @@ -4973,6 +5576,10 @@ extern const struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */ /* driver_macsec_qca.c */ extern const struct wpa_driver_ops wpa_driver_macsec_qca_ops; #endif /* CONFIG_DRIVER_MACSEC_QCA */ +#ifdef CONFIG_DRIVER_MACSEC_LINUX +/* driver_macsec_linux.c */ +extern const struct wpa_driver_ops wpa_driver_macsec_linux_ops; +#endif /* CONFIG_DRIVER_MACSEC_LINUX */ #ifdef CONFIG_DRIVER_ROBOSWITCH /* driver_roboswitch.c */ extern const struct wpa_driver_ops wpa_driver_roboswitch_ops; diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c index a88345fc92f8f..62f5baad63612 100644 --- a/src/drivers/driver_atheros.c +++ b/src/drivers/driver_atheros.c @@ -36,6 +36,10 @@ #include "ieee80211_external.h" +/* Avoid conflicting definition from the driver header files with + * common/wpa_common.h */ +#undef WPA_OUI_TYPE + #ifdef CONFIG_WPS #include <netpacket/packet.h> @@ -55,7 +59,7 @@ #include "netlink.h" #include "linux_ioctl.h" -#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) || defined(CONFIG_WNM) || defined(CONFIG_WPS) +#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_HS20) || defined(CONFIG_WNM) || defined(CONFIG_WPS) || defined(CONFIG_FILS) #define ATHEROS_USE_RAW_RECEIVE #endif @@ -70,6 +74,7 @@ struct atheros_driver_data { int ioctl_sock; /* socket for ioctl() use */ struct netlink_data *netlink; int we_version; + int fils_en; /* FILS enable/disable in driver */ u8 acct_mac[ETH_ALEN]; struct hostap_sta_driver_data acct_data; @@ -178,6 +183,25 @@ static const char * athr_get_param_name(int op) } +#ifdef CONFIG_FILS +static int +get80211param(struct atheros_driver_data *drv, int op, int *data) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.mode = op; + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_GETPARAM, &iwr) < 0) + return -1; + + *data = iwr.u.mode; + return 0; +} +#endif /* CONFIG_FILS */ + + static int set80211priv(struct atheros_driver_data *drv, int op, void *data, int len) { @@ -692,10 +716,14 @@ atheros_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) wpa_hexdump(MSG_DEBUG, "atheros: set_generic_elem", ie, ie_len); wpabuf_free(drv->wpa_ie); - drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len); + if (ie) + drv->wpa_ie = wpabuf_alloc_copy(ie, ie_len); + else + drv->wpa_ie = NULL; app_ie = (struct ieee80211req_getset_appiebuf *) buf; - os_memcpy(&(app_ie->app_buf[0]), ie, ie_len); + if (ie) + os_memcpy(&(app_ie->app_buf[0]), ie, ie_len); app_ie->app_buflen = ie_len; app_ie->app_frmtype = IEEE80211_APPIE_FRAME_BEACON; @@ -903,6 +931,12 @@ static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) break; os_memset(&event, 0, sizeof(event)); + if (le_to_host16(mgmt->u.auth.auth_alg) == WLAN_AUTH_SAE) { + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = len; + wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event); + break; + } os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN); os_memcpy(event.auth.bssid, mgmt->bssid, ETH_ALEN); event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg); @@ -931,11 +965,11 @@ static int atheros_receive_pkt(struct atheros_driver_data *drv) #ifdef CONFIG_WPS filt.app_filterype |= IEEE80211_FILTER_TYPE_PROBE_REQ; #endif /* CONFIG_WPS */ -#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) +#if defined(CONFIG_IEEE80211W) || defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS) filt.app_filterype |= (IEEE80211_FILTER_TYPE_ASSOC_REQ | IEEE80211_FILTER_TYPE_AUTH | IEEE80211_FILTER_TYPE_ACTION); -#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */ #ifdef CONFIG_WNM filt.app_filterype |= IEEE80211_FILTER_TYPE_ACTION; #endif /* CONFIG_WNM */ @@ -949,12 +983,12 @@ static int atheros_receive_pkt(struct atheros_driver_data *drv) return ret; } -#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) +#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS) drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW, atheros_raw_receive, drv, 1); if (drv->sock_raw == NULL) return -1; -#endif /* CONFIG_WPS || CONFIG_IEEE80211R */ +#endif /* CONFIG_WPS || CONFIG_IEEE80211R || CONFIG_FILS */ return ret; } @@ -981,7 +1015,8 @@ atheros_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) beac_ie = (struct ieee80211req_getset_appiebuf *) buf; beac_ie->app_frmtype = frametype; beac_ie->app_buflen = len; - os_memcpy(&(beac_ie->app_buf[0]), ie, len); + if (ie) + os_memcpy(&(beac_ie->app_buf[0]), ie, len); /* append the WPA/RSN IE if it is set already */ if (((frametype == IEEE80211_APPIE_FRAME_BEACON) || @@ -1034,32 +1069,56 @@ atheros_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, #define atheros_set_ap_wps_ie NULL #endif /* CONFIG_WPS */ -#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) static int -atheros_sta_auth(void *priv, const u8 *own_addr, const u8 *addr, u16 seq, - u16 status_code, const u8 *ie, size_t len) +atheros_sta_auth(void *priv, struct wpa_driver_sta_auth_params *params) { struct atheros_driver_data *drv = priv; struct ieee80211req_mlme mlme; int ret; wpa_printf(MSG_DEBUG, "%s: addr=%s status_code=%d", - __func__, ether_sprintf(addr), status_code); + __func__, ether_sprintf(params->addr), params->status); +#ifdef CONFIG_FILS + /* Copy FILS AAD parameters if the driver supports FILS */ + if (params->fils_auth && drv->fils_en) { + wpa_printf(MSG_DEBUG, "%s: im_op IEEE80211_MLME_AUTH_FILS", + __func__); + os_memcpy(mlme.fils_aad.ANonce, params->fils_anonce, + IEEE80211_FILS_NONCE_LEN); + os_memcpy(mlme.fils_aad.SNonce, params->fils_snonce, + IEEE80211_FILS_NONCE_LEN); + os_memcpy(mlme.fils_aad.kek, params->fils_kek, + IEEE80211_MAX_WPA_KEK_LEN); + mlme.fils_aad.kek_len = params->fils_kek_len; + mlme.im_op = IEEE80211_MLME_AUTH_FILS; + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", + mlme.fils_aad.ANonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: SNonce", + mlme.fils_aad.SNonce, FILS_NONCE_LEN); + wpa_hexdump_key(MSG_DEBUG, "FILS: KEK", + mlme.fils_aad.kek, mlme.fils_aad.kek_len); + } else { + mlme.im_op = IEEE80211_MLME_AUTH; + } +#else /* CONFIG_FILS */ mlme.im_op = IEEE80211_MLME_AUTH; - mlme.im_reason = status_code; - mlme.im_seq = seq; - os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); - mlme.im_optie_len = len; - if (len) { - if (len < IEEE80211_MAX_OPT_IE) { - os_memcpy(mlme.im_optie, ie, len); +#endif /* CONFIG_FILS */ + + mlme.im_reason = params->status; + mlme.im_seq = params->seq; + os_memcpy(mlme.im_macaddr, params->addr, IEEE80211_ADDR_LEN); + mlme.im_optie_len = params->len; + if (params->len) { + if (params->len < IEEE80211_MAX_OPT_IE) { + os_memcpy(mlme.im_optie, params->ie, params->len); } else { wpa_printf(MSG_DEBUG, "%s: Not enough space to copy " "opt_ie STA (addr " MACSTR " reason %d, " "ie_len %d)", - __func__, MAC2STR(addr), status_code, - (int) len); + __func__, MAC2STR(params->addr), + params->status, (int) params->len); return -1; } } @@ -1067,7 +1126,7 @@ atheros_sta_auth(void *priv, const u8 *own_addr, const u8 *addr, u16 seq, if (ret < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to auth STA (addr " MACSTR " reason %d)", - __func__, MAC2STR(addr), status_code); + __func__, MAC2STR(params->addr), params->status); } return ret; } @@ -1110,7 +1169,7 @@ atheros_sta_assoc(void *priv, const u8 *own_addr, const u8 *addr, } return ret; } -#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */ static void atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) @@ -1257,7 +1316,7 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, atheros_raw_receive(drv, NULL, (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); #endif /* CONFIG_WPS */ -#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) } else if (os_strncmp(custom, "Manage.assoc_req ", 17) == 0) { /* Format: "Manage.assoc_req <frame len>" | zero padding | * frame */ @@ -1270,20 +1329,20 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, } atheros_raw_receive(drv, NULL, (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); - } else if (os_strncmp(custom, "Manage.auth ", 12) == 0) { + } else if (os_strncmp(custom, "Manage.auth ", 12) == 0) { /* Format: "Manage.auth <frame len>" | zero padding | frame */ int len = atoi(custom + 12); - if (len < 0 || - MGMT_FRAM_TAG_SIZE + len > end - custom) { + if (len < 0 || + MGMT_FRAM_TAG_SIZE + len > end - custom) { wpa_printf(MSG_DEBUG, "Invalid Manage.auth event length %d", len); return; } atheros_raw_receive(drv, NULL, (u8 *) custom + MGMT_FRAM_TAG_SIZE, len); -#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R */ +#endif /* CONFIG_IEEE80211W || CONFIG_IEEE80211R || CONFIG_FILS */ #ifdef ATHEROS_USE_RAW_RECEIVE - } else if (os_strncmp(custom, "Manage.action ", 14) == 0) { + } else if (os_strncmp(custom, "Manage.action ", 14) == 0) { /* Format: "Manage.assoc_req <frame len>" | zero padding | frame */ int len = atoi(custom + 14); @@ -1299,6 +1358,40 @@ atheros_wireless_event_wireless_custom(struct atheros_driver_data *drv, } } + +static void send_action_cb_event(struct atheros_driver_data *drv, + char *data, size_t data_len) +{ + union wpa_event_data event; + struct ieee80211_send_action_cb *sa; + const struct ieee80211_hdr *hdr; + u16 fc; + + if (data_len < sizeof(*sa) + 24) { + wpa_printf(MSG_DEBUG, + "athr: Too short event message (data_len=%d sizeof(*sa)=%d)", + (int) data_len, (int) sizeof(*sa)); + wpa_hexdump(MSG_DEBUG, "athr: Short event message", + data, data_len); + return; + } + + sa = (struct ieee80211_send_action_cb *) data; + + hdr = (const struct ieee80211_hdr *) (sa + 1); + fc = le_to_host16(hdr->frame_control); + + os_memset(&event, 0, sizeof(event)); + event.tx_status.type = WLAN_FC_GET_TYPE(fc); + event.tx_status.stype = WLAN_FC_GET_STYPE(fc); + event.tx_status.dst = sa->dst_addr; + event.tx_status.data = (const u8 *) hdr; + event.tx_status.data_len = data_len - sizeof(*sa); + event.tx_status.ack = sa->ack; + wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event); +} + + /* * Handle size of data problem. WEXT only allows data of 256 bytes for custom * events, and p2p data can be much bigger. So the athr driver sends a small @@ -1363,6 +1456,11 @@ static void fetch_pending_big_events(struct atheros_driver_data *drv) &event); continue; } + } else if (frame_type == IEEE80211_EV_P2P_SEND_ACTION_CB) { + wpa_printf(MSG_DEBUG, + "%s: ACTION_CB frame_type=%u len=%zu", + __func__, frame_type, data_len); + send_action_cb_event(drv, (void *) mgmt, data_len); } else { wpa_printf(MSG_DEBUG, "athr: %s unknown type %d", __func__, frame_type); @@ -1376,6 +1474,10 @@ atheros_wireless_event_atheros_custom(struct atheros_driver_data *drv, int opcode, char *buf, int len) { switch (opcode) { + case IEEE80211_EV_P2P_SEND_ACTION_CB: + wpa_printf(MSG_DEBUG, "WEXT: EV_P2P_SEND_ACTION_CB"); + fetch_pending_big_events(drv); + break; case IEEE80211_EV_RX_MGMT: wpa_printf(MSG_DEBUG, "WEXT: EV_RX_MGMT"); fetch_pending_big_events(drv); @@ -1603,6 +1705,27 @@ handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) len - sizeof(struct l2_ethhdr)); } + +static void atheros_read_fils_cap(struct atheros_driver_data *drv) +{ + int fils = 0; + +#ifdef CONFIG_FILS + /* TODO: Would be better to have #ifdef on the IEEE80211_PARAM_* value + * to automatically check this against the driver header files. */ + if (get80211param(drv, IEEE80211_PARAM_ENABLE_FILS, &fils) < 0) { + wpa_printf(MSG_DEBUG, + "%s: Failed to get FILS capability from driver", + __func__); + /* Assume driver does not support FILS */ + fils = 0; + } +#endif /* CONFIG_FILS */ + drv->fils_en = fils; + wpa_printf(MSG_DEBUG, "atheros: fils_en=%d", drv->fils_en); +} + + static void * atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params) { @@ -1683,6 +1806,9 @@ atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params) if (atheros_wireless_event_init(drv)) goto bad; + /* Read FILS capability from the driver */ + atheros_read_fils_cap(drv); + return drv; bad: atheros_reset_appfilter(drv); @@ -1735,7 +1861,7 @@ atheros_set_ssid(void *priv, const u8 *buf, int len) os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); iwr.u.essid.flags = 1; /* SSID active */ iwr.u.essid.pointer = (caddr_t) buf; - iwr.u.essid.length = len + 1; + iwr.u.essid.length = len; if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s", @@ -1848,7 +1974,7 @@ static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params) } -#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len, int noack, unsigned int freq, @@ -1874,7 +2000,7 @@ static int atheros_send_mgmt(void *priv, const u8 *frm, size_t data_len, return set80211priv(drv, IEEE80211_IOCTL_SEND_MGMT, mgmt_frm, sizeof(struct ieee80211req_mgmtbuf) + data_len); } -#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */ #ifdef CONFIG_IEEE80211R @@ -2158,11 +2284,11 @@ const struct wpa_driver_ops wpa_driver_atheros_ops = { .set_ap_wps_ie = atheros_set_ap_wps_ie, .set_authmode = atheros_set_authmode, .set_ap = atheros_set_ap, -#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) || defined(CONFIG_FILS) .sta_assoc = atheros_sta_assoc, .sta_auth = atheros_sta_auth, .send_mlme = atheros_send_mgmt, -#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W || CONFIG_FILS */ #ifdef CONFIG_IEEE80211R .add_tspec = atheros_add_tspec, .add_sta_node = atheros_add_sta_node, diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c index 2afd7df9637db..8621aa0b0099f 100644 --- a/src/drivers/driver_bsd.c +++ b/src/drivers/driver_bsd.c @@ -726,7 +726,7 @@ bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, } -static int +static int bsd_flush(void *priv) { u8 allsta[IEEE80211_ADDR_LEN]; diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c index c7107ba899b0b..ac0916e4061f9 100644 --- a/src/drivers/driver_common.c +++ b/src/drivers/driver_common.c @@ -1,6 +1,6 @@ /* * Common driver-related functions - * Copyright (c) 2003-2011, 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. @@ -35,7 +35,6 @@ const char * event_to_string(enum wpa_event_type event) E2S(ASSOCINFO); E2S(INTERFACE_STATUS); E2S(PMKID_CANDIDATE); - E2S(STKSTART); E2S(TDLS); E2S(FT_RESPONSE); E2S(IBSS_RSN_START); @@ -81,6 +80,13 @@ const char * event_to_string(enum wpa_event_type event) E2S(ACS_CHANNEL_SELECTED); E2S(DFS_CAC_STARTED); E2S(P2P_LO_STOP); + E2S(BEACON_LOSS); + E2S(DFS_PRE_CAC_EXPIRED); + E2S(EXTERNAL_AUTH); + E2S(PORT_AUTHORIZED); + E2S(STATION_OPMODE_CHANGED); + E2S(INTERFACE_MAC_CHANGED); + E2S(WDS_STA_INTERFACE_STATUS); } return "UNKNOWN"; @@ -267,6 +273,19 @@ const char * driver_flag_to_string(u64 flag) DF2S(OFFCHANNEL_SIMULTANEOUS); DF2S(FULL_AP_CLIENT_STATE); DF2S(P2P_LISTEN_OFFLOAD); + DF2S(SUPPORT_FILS); + DF2S(BEACON_RATE_LEGACY); + DF2S(BEACON_RATE_HT); + DF2S(BEACON_RATE_VHT); + DF2S(MGMT_TX_RANDOM_TA); + DF2S(MGMT_TX_RANDOM_TA_CONNECTED); + DF2S(SCHED_SCAN_RELATIVE_RSSI); + DF2S(HE_CAPABILITIES); + DF2S(FILS_SK_OFFLOAD); + DF2S(OCE_STA); + DF2S(OCE_AP); + DF2S(OCE_STA_CFON); + DF2S(MFP_OPTIONAL); } return "UNKNOWN"; #undef DF2S diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c index 517a3bbb5d308..597da335e4748 100644 --- a/src/drivers/driver_hostap.c +++ b/src/drivers/driver_hostap.c @@ -741,10 +741,9 @@ static int hostap_set_generic_elem(void *priv, drv->generic_ie = NULL; drv->generic_ie_len = 0; if (elem) { - drv->generic_ie = os_malloc(elem_len); + drv->generic_ie = os_memdup(elem, elem_len); if (drv->generic_ie == NULL) return -1; - os_memcpy(drv->generic_ie, elem, elem_len); drv->generic_ie_len = elem_len; } @@ -768,11 +767,10 @@ static int hostap_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, drv->wps_ie = NULL; drv->wps_ie_len = 0; if (proberesp) { - drv->wps_ie = os_malloc(wpabuf_len(proberesp)); + drv->wps_ie = os_memdup(wpabuf_head(proberesp), + wpabuf_len(proberesp)); if (drv->wps_ie == NULL) return -1; - os_memcpy(drv->wps_ie, wpabuf_head(proberesp), - wpabuf_len(proberesp)); drv->wps_ie_len = wpabuf_len(proberesp); } @@ -1090,7 +1088,7 @@ static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv, u16 *num_modes, - u16 *flags) + u16 *flags, u8 *dfs) { struct hostapd_hw_modes *mode; int i, clen, rlen; @@ -1105,6 +1103,7 @@ static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv, *num_modes = 1; *flags = 0; + *dfs = 0; mode->mode = HOSTAPD_MODE_IEEE80211B; mode->num_channels = 14; diff --git a/src/drivers/driver_macsec_linux.c b/src/drivers/driver_macsec_linux.c new file mode 100644 index 0000000000000..4f6629f5aaa2b --- /dev/null +++ b/src/drivers/driver_macsec_linux.c @@ -0,0 +1,1310 @@ +/* + * Driver interaction with Linux MACsec kernel module + * Copyright (c) 2016, Sabrina Dubroca <sd@queasysnail.net> and Red Hat, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include <net/if.h> +#include <netpacket/packet.h> +#include <net/if_arp.h> +#include <net/if.h> +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#include <netlink/route/link.h> +#include <netlink/route/link/macsec.h> +#include <linux/if_macsec.h> +#include <inttypes.h> + +#include "utils/common.h" +#include "utils/eloop.h" +#include "pae/ieee802_1x_kay.h" +#include "driver.h" +#include "driver_wired_common.h" + +#define DRV_PREFIX "macsec_linux: " + +#define UNUSED_SCI 0xffffffffffffffff + +struct cb_arg { + struct macsec_drv_data *drv; + u32 *pn; + int ifindex; + u8 txsa; + u8 rxsa; + u64 rxsci; +}; + +struct macsec_genl_ctx { + struct nl_sock *sk; + int macsec_genl_id; + struct cb_arg cb_arg; +}; + +struct macsec_drv_data { + struct driver_wired_common_data common; + struct rtnl_link *link; + struct nl_cache *link_cache; + struct nl_sock *sk; + struct macsec_genl_ctx ctx; + + struct netlink_data *netlink; + struct nl_handle *nl; + char ifname[IFNAMSIZ + 1]; + int ifi; + int parent_ifi; + + Boolean created_link; + + Boolean controlled_port_enabled; + Boolean controlled_port_enabled_set; + + Boolean protect_frames; + Boolean protect_frames_set; + + Boolean encrypt; + Boolean encrypt_set; + + Boolean replay_protect; + Boolean replay_protect_set; + + u32 replay_window; + + u8 encoding_sa; + Boolean encoding_sa_set; +}; + + +static int dump_callback(struct nl_msg *msg, void *argp); + + +static struct nl_msg * msg_prepare(enum macsec_nl_commands cmd, + const struct macsec_genl_ctx *ctx, + unsigned int ifindex) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) { + wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc message"); + return NULL; + } + + if (!genlmsg_put(msg, 0, 0, ctx->macsec_genl_id, 0, 0, cmd, 0)) { + wpa_printf(MSG_ERROR, DRV_PREFIX "failed to put header"); + goto nla_put_failure; + } + + NLA_PUT_U32(msg, MACSEC_ATTR_IFINDEX, ifindex); + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +} + + +static int nla_put_rxsc_config(struct nl_msg *msg, u64 sci) +{ + struct nlattr *nest = nla_nest_start(msg, MACSEC_ATTR_RXSC_CONFIG); + + if (!nest) + return -1; + + NLA_PUT_U64(msg, MACSEC_RXSC_ATTR_SCI, sci); + + nla_nest_end(msg, nest); + + return 0; + +nla_put_failure: + return -1; +} + + +static int init_genl_ctx(struct macsec_drv_data *drv) +{ + struct macsec_genl_ctx *ctx = &drv->ctx; + + ctx->sk = nl_socket_alloc(); + if (!ctx->sk) { + wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc genl socket"); + return -1; + } + + if (genl_connect(ctx->sk) < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "connection to genl socket failed"); + goto out_free; + } + + ctx->macsec_genl_id = genl_ctrl_resolve(ctx->sk, "macsec"); + if (ctx->macsec_genl_id < 0) { + wpa_printf(MSG_ERROR, DRV_PREFIX "genl resolve failed"); + goto out_free; + } + + memset(&ctx->cb_arg, 0, sizeof(ctx->cb_arg)); + ctx->cb_arg.drv = drv; + + nl_socket_modify_cb(ctx->sk, NL_CB_VALID, NL_CB_CUSTOM, dump_callback, + &ctx->cb_arg); + + return 0; + +out_free: + nl_socket_free(ctx->sk); + ctx->sk = NULL; + return -1; +} + + +static int try_commit(struct macsec_drv_data *drv) +{ + int err; + + if (!drv->sk) + return 0; + + if (!drv->link) + return 0; + + if (drv->controlled_port_enabled_set) { + struct rtnl_link *change = rtnl_link_alloc(); + + if (!change) + return -1; + + rtnl_link_set_name(change, drv->ifname); + + if (drv->controlled_port_enabled) + rtnl_link_set_flags(change, IFF_UP); + else + rtnl_link_unset_flags(change, IFF_UP); + + err = rtnl_link_change(drv->sk, change, change, 0); + if (err < 0) + return err; + + rtnl_link_put(change); + + drv->controlled_port_enabled_set = FALSE; + } + + if (drv->protect_frames_set) + rtnl_link_macsec_set_protect(drv->link, drv->protect_frames); + + if (drv->encrypt_set) + rtnl_link_macsec_set_encrypt(drv->link, drv->encrypt); + + if (drv->replay_protect_set) { + rtnl_link_macsec_set_replay_protect(drv->link, + drv->replay_protect); + if (drv->replay_protect) + rtnl_link_macsec_set_window(drv->link, + drv->replay_window); + } + + if (drv->encoding_sa_set) + rtnl_link_macsec_set_encoding_sa(drv->link, drv->encoding_sa); + + err = rtnl_link_add(drv->sk, drv->link, 0); + if (err < 0) + return err; + + drv->protect_frames_set = FALSE; + drv->encrypt_set = FALSE; + drv->replay_protect_set = FALSE; + + return 0; +} + + +static void macsec_drv_wpa_deinit(void *priv) +{ + struct macsec_drv_data *drv = priv; + + driver_wired_deinit_common(&drv->common); + os_free(drv); +} + + +static int macsec_check_macsec(void) +{ + struct nl_sock *sk; + int err = -1; + + sk = nl_socket_alloc(); + if (!sk) { + wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc genl socket"); + return -1; + } + + if (genl_connect(sk) < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "connection to genl socket failed"); + goto out_free; + } + + if (genl_ctrl_resolve(sk, "macsec") < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "genl resolve failed - macsec kernel module not present?"); + goto out_free; + } + + err = 0; + +out_free: + nl_socket_free(sk); + return err; +} + + +static void * macsec_drv_wpa_init(void *ctx, const char *ifname) +{ + struct macsec_drv_data *drv; + + if (macsec_check_macsec() < 0) + return NULL; + + drv = os_zalloc(sizeof(*drv)); + if (!drv) + return NULL; + + if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) { + os_free(drv); + return NULL; + } + + return drv; +} + + +static int macsec_drv_macsec_init(void *priv, struct macsec_init_params *params) +{ + struct macsec_drv_data *drv = priv; + int err; + + wpa_printf(MSG_DEBUG, "%s", __func__); + + drv->sk = nl_socket_alloc(); + if (!drv->sk) + return -1; + + err = nl_connect(drv->sk, NETLINK_ROUTE); + if (err < 0) { + wpa_printf(MSG_ERROR, DRV_PREFIX + "Unable to connect NETLINK_ROUTE socket: %s", + strerror(errno)); + goto sock; + } + + err = rtnl_link_alloc_cache(drv->sk, AF_UNSPEC, &drv->link_cache); + if (err < 0) { + wpa_printf(MSG_ERROR, DRV_PREFIX "Unable to get link cache: %s", + strerror(errno)); + goto sock; + } + + drv->parent_ifi = rtnl_link_name2i(drv->link_cache, drv->common.ifname); + if (drv->parent_ifi == 0) { + wpa_printf(MSG_ERROR, DRV_PREFIX + "couldn't find ifindex for interface %s", + drv->common.ifname); + goto cache; + } + + err = init_genl_ctx(drv); + if (err < 0) + goto cache; + + return 0; + +cache: + nl_cache_free(drv->link_cache); + drv->link_cache = NULL; +sock: + nl_socket_free(drv->sk); + drv->sk = NULL; + return -1; +} + + +static int macsec_drv_macsec_deinit(void *priv) +{ + struct macsec_drv_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s", __func__); + + if (drv->sk) + nl_socket_free(drv->sk); + drv->sk = NULL; + + if (drv->link_cache) + nl_cache_free(drv->link_cache); + drv->link_cache = NULL; + + if (drv->ctx.sk) + nl_socket_free(drv->ctx.sk); + + return 0; +} + + +static int macsec_drv_get_capability(void *priv, enum macsec_cap *cap) +{ + wpa_printf(MSG_DEBUG, "%s", __func__); + + *cap = MACSEC_CAP_INTEG_AND_CONF; + + return 0; +} + + +/** + * macsec_drv_enable_protect_frames - Set protect frames status + * @priv: Private driver interface data + * @enabled: TRUE = protect frames enabled + * FALSE = protect frames disabled + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_enable_protect_frames(void *priv, Boolean enabled) +{ + struct macsec_drv_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE"); + + drv->protect_frames_set = TRUE; + drv->protect_frames = enabled; + + return try_commit(drv); +} + + +/** + * macsec_drv_enable_encrypt - Set protect frames status + * @priv: Private driver interface data + * @enabled: TRUE = protect frames enabled + * FALSE = protect frames disabled + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_enable_encrypt(void *priv, Boolean enabled) +{ + struct macsec_drv_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE"); + + drv->encrypt_set = TRUE; + drv->encrypt = enabled; + + return try_commit(drv); +} + + +/** + * macsec_drv_set_replay_protect - Set replay protect status and window size + * @priv: Private driver interface data + * @enabled: TRUE = replay protect enabled + * FALSE = replay protect disabled + * @window: replay window size, valid only when replay protect enabled + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_set_replay_protect(void *priv, Boolean enabled, + u32 window) +{ + struct macsec_drv_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s -> %s, %u", __func__, + enabled ? "TRUE" : "FALSE", window); + + drv->replay_protect_set = TRUE; + drv->replay_protect = enabled; + if (enabled) + drv->replay_window = window; + + return try_commit(drv); +} + + +/** + * macsec_drv_set_current_cipher_suite - Set current cipher suite + * @priv: Private driver interface data + * @cs: EUI64 identifier + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_set_current_cipher_suite(void *priv, u64 cs) +{ + wpa_printf(MSG_DEBUG, "%s -> %016" PRIx64, __func__, cs); + return 0; +} + + +/** + * macsec_drv_enable_controlled_port - Set controlled port status + * @priv: Private driver interface data + * @enabled: TRUE = controlled port enabled + * FALSE = controlled port disabled + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_enable_controlled_port(void *priv, Boolean enabled) +{ + struct macsec_drv_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE"); + + drv->controlled_port_enabled = enabled; + drv->controlled_port_enabled_set = TRUE; + + return try_commit(drv); +} + + +static struct nla_policy sa_policy[MACSEC_SA_ATTR_MAX + 1] = { + [MACSEC_SA_ATTR_AN] = { .type = NLA_U8 }, + [MACSEC_SA_ATTR_ACTIVE] = { .type = NLA_U8 }, + [MACSEC_SA_ATTR_PN] = { .type = NLA_U32 }, + [MACSEC_SA_ATTR_KEYID] = { .type = NLA_BINARY }, +}; + +static struct nla_policy sc_policy[MACSEC_RXSC_ATTR_MAX + 1] = { + [MACSEC_RXSC_ATTR_SCI] = { .type = NLA_U64 }, + [MACSEC_RXSC_ATTR_ACTIVE] = { .type = NLA_U8 }, + [MACSEC_RXSC_ATTR_SA_LIST] = { .type = NLA_NESTED }, +}; + +static struct nla_policy main_policy[MACSEC_ATTR_MAX + 1] = { + [MACSEC_ATTR_IFINDEX] = { .type = NLA_U32 }, + [MACSEC_ATTR_SECY] = { .type = NLA_NESTED }, + [MACSEC_ATTR_TXSA_LIST] = { .type = NLA_NESTED }, + [MACSEC_ATTR_RXSC_LIST] = { .type = NLA_NESTED }, +}; + +static int dump_callback(struct nl_msg *msg, void *argp) +{ + struct nlmsghdr *ret_hdr = nlmsg_hdr(msg); + struct nlattr *tb_msg[MACSEC_ATTR_MAX + 1]; + struct cb_arg *arg = (struct cb_arg *) argp; + struct genlmsghdr *gnlh = (struct genlmsghdr *) nlmsg_data(ret_hdr); + int err; + + if (ret_hdr->nlmsg_type != arg->drv->ctx.macsec_genl_id) + return 0; + + err = nla_parse(tb_msg, MACSEC_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), main_policy); + if (err < 0) + return 0; + + if (!tb_msg[MACSEC_ATTR_IFINDEX]) + return 0; + + if (nla_get_u32(tb_msg[MACSEC_ATTR_IFINDEX]) != (u32) arg->ifindex) + return 0; + + if (arg->txsa < 4 && !tb_msg[MACSEC_ATTR_TXSA_LIST]) { + return 0; + } else if (arg->txsa < 4) { + struct nlattr *nla; + int rem; + + nla_for_each_nested(nla, tb_msg[MACSEC_ATTR_TXSA_LIST], rem) { + struct nlattr *tb[MACSEC_SA_ATTR_MAX + 1]; + + err = nla_parse_nested(tb, MACSEC_SA_ATTR_MAX, nla, + sa_policy); + if (err < 0) + continue; + if (!tb[MACSEC_SA_ATTR_AN]) + continue; + if (nla_get_u8(tb[MACSEC_SA_ATTR_AN]) != arg->txsa) + continue; + if (!tb[MACSEC_SA_ATTR_PN]) + return 0; + *arg->pn = nla_get_u32(tb[MACSEC_SA_ATTR_PN]); + return 0; + } + + return 0; + } + + if (arg->rxsci == UNUSED_SCI) + return 0; + + if (tb_msg[MACSEC_ATTR_RXSC_LIST]) { + struct nlattr *nla; + int rem; + + nla_for_each_nested(nla, tb_msg[MACSEC_ATTR_RXSC_LIST], rem) { + struct nlattr *tb[MACSEC_RXSC_ATTR_MAX + 1]; + + err = nla_parse_nested(tb, MACSEC_RXSC_ATTR_MAX, nla, + sc_policy); + if (err < 0) + return 0; + if (!tb[MACSEC_RXSC_ATTR_SCI]) + continue; + if (nla_get_u64(tb[MACSEC_RXSC_ATTR_SCI]) != arg->rxsci) + continue; + if (!tb[MACSEC_RXSC_ATTR_SA_LIST]) + return 0; + + nla_for_each_nested(nla, tb[MACSEC_RXSC_ATTR_SA_LIST], + rem) { + struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1]; + + err = nla_parse_nested(tb_sa, + MACSEC_SA_ATTR_MAX, nla, + sa_policy); + if (err < 0) + continue; + if (!tb_sa[MACSEC_SA_ATTR_AN]) + continue; + if (nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]) != + arg->rxsa) + continue; + if (!tb_sa[MACSEC_SA_ATTR_PN]) + return 0; + *arg->pn = + nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]); + + return 0; + } + + return 0; + } + + return 0; + } + + return 0; +} + + +static int nl_send_recv(struct nl_sock *sk, struct nl_msg *msg) +{ + int ret; + + ret = nl_send_auto_complete(sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to send: %d (%s)", + __func__, ret, nl_geterror(-ret)); + return ret; + } + + ret = nl_recvmsgs_default(sk); + if (ret < 0) { + wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to recv: %d (%s)", + __func__, ret, nl_geterror(-ret)); + } + + return ret; +} + + +static int do_dump(struct macsec_drv_data *drv, u8 txsa, u64 rxsci, u8 rxsa, + u32 *pn) +{ + struct macsec_genl_ctx *ctx = &drv->ctx; + struct nl_msg *msg; + int ret = 1; + + ctx->cb_arg.ifindex = drv->ifi; + ctx->cb_arg.rxsci = rxsci; + ctx->cb_arg.rxsa = rxsa; + ctx->cb_arg.txsa = txsa; + ctx->cb_arg.pn = pn; + + msg = nlmsg_alloc(); + if (!msg) { + wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to alloc message", + __func__); + return 1; + } + + if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, ctx->macsec_genl_id, 0, + NLM_F_DUMP, MACSEC_CMD_GET_TXSC, 0)) { + wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to put header", + __func__); + goto out_free_msg; + } + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) + wpa_printf(MSG_ERROR, + DRV_PREFIX "failed to communicate: %d (%s)", + ret, nl_geterror(-ret)); + + ctx->cb_arg.pn = NULL; + +out_free_msg: + nlmsg_free(msg); + return ret; +} + + +/** + * macsec_drv_get_receive_lowest_pn - Get receive lowest PN + * @priv: Private driver interface data + * @sa: secure association + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_get_receive_lowest_pn(void *priv, struct receive_sa *sa) +{ + struct macsec_drv_data *drv = priv; + int err; + + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s", __func__); + + err = do_dump(drv, 0xff, mka_sci_u64(&sa->sc->sci), sa->an, + &sa->lowest_pn); + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: result %d", __func__, + sa->lowest_pn); + + return err; +} + + +/** + * macsec_drv_get_transmit_next_pn - Get transmit next PN + * @priv: Private driver interface data + * @sa: secure association + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_get_transmit_next_pn(void *priv, struct transmit_sa *sa) +{ + struct macsec_drv_data *drv = priv; + int err; + + wpa_printf(MSG_DEBUG, "%s", __func__); + + err = do_dump(drv, sa->an, UNUSED_SCI, 0xff, &sa->next_pn); + wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: err %d result %d", __func__, err, + sa->next_pn); + return err; +} + + +/** + * macsec_drv_set_transmit_next_pn - Set transmit next pn + * @priv: Private driver interface data + * @sa: secure association + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_set_transmit_next_pn(void *priv, struct transmit_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + struct nl_msg *msg; + struct nlattr *nest; + int ret = -1; + + wpa_printf(MSG_DEBUG, "%s -> %d: %d", __func__, sa->an, sa->next_pn); + + msg = msg_prepare(MACSEC_CMD_UPD_TXSA, ctx, drv->ifi); + if (!msg) + return ret; + + nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG); + if (!nest) + goto nla_put_failure; + + NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an); + NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn); + + nla_nest_end(msg, nest); + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "failed to communicate: %d (%s)", + ret, nl_geterror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +#define SCISTR MACSTR "::%hx" +#define SCI2STR(addr, port) MAC2STR(addr), htons(port) + +/** + * macsec_drv_create_receive_sc - Create secure channel for receiving + * @priv: Private driver interface data + * @sc: secure channel + * @sci_addr: secure channel identifier - address + * @sci_port: secure channel identifier - port + * @conf_offset: confidentiality offset (0, 30, or 50) + * @validation: frame validation policy (0 = Disabled, 1 = Checked, + * 2 = Strict) + * Returns: 0 on success, -1 on failure (or if not supported) + */ +static int macsec_drv_create_receive_sc(void *priv, struct receive_sc *sc, + unsigned int conf_offset, + int validation) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + struct nl_msg *msg; + int ret = -1; + + wpa_printf(MSG_DEBUG, "%s -> " SCISTR, __func__, + SCI2STR(sc->sci.addr, sc->sci.port)); + + msg = msg_prepare(MACSEC_CMD_ADD_RXSC, ctx, drv->ifi); + if (!msg) + return ret; + + if (nla_put_rxsc_config(msg, mka_sci_u64(&sc->sci))) + goto nla_put_failure; + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "%s: failed to communicate: %d (%s)", + __func__, ret, nl_geterror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +/** + * macsec_drv_delete_receive_sc - Delete secure connection for receiving + * @priv: private driver interface data from init() + * @sc: secure channel + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_delete_receive_sc(void *priv, struct receive_sc *sc) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + struct nl_msg *msg; + int ret = -1; + + wpa_printf(MSG_DEBUG, "%s -> " SCISTR, __func__, + SCI2STR(sc->sci.addr, sc->sci.port)); + + msg = msg_prepare(MACSEC_CMD_DEL_RXSC, ctx, drv->ifi); + if (!msg) + return ret; + + if (nla_put_rxsc_config(msg, mka_sci_u64(&sc->sci))) + goto nla_put_failure; + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "%s: failed to communicate: %d (%s)", + __func__, ret, nl_geterror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +/** + * macsec_drv_create_receive_sa - Create secure association for receive + * @priv: private driver interface data from init() + * @sa: secure association + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_create_receive_sa(void *priv, struct receive_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + struct nl_msg *msg; + struct nlattr *nest; + int ret = -1; + + wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an, + SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); + + msg = msg_prepare(MACSEC_CMD_ADD_RXSA, ctx, drv->ifi); + if (!msg) + return ret; + + if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci))) + goto nla_put_failure; + + nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG); + if (!nest) + goto nla_put_failure; + + NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an); + NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, sa->enable_receive); + NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn); + NLA_PUT(msg, MACSEC_SA_ATTR_KEYID, sizeof(sa->pkey->key_identifier), + &sa->pkey->key_identifier); + NLA_PUT(msg, MACSEC_SA_ATTR_KEY, sa->pkey->key_len, sa->pkey->key); + + nla_nest_end(msg, nest); + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "%s: failed to communicate: %d (%s)", + __func__, ret, nl_geterror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +/** + * macsec_drv_delete_receive_sa - Delete secure association for receive + * @priv: private driver interface data from init() + * @sa: secure association + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_delete_receive_sa(void *priv, struct receive_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + struct nl_msg *msg; + struct nlattr *nest; + int ret = -1; + + wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an, + SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); + + msg = msg_prepare(MACSEC_CMD_DEL_RXSA, ctx, drv->ifi); + if (!msg) + return ret; + + if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci))) + goto nla_put_failure; + + nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG); + if (!nest) + goto nla_put_failure; + + NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an); + + nla_nest_end(msg, nest); + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "%s: failed to communicate: %d (%s)", + __func__, ret, nl_geterror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int set_active_rx_sa(const struct macsec_genl_ctx *ctx, int ifindex, + u64 sci, unsigned char an, Boolean state) +{ + struct nl_msg *msg; + struct nlattr *nest; + int ret = -1; + + msg = msg_prepare(MACSEC_CMD_UPD_RXSA, ctx, ifindex); + if (!msg) + return ret; + + if (nla_put_rxsc_config(msg, sci)) + goto nla_put_failure; + + nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG); + if (!nest) + goto nla_put_failure; + + NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, an); + NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, !!state); + + nla_nest_end(msg, nest); + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) + wpa_printf(MSG_ERROR, + DRV_PREFIX "%s: failed to communicate: %d (%s)", + __func__, ret, nl_geterror(-ret)); + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +/** + * macsec_drv_enable_receive_sa - Enable the SA for receive + * @priv: private driver interface data from init() + * @sa: secure association + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_enable_receive_sa(void *priv, struct receive_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + + wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an, + SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); + + return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci), + sa->an, TRUE); +} + + +/** + * macsec_drv_disable_receive_sa - Disable SA for receive + * @priv: private driver interface data from init() + * @sa: secure association + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_disable_receive_sa(void *priv, struct receive_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + + wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an, + SCI2STR(sa->sc->sci.addr, sa->sc->sci.port)); + + return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci), + sa->an, FALSE); +} + + +static struct rtnl_link * lookup_sc(struct nl_cache *cache, int parent, u64 sci) +{ + struct rtnl_link *needle; + void *match; + + needle = rtnl_link_macsec_alloc(); + if (!needle) + return NULL; + + rtnl_link_set_link(needle, parent); + rtnl_link_macsec_set_sci(needle, sci); + + match = nl_cache_find(cache, (struct nl_object *) needle); + rtnl_link_put(needle); + + return (struct rtnl_link *) match; +} + + +/** + * macsec_drv_create_transmit_sc - Create secure connection for transmit + * @priv: private driver interface data from init() + * @sc: secure channel + * @conf_offset: confidentiality offset + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_create_transmit_sc( + void *priv, struct transmit_sc *sc, + unsigned int conf_offset) +{ + struct macsec_drv_data *drv = priv; + struct rtnl_link *link; + char *ifname; + u64 sci; + int err; + + wpa_printf(MSG_DEBUG, "%s", __func__); + + if (!drv->sk) { + wpa_printf(MSG_ERROR, DRV_PREFIX "NULL rtnl socket"); + return -1; + } + + link = rtnl_link_macsec_alloc(); + if (!link) { + wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link"); + return -1; + } + + rtnl_link_set_link(link, drv->parent_ifi); + + sci = mka_sci_u64(&sc->sci); + rtnl_link_macsec_set_sci(link, sci); + + drv->created_link = TRUE; + + err = rtnl_link_add(drv->sk, link, NLM_F_CREATE); + if (err == -NLE_BUSY) { + wpa_printf(MSG_INFO, + DRV_PREFIX "link already exists, using it"); + drv->created_link = FALSE; + } else if (err < 0) { + rtnl_link_put(link); + wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't create link: err %d", + err); + return err; + } + + rtnl_link_put(link); + + nl_cache_refill(drv->sk, drv->link_cache); + link = lookup_sc(drv->link_cache, drv->parent_ifi, sci); + if (!link) { + wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't find link"); + return -1; + } + + drv->ifi = rtnl_link_get_ifindex(link); + ifname = rtnl_link_get_name(link); + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + rtnl_link_put(link); + + drv->link = rtnl_link_macsec_alloc(); + if (!drv->link) { + wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link"); + return -1; + } + + rtnl_link_set_name(drv->link, drv->ifname); + + /* In case some settings have already been done but we couldn't apply + * them. */ + return try_commit(drv); +} + + +/** + * macsec_drv_delete_transmit_sc - Delete secure connection for transmit + * @priv: private driver interface data from init() + * @sc: secure channel + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_delete_transmit_sc(void *priv, struct transmit_sc *sc) +{ + struct macsec_drv_data *drv = priv; + int err; + + wpa_printf(MSG_DEBUG, "%s", __func__); + + if (!drv->sk) + return 0; + + if (!drv->created_link) { + rtnl_link_put(drv->link); + drv->link = NULL; + wpa_printf(MSG_DEBUG, DRV_PREFIX + "we didn't create the link, leave it alone"); + return 0; + } + + err = rtnl_link_delete(drv->sk, drv->link); + if (err < 0) + wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't delete link"); + rtnl_link_put(drv->link); + drv->link = NULL; + + return err; +} + + +/** + * macsec_drv_create_transmit_sa - Create secure association for transmit + * @priv: private driver interface data from init() + * @sa: secure association + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_create_transmit_sa(void *priv, struct transmit_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + struct nl_msg *msg; + struct nlattr *nest; + int ret = -1; + + wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an); + + msg = msg_prepare(MACSEC_CMD_ADD_TXSA, ctx, drv->ifi); + if (!msg) + return ret; + + nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG); + if (!nest) + goto nla_put_failure; + + NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an); + NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn); + NLA_PUT(msg, MACSEC_SA_ATTR_KEYID, sizeof(sa->pkey->key_identifier), + &sa->pkey->key_identifier); + NLA_PUT(msg, MACSEC_SA_ATTR_KEY, sa->pkey->key_len, sa->pkey->key); + NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, sa->enable_transmit); + + nla_nest_end(msg, nest); + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "%s: failed to communicate: %d (%s)", + __func__, ret, nl_geterror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +/** + * macsec_drv_delete_transmit_sa - Delete secure association for transmit + * @priv: private driver interface data from init() + * @sa: secure association + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_delete_transmit_sa(void *priv, struct transmit_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + struct nl_msg *msg; + struct nlattr *nest; + int ret = -1; + + wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an); + + msg = msg_prepare(MACSEC_CMD_DEL_TXSA, ctx, drv->ifi); + if (!msg) + return ret; + + nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG); + if (!nest) + goto nla_put_failure; + + NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an); + + nla_nest_end(msg, nest); + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "%s: failed to communicate: %d (%s)", + __func__, ret, nl_geterror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int set_active_tx_sa(const struct macsec_genl_ctx *ctx, int ifindex, + unsigned char an, Boolean state) +{ + struct nl_msg *msg; + struct nlattr *nest; + int ret = -1; + + msg = msg_prepare(MACSEC_CMD_UPD_TXSA, ctx, ifindex); + if (!msg) + return ret; + + nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG); + if (!nest) + goto nla_put_failure; + + NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, an); + NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, !!state); + + nla_nest_end(msg, nest); + + ret = nl_send_recv(ctx->sk, msg); + if (ret < 0) { + wpa_printf(MSG_ERROR, + DRV_PREFIX "%s: failed to communicate: %d (%s)", + __func__, ret, nl_geterror(-ret)); + } + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +/** + * macsec_drv_enable_transmit_sa - Enable SA for transmit + * @priv: private driver interface data from init() + * @sa: secure association + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_enable_transmit_sa(void *priv, struct transmit_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + int ret; + + wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an); + + ret = set_active_tx_sa(ctx, drv->ifi, sa->an, TRUE); + if (ret < 0) { + wpa_printf(MSG_ERROR, DRV_PREFIX "failed to enable txsa"); + return ret; + } + + drv->encoding_sa_set = TRUE; + drv->encoding_sa = sa->an; + + return try_commit(drv); +} + + +/** + * macsec_drv_disable_transmit_sa - Disable SA for transmit + * @priv: private driver interface data from init() + * @sa: secure association + * Returns: 0 on success, -1 on failure + */ +static int macsec_drv_disable_transmit_sa(void *priv, struct transmit_sa *sa) +{ + struct macsec_drv_data *drv = priv; + struct macsec_genl_ctx *ctx = &drv->ctx; + + wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an); + + return set_active_tx_sa(ctx, drv->ifi, sa->an, FALSE); +} + + +const struct wpa_driver_ops wpa_driver_macsec_linux_ops = { + .name = "macsec_linux", + .desc = "MACsec Ethernet driver for Linux", + .get_ssid = driver_wired_get_ssid, + .get_bssid = driver_wired_get_bssid, + .get_capa = driver_wired_get_capa, + .init = macsec_drv_wpa_init, + .deinit = macsec_drv_wpa_deinit, + + .macsec_init = macsec_drv_macsec_init, + .macsec_deinit = macsec_drv_macsec_deinit, + .macsec_get_capability = macsec_drv_get_capability, + .enable_protect_frames = macsec_drv_enable_protect_frames, + .enable_encrypt = macsec_drv_enable_encrypt, + .set_replay_protect = macsec_drv_set_replay_protect, + .set_current_cipher_suite = macsec_drv_set_current_cipher_suite, + .enable_controlled_port = macsec_drv_enable_controlled_port, + .get_receive_lowest_pn = macsec_drv_get_receive_lowest_pn, + .get_transmit_next_pn = macsec_drv_get_transmit_next_pn, + .set_transmit_next_pn = macsec_drv_set_transmit_next_pn, + .create_receive_sc = macsec_drv_create_receive_sc, + .delete_receive_sc = macsec_drv_delete_receive_sc, + .create_receive_sa = macsec_drv_create_receive_sa, + .delete_receive_sa = macsec_drv_delete_receive_sa, + .enable_receive_sa = macsec_drv_enable_receive_sa, + .disable_receive_sa = macsec_drv_disable_receive_sa, + .create_transmit_sc = macsec_drv_create_transmit_sc, + .delete_transmit_sc = macsec_drv_delete_transmit_sc, + .create_transmit_sa = macsec_drv_create_transmit_sa, + .delete_transmit_sa = macsec_drv_delete_transmit_sa, + .enable_transmit_sa = macsec_drv_enable_transmit_sa, + .disable_transmit_sa = macsec_drv_disable_transmit_sa, +}; diff --git a/src/drivers/driver_macsec_qca.c b/src/drivers/driver_macsec_qca.c index 826d3cc621335..8372393f26c27 100644 --- a/src/drivers/driver_macsec_qca.c +++ b/src/drivers/driver_macsec_qca.c @@ -29,7 +29,9 @@ #include "utils/eloop.h" #include "common/defs.h" #include "common/ieee802_1x_defs.h" +#include "pae/ieee802_1x_kay.h" #include "driver.h" +#include "driver_wired_common.h" #include "nss_macsec_secy.h" #include "nss_macsec_secy_rx.h" @@ -37,6 +39,9 @@ #define MAXSC 16 +#define SAK_128_LEN 16 +#define SAK_256_LEN 32 + /* TCI field definition */ #define TCI_ES 0x40 #define TCI_SC 0x20 @@ -52,17 +57,14 @@ #pragma pack(pop) #endif /* _MSC_VER */ -static const u8 pae_group_addr[ETH_ALEN] = -{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; +struct channel_map { + struct ieee802_1x_mka_sci sci; +}; struct macsec_qca_data { - char ifname[IFNAMSIZ + 1]; - u32 secy_id; - void *ctx; + struct driver_wired_common_data common; - int sock; /* raw packet socket for driver access */ - int pf_sock; - int membership, multi, iff_allmulti, iff_up; + u32 secy_id; /* shadow */ Boolean always_include_sci; @@ -71,192 +73,10 @@ struct macsec_qca_data { Boolean protect_frames; Boolean replay_protect; u32 replay_window; -}; - - -static int macsec_qca_multicast_membership(int sock, int ifindex, - const u8 *addr, int add) -{ -#ifdef __linux__ - struct packet_mreq mreq; - - if (sock < 0) - return -1; - - os_memset(&mreq, 0, sizeof(mreq)); - mreq.mr_ifindex = ifindex; - mreq.mr_type = PACKET_MR_MULTICAST; - mreq.mr_alen = ETH_ALEN; - os_memcpy(mreq.mr_address, addr, ETH_ALEN); - - if (setsockopt(sock, SOL_PACKET, - add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, - &mreq, sizeof(mreq)) < 0) { - wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno)); - return -1; - } - return 0; -#else /* __linux__ */ - return -1; -#endif /* __linux__ */ -} - - -static int macsec_qca_get_ssid(void *priv, u8 *ssid) -{ - ssid[0] = 0; - return 0; -} - - -static int macsec_qca_get_bssid(void *priv, u8 *bssid) -{ - /* Report PAE group address as the "BSSID" for macsec connection. */ - os_memcpy(bssid, pae_group_addr, ETH_ALEN); - return 0; -} - - -static int macsec_qca_get_capa(void *priv, struct wpa_driver_capa *capa) -{ - os_memset(capa, 0, sizeof(*capa)); - capa->flags = WPA_DRIVER_FLAGS_WIRED; - return 0; -} - - -static int macsec_qca_get_ifflags(const char *ifname, int *flags) -{ - struct ifreq ifr; - int s; - - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s < 0) { - wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); - return -1; - } - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s", - strerror(errno)); - close(s); - return -1; - } - close(s); - *flags = ifr.ifr_flags & 0xffff; - return 0; -} - - -static int macsec_qca_set_ifflags(const char *ifname, int flags) -{ - struct ifreq ifr; - int s; - - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s < 0) { - wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); - return -1; - } - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - ifr.ifr_flags = flags & 0xffff; - if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s", - strerror(errno)); - close(s); - return -1; - } - close(s); - return 0; -} - -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) -static int macsec_qca_get_ifstatus(const char *ifname, int *status) -{ - struct ifmediareq ifmr; - int s; - - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s < 0) { - wpa_print(MSG_ERROR, "socket: %s", strerror(errno)); - return -1; - } - - os_memset(&ifmr, 0, sizeof(ifmr)); - os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ); - if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s", - strerror(errno)); - close(s); - return -1; - } - close(s); - *status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) == - (IFM_ACTIVE | IFM_AVALID); - - return 0; -} -#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ - - -static int macsec_qca_multi(const char *ifname, const u8 *addr, int add) -{ - struct ifreq ifr; - int s; - -#ifdef __sun__ - return -1; -#endif /* __sun__ */ - - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s < 0) { - wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); - return -1; - } - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); -#ifdef __linux__ - ifr.ifr_hwaddr.sa_family = AF_UNSPEC; - os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); -#endif /* __linux__ */ -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) - { - struct sockaddr_dl *dlp; - dlp = (struct sockaddr_dl *) &ifr.ifr_addr; - dlp->sdl_len = sizeof(struct sockaddr_dl); - dlp->sdl_family = AF_LINK; - dlp->sdl_index = 0; - dlp->sdl_nlen = 0; - dlp->sdl_alen = ETH_ALEN; - dlp->sdl_slen = 0; - os_memcpy(LLADDR(dlp), addr, ETH_ALEN); - } -#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ -#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) - { - struct sockaddr *sap; - sap = (struct sockaddr *) &ifr.ifr_addr; - sap->sa_len = sizeof(struct sockaddr); - sap->sa_family = AF_UNSPEC; - os_memcpy(sap->sa_data, addr, ETH_ALEN); - } -#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */ - - if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s", - strerror(errno)); - close(s); - return -1; - } - close(s); - return 0; -} + struct channel_map receive_channel_map[MAXSC]; + struct channel_map transmit_channel_map[MAXSC]; +}; static void __macsec_drv_init(struct macsec_qca_data *drv) @@ -309,76 +129,23 @@ static void __macsec_drv_deinit(struct macsec_qca_data *drv) static void * macsec_qca_init(void *ctx, const char *ifname) { struct macsec_qca_data *drv; - int flags; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) return NULL; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - drv->ctx = ctx; /* Board specific settings */ - if (os_memcmp("eth2", drv->ifname, 4) == 0) + if (os_memcmp("eth2", ifname, 4) == 0) drv->secy_id = 1; - else if (os_memcmp("eth3", drv->ifname, 4) == 0) + else if (os_memcmp("eth3", ifname, 4) == 0) drv->secy_id = 2; else drv->secy_id = -1; -#ifdef __linux__ - drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0); - if (drv->pf_sock < 0) - wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno)); -#else /* __linux__ */ - drv->pf_sock = -1; -#endif /* __linux__ */ - - if (macsec_qca_get_ifflags(ifname, &flags) == 0 && - !(flags & IFF_UP) && - macsec_qca_set_ifflags(ifname, flags | IFF_UP) == 0) { - drv->iff_up = 1; - } - - if (macsec_qca_multicast_membership(drv->pf_sock, - if_nametoindex(drv->ifname), - pae_group_addr, 1) == 0) { - wpa_printf(MSG_DEBUG, - "%s: Added multicast membership with packet socket", - __func__); - drv->membership = 1; - } else if (macsec_qca_multi(ifname, pae_group_addr, 1) == 0) { - wpa_printf(MSG_DEBUG, - "%s: Added multicast membership with SIOCADDMULTI", - __func__); - drv->multi = 1; - } else if (macsec_qca_get_ifflags(ifname, &flags) < 0) { - wpa_printf(MSG_INFO, "%s: Could not get interface flags", - __func__); + if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) { os_free(drv); return NULL; - } else if (flags & IFF_ALLMULTI) { - wpa_printf(MSG_DEBUG, - "%s: Interface is already configured for multicast", - __func__); - } else if (macsec_qca_set_ifflags(ifname, flags | IFF_ALLMULTI) < 0) { - wpa_printf(MSG_INFO, "%s: Failed to enable allmulti", - __func__); - os_free(drv); - return NULL; - } else { - wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", __func__); - drv->iff_allmulti = 1; } -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) - { - int status; - wpa_printf(MSG_DEBUG, "%s: waiting for link to become active", - __func__); - while (macsec_qca_get_ifstatus(ifname, &status) == 0 && - status == 0) - sleep(1); - } -#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ return drv; } @@ -387,42 +154,8 @@ static void * macsec_qca_init(void *ctx, const char *ifname) static void macsec_qca_deinit(void *priv) { struct macsec_qca_data *drv = priv; - int flags; - - if (drv->membership && - macsec_qca_multicast_membership(drv->pf_sock, - if_nametoindex(drv->ifname), - pae_group_addr, 0) < 0) { - wpa_printf(MSG_DEBUG, - "%s: Failed to remove PAE multicast group (PACKET)", - __func__); - } - - if (drv->multi && - macsec_qca_multi(drv->ifname, pae_group_addr, 0) < 0) { - wpa_printf(MSG_DEBUG, - "%s: Failed to remove PAE multicast group (SIOCDELMULTI)", - __func__); - } - - if (drv->iff_allmulti && - (macsec_qca_get_ifflags(drv->ifname, &flags) < 0 || - macsec_qca_set_ifflags(drv->ifname, flags & ~IFF_ALLMULTI) < 0)) { - wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode", - __func__); - } - - if (drv->iff_up && - macsec_qca_get_ifflags(drv->ifname, &flags) == 0 && - (flags & IFF_UP) && - macsec_qca_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down", - __func__); - } - - if (drv->pf_sock != -1) - close(drv->pf_sock); + driver_wired_deinit_common(&drv->common); os_free(drv); } @@ -457,6 +190,16 @@ static int macsec_qca_macsec_deinit(void *priv) } +static int macsec_qca_get_capability(void *priv, enum macsec_cap *cap) +{ + wpa_printf(MSG_DEBUG, "%s", __func__); + + *cap = MACSEC_CAP_INTEG_AND_CONF_0_30_50; + + return 0; +} + + static int macsec_qca_enable_protect_frames(void *priv, Boolean enabled) { struct macsec_qca_data *drv = priv; @@ -486,19 +229,32 @@ static int macsec_qca_set_replay_protect(void *priv, Boolean enabled, } +static fal_cipher_suite_e macsec_qca_cs_type_get(u64 cs) +{ + if (cs == CS_ID_GCM_AES_128) + return FAL_CIPHER_SUITE_AES_GCM_128; + if (cs == CS_ID_GCM_AES_256) + return FAL_CIPHER_SUITE_AES_GCM_256; + return FAL_CIPHER_SUITE_MAX; +} + + static int macsec_qca_set_current_cipher_suite(void *priv, u64 cs) { - if (cs != CS_ID_GCM_AES_128) { + struct macsec_qca_data *drv = priv; + fal_cipher_suite_e cs_type; + + if (cs != CS_ID_GCM_AES_128 && cs != CS_ID_GCM_AES_256) { wpa_printf(MSG_ERROR, "%s: NOT supported CipherSuite: %016" PRIx64, __func__, cs); return -1; } - /* Support default Cipher Suite 0080020001000001 (GCM-AES-128) */ - wpa_printf(MSG_DEBUG, "%s: default support aes-gcm-128", __func__); + wpa_printf(MSG_DEBUG, "%s: CipherSuite: %016" PRIx64, __func__, cs); - return 0; + cs_type = macsec_qca_cs_type_get(cs); + return nss_macsec_secy_cipher_suite_set(drv->secy_id, cs_type); } @@ -515,16 +271,82 @@ static int macsec_qca_enable_controlled_port(void *priv, Boolean enabled) } -static int macsec_qca_get_receive_lowest_pn(void *priv, u32 channel, u8 an, - u32 *lowest_pn) +static int macsec_qca_lookup_channel(struct channel_map *map, + struct ieee802_1x_mka_sci *sci, + u32 *channel) +{ + u32 i; + + for (i = 0; i < MAXSC; i++) { + if (os_memcmp(&map[i].sci, sci, + sizeof(struct ieee802_1x_mka_sci)) == 0) { + *channel = i; + return 0; + } + } + + return -1; +} + + +static void macsec_qca_register_channel(struct channel_map *map, + struct ieee802_1x_mka_sci *sci, + u32 channel) +{ + os_memcpy(&map[channel].sci, sci, sizeof(struct ieee802_1x_mka_sci)); +} + + +static int macsec_qca_lookup_receive_channel(struct macsec_qca_data *drv, + struct receive_sc *sc, + u32 *channel) +{ + return macsec_qca_lookup_channel(drv->receive_channel_map, &sc->sci, + channel); +} + + +static void macsec_qca_register_receive_channel(struct macsec_qca_data *drv, + struct receive_sc *sc, + u32 channel) +{ + macsec_qca_register_channel(drv->receive_channel_map, &sc->sci, + channel); +} + + +static int macsec_qca_lookup_transmit_channel(struct macsec_qca_data *drv, + struct transmit_sc *sc, + u32 *channel) +{ + return macsec_qca_lookup_channel(drv->transmit_channel_map, &sc->sci, + channel); +} + + +static void macsec_qca_register_transmit_channel(struct macsec_qca_data *drv, + struct transmit_sc *sc, + u32 channel) +{ + macsec_qca_register_channel(drv->transmit_channel_map, &sc->sci, + channel); +} + + +static int macsec_qca_get_receive_lowest_pn(void *priv, struct receive_sa *sa) { struct macsec_qca_data *drv = priv; int ret = 0; u32 next_pn = 0; bool enabled = FALSE; u32 win; + u32 channel; + + ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel); + if (ret != 0) + return ret; - ret += nss_macsec_secy_rx_sa_next_pn_get(drv->secy_id, channel, an, + ret += nss_macsec_secy_rx_sa_next_pn_get(drv->secy_id, channel, sa->an, &next_pn); ret += nss_macsec_secy_rx_sc_replay_protect_get(drv->secy_id, channel, &enabled); @@ -532,40 +354,49 @@ static int macsec_qca_get_receive_lowest_pn(void *priv, u32 channel, u8 an, channel, &win); if (enabled) - *lowest_pn = (next_pn > win) ? (next_pn - win) : 1; + sa->lowest_pn = (next_pn > win) ? (next_pn - win) : 1; else - *lowest_pn = next_pn; + sa->lowest_pn = next_pn; - wpa_printf(MSG_DEBUG, "%s: lpn=0x%x", __func__, *lowest_pn); + wpa_printf(MSG_DEBUG, "%s: lpn=0x%x", __func__, sa->lowest_pn); return ret; } -static int macsec_qca_get_transmit_next_pn(void *priv, u32 channel, u8 an, - u32 *next_pn) +static int macsec_qca_get_transmit_next_pn(void *priv, struct transmit_sa *sa) { struct macsec_qca_data *drv = priv; int ret = 0; + u32 channel; - ret += nss_macsec_secy_tx_sa_next_pn_get(drv->secy_id, channel, an, - next_pn); + ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel); + if (ret != 0) + return ret; - wpa_printf(MSG_DEBUG, "%s: npn=0x%x", __func__, *next_pn); + ret += nss_macsec_secy_tx_sa_next_pn_get(drv->secy_id, channel, sa->an, + &sa->next_pn); + + wpa_printf(MSG_DEBUG, "%s: npn=0x%x", __func__, sa->next_pn); return ret; } -int macsec_qca_set_transmit_next_pn(void *priv, u32 channel, u8 an, u32 next_pn) +static int macsec_qca_set_transmit_next_pn(void *priv, struct transmit_sa *sa) { struct macsec_qca_data *drv = priv; int ret = 0; + u32 channel; + + ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel); + if (ret != 0) + return ret; - ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, an, - next_pn); + ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an, + sa->next_pn); - wpa_printf(MSG_INFO, "%s: npn=0x%x", __func__, next_pn); + wpa_printf(MSG_INFO, "%s: npn=0x%x", __func__, sa->next_pn); return ret; } @@ -598,8 +429,7 @@ static int macsec_qca_get_available_receive_sc(void *priv, u32 *channel) } -static int macsec_qca_create_receive_sc(void *priv, u32 channel, - const u8 *sci_addr, u16 sci_port, +static int macsec_qca_create_receive_sc(void *priv, struct receive_sc *sc, unsigned int conf_offset, int validation) { @@ -608,6 +438,13 @@ static int macsec_qca_create_receive_sc(void *priv, u32 channel, fal_rx_prc_lut_t entry; fal_rx_sc_validate_frame_e vf; enum validate_frames validate_frames = validation; + u32 channel; + const u8 *sci_addr = sc->sci.addr; + u16 sci_port = be_to_host16(sc->sci.port); + + ret = macsec_qca_get_available_receive_sc(priv, &channel); + if (ret != 0) + return ret; wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel); @@ -615,8 +452,8 @@ static int macsec_qca_create_receive_sc(void *priv, u32 channel, os_memset(&entry, 0, sizeof(entry)); os_memcpy(entry.sci, sci_addr, ETH_ALEN); - entry.sci[6] = (sci_port >> 8) & 0xf; - entry.sci[7] = sci_port & 0xf; + entry.sci[6] = (sci_port >> 8) & 0xff; + entry.sci[7] = sci_port & 0xff; entry.sci_mask = 0xf; entry.valid = 1; @@ -642,15 +479,22 @@ static int macsec_qca_create_receive_sc(void *priv, u32 channel, channel, drv->replay_window); + macsec_qca_register_receive_channel(drv, sc, channel); + return ret; } -static int macsec_qca_delete_receive_sc(void *priv, u32 channel) +static int macsec_qca_delete_receive_sc(void *priv, struct receive_sc *sc) { struct macsec_qca_data *drv = priv; - int ret = 0; + int ret; fal_rx_prc_lut_t entry; + u32 channel; + + ret = macsec_qca_lookup_receive_channel(priv, sc, &channel); + if (ret != 0) + return ret; wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel); @@ -664,49 +508,91 @@ static int macsec_qca_delete_receive_sc(void *priv, u32 channel) } -static int macsec_qca_create_receive_sa(void *priv, u32 channel, u8 an, - u32 lowest_pn, const u8 *sak) +static int macsec_qca_create_receive_sa(void *priv, struct receive_sa *sa) { struct macsec_qca_data *drv = priv; - int ret = 0; + int ret; fal_rx_sak_t rx_sak; int i = 0; + u32 channel; + fal_rx_prc_lut_t entry; + u32 offset; + + ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel); + if (ret != 0) + return ret; wpa_printf(MSG_DEBUG, "%s, channel=%d, an=%d, lpn=0x%x", - __func__, channel, an, lowest_pn); + __func__, channel, sa->an, sa->lowest_pn); os_memset(&rx_sak, 0, sizeof(rx_sak)); - for (i = 0; i < 16; i++) - rx_sak.sak[i] = sak[15 - i]; + rx_sak.sak_len = sa->pkey->key_len; + if (sa->pkey->key_len == SAK_128_LEN) { + for (i = 0; i < 16; i++) + rx_sak.sak[i] = sa->pkey->key[15 - i]; + } else if (sa->pkey->key_len == SAK_256_LEN) { + for (i = 0; i < 16; i++) { + rx_sak.sak1[i] = sa->pkey->key[15 - i]; + rx_sak.sak[i] = sa->pkey->key[31 - i]; + } + } else { + return -1; + } - ret += nss_macsec_secy_rx_sa_create(drv->secy_id, channel, an); - ret += nss_macsec_secy_rx_sak_set(drv->secy_id, channel, an, &rx_sak); + if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_0) + offset = 0; + else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_30) + offset = 30; + else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_50) + offset = 50; + else + return -1; + ret += nss_macsec_secy_rx_prc_lut_get(drv->secy_id, channel, &entry); + entry.offset = offset; + ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry); + ret += nss_macsec_secy_rx_sa_create(drv->secy_id, channel, sa->an); + ret += nss_macsec_secy_rx_sak_set(drv->secy_id, channel, sa->an, + &rx_sak); return ret; } -static int macsec_qca_enable_receive_sa(void *priv, u32 channel, u8 an) +static int macsec_qca_enable_receive_sa(void *priv, struct receive_sa *sa) { struct macsec_qca_data *drv = priv; - int ret = 0; + int ret; + u32 channel; + + ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel); + if (ret != 0) + return ret; - wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an); + wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, + sa->an); - ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, an, TRUE); + ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an, + TRUE); return ret; } -static int macsec_qca_disable_receive_sa(void *priv, u32 channel, u8 an) +static int macsec_qca_disable_receive_sa(void *priv, struct receive_sa *sa) { struct macsec_qca_data *drv = priv; - int ret = 0; + int ret; + u32 channel; + + ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel); + if (ret != 0) + return ret; - wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an); + wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, + sa->an); - ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, an, FALSE); + ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an, + FALSE); return ret; } @@ -715,14 +601,12 @@ static int macsec_qca_disable_receive_sa(void *priv, u32 channel, u8 an) static int macsec_qca_get_available_transmit_sc(void *priv, u32 *channel) { struct macsec_qca_data *drv = priv; - int ret = 0; u32 sc_ch = 0; bool in_use = FALSE; for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) { - ret = nss_macsec_secy_tx_sc_in_used_get(drv->secy_id, sc_ch, - &in_use); - if (ret) + if (nss_macsec_secy_tx_sc_in_used_get(drv->secy_id, sc_ch, + &in_use)) continue; if (!in_use) { @@ -739,14 +623,19 @@ static int macsec_qca_get_available_transmit_sc(void *priv, u32 *channel) } -static int macsec_qca_create_transmit_sc(void *priv, u32 channel, - const u8 *sci_addr, u16 sci_port, +static int macsec_qca_create_transmit_sc(void *priv, struct transmit_sc *sc, unsigned int conf_offset) { struct macsec_qca_data *drv = priv; - int ret = 0; + int ret; fal_tx_class_lut_t entry; u8 psci[ETH_ALEN + 2]; + u32 channel; + u16 sci_port = be_to_host16(sc->sci.port); + + ret = macsec_qca_get_available_transmit_sc(priv, &channel); + if (ret != 0) + return ret; wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel); @@ -757,9 +646,9 @@ static int macsec_qca_create_transmit_sc(void *priv, u32 channel, entry.action = FAL_TX_CLASS_ACTION_FORWARD; entry.channel = channel; - os_memcpy(psci, sci_addr, ETH_ALEN); - psci[6] = (sci_port >> 8) & 0xf; - psci[7] = sci_port & 0xf; + os_memcpy(psci, sc->sci.addr, ETH_ALEN); + psci[6] = (sci_port >> 8) & 0xff; + psci[7] = sci_port & 0xff; ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry); ret += nss_macsec_secy_tx_sc_create(drv->secy_id, channel, psci, 8); @@ -769,15 +658,22 @@ static int macsec_qca_create_transmit_sc(void *priv, u32 channel, channel, conf_offset); + macsec_qca_register_transmit_channel(drv, sc, channel); + return ret; } -static int macsec_qca_delete_transmit_sc(void *priv, u32 channel) +static int macsec_qca_delete_transmit_sc(void *priv, struct transmit_sc *sc) { struct macsec_qca_data *drv = priv; - int ret = 0; + int ret; fal_tx_class_lut_t entry; + u32 channel; + + ret = macsec_qca_lookup_transmit_channel(priv, sc, &channel); + if (ret != 0) + return ret; wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel); @@ -791,19 +687,23 @@ static int macsec_qca_delete_transmit_sc(void *priv, u32 channel) } -static int macsec_qca_create_transmit_sa(void *priv, u32 channel, u8 an, - u32 next_pn, Boolean confidentiality, - const u8 *sak) +static int macsec_qca_create_transmit_sa(void *priv, struct transmit_sa *sa) { struct macsec_qca_data *drv = priv; - int ret = 0; + int ret; u8 tci = 0; fal_tx_sak_t tx_sak; int i; + u32 channel; + u32 offset; + + ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel); + if (ret != 0) + return ret; wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d, next_pn=0x%x, confidentiality=%d", - __func__, channel, an, next_pn, confidentiality); + __func__, channel, sa->an, sa->next_pn, sa->confidentiality); if (drv->always_include_sci) tci |= TCI_SC; @@ -812,45 +712,81 @@ static int macsec_qca_create_transmit_sa(void *priv, u32 channel, u8 an, else if (drv->use_scb) tci |= TCI_SCB; - if (confidentiality) + if (sa->confidentiality) tci |= TCI_E | TCI_C; os_memset(&tx_sak, 0, sizeof(tx_sak)); - for (i = 0; i < 16; i++) - tx_sak.sak[i] = sak[15 - i]; + tx_sak.sak_len = sa->pkey->key_len; + if (sa->pkey->key_len == SAK_128_LEN) { + for (i = 0; i < 16; i++) + tx_sak.sak[i] = sa->pkey->key[15 - i]; + } else if (sa->pkey->key_len == SAK_256_LEN) { + for (i = 0; i < 16; i++) { + tx_sak.sak1[i] = sa->pkey->key[15 - i]; + tx_sak.sak[i] = sa->pkey->key[31 - i]; + } + } else { + return -1; + } - ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, an, - next_pn); - ret += nss_macsec_secy_tx_sak_set(drv->secy_id, channel, an, &tx_sak); + if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_0) + offset = 0; + else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_30) + offset = 30; + else if (sa->pkey->confidentiality_offset == CONFIDENTIALITY_OFFSET_50) + offset = 50; + else + return -1; + ret += nss_macsec_secy_tx_sc_confidentiality_offset_set(drv->secy_id, + channel, + offset); + ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an, + sa->next_pn); + ret += nss_macsec_secy_tx_sak_set(drv->secy_id, channel, sa->an, + &tx_sak); ret += nss_macsec_secy_tx_sc_tci_7_2_set(drv->secy_id, channel, (tci >> 2)); - ret += nss_macsec_secy_tx_sc_an_set(drv->secy_id, channel, an); + ret += nss_macsec_secy_tx_sc_an_set(drv->secy_id, channel, sa->an); return ret; } -static int macsec_qca_enable_transmit_sa(void *priv, u32 channel, u8 an) +static int macsec_qca_enable_transmit_sa(void *priv, struct transmit_sa *sa) { struct macsec_qca_data *drv = priv; - int ret = 0; + int ret; + u32 channel; - wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an); + ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel); + if (ret != 0) + return ret; - ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, an, TRUE); + wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, + sa->an); + + ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an, + TRUE); return ret; } -static int macsec_qca_disable_transmit_sa(void *priv, u32 channel, u8 an) +static int macsec_qca_disable_transmit_sa(void *priv, struct transmit_sa *sa) { struct macsec_qca_data *drv = priv; - int ret = 0; + int ret; + u32 channel; + + ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel); + if (ret != 0) + return ret; - wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an); + wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, + sa->an); - ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, an, FALSE); + ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an, + FALSE); return ret; } @@ -859,14 +795,15 @@ static int macsec_qca_disable_transmit_sa(void *priv, u32 channel, u8 an) const struct wpa_driver_ops wpa_driver_macsec_qca_ops = { .name = "macsec_qca", .desc = "QCA MACsec Ethernet driver", - .get_ssid = macsec_qca_get_ssid, - .get_bssid = macsec_qca_get_bssid, - .get_capa = macsec_qca_get_capa, + .get_ssid = driver_wired_get_ssid, + .get_bssid = driver_wired_get_bssid, + .get_capa = driver_wired_get_capa, .init = macsec_qca_init, .deinit = macsec_qca_deinit, .macsec_init = macsec_qca_macsec_init, .macsec_deinit = macsec_qca_macsec_deinit, + .macsec_get_capability = macsec_qca_get_capability, .enable_protect_frames = macsec_qca_enable_protect_frames, .set_replay_protect = macsec_qca_set_replay_protect, .set_current_cipher_suite = macsec_qca_set_current_cipher_suite, @@ -874,13 +811,11 @@ const struct wpa_driver_ops wpa_driver_macsec_qca_ops = { .get_receive_lowest_pn = macsec_qca_get_receive_lowest_pn, .get_transmit_next_pn = macsec_qca_get_transmit_next_pn, .set_transmit_next_pn = macsec_qca_set_transmit_next_pn, - .get_available_receive_sc = macsec_qca_get_available_receive_sc, .create_receive_sc = macsec_qca_create_receive_sc, .delete_receive_sc = macsec_qca_delete_receive_sc, .create_receive_sa = macsec_qca_create_receive_sa, .enable_receive_sa = macsec_qca_enable_receive_sa, .disable_receive_sa = macsec_qca_disable_receive_sa, - .get_available_transmit_sc = macsec_qca_get_available_transmit_sc, .create_transmit_sc = macsec_qca_create_transmit_sc, .delete_transmit_sc = macsec_qca_delete_transmit_sc, .create_transmit_sa = macsec_qca_create_transmit_sa, diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c index 9440f0127235a..614c4521e6ff4 100644 --- a/src/drivers/driver_ndis.c +++ b/src/drivers/driver_ndis.c @@ -1220,12 +1220,16 @@ static int wpa_driver_ndis_set_pmkid(struct wpa_driver_ndis_data *drv) } -static int wpa_driver_ndis_add_pmkid(void *priv, const u8 *bssid, - const u8 *pmkid) +static int wpa_driver_ndis_add_pmkid(void *priv, + struct wpa_pmkid_params *params) { struct wpa_driver_ndis_data *drv = priv; struct ndis_pmkid_entry *entry, *prev; + const u8 *bssid = params->bssid; + const u8 *pmkid = params->pmkid; + if (!bssid || !pmkid) + return -1; if (drv->no_of_pmkid == 0) return 0; @@ -1261,12 +1265,16 @@ static int wpa_driver_ndis_add_pmkid(void *priv, const u8 *bssid, } -static int wpa_driver_ndis_remove_pmkid(void *priv, const u8 *bssid, - const u8 *pmkid) +static int wpa_driver_ndis_remove_pmkid(void *priv, + struct wpa_pmkid_params *params) { struct wpa_driver_ndis_data *drv = priv; struct ndis_pmkid_entry *entry, *prev; + const u8 *bssid = params->bssid; + const u8 *pmkid = params->pmkid; + if (!bssid || !pmkid) + return -1; if (drv->no_of_pmkid == 0) return 0; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 1210d43560877..871a5d0b4a205 100644 --- a/src/drivers/driver_nl80211.c +++ b/src/drivers/driver_nl80211.c @@ -29,6 +29,7 @@ #include "common/qca-vendor-attr.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "common/wpa_common.h" #include "l2_packet/l2_packet.h" #include "netlink.h" #include "linux_defines.h" @@ -39,6 +40,29 @@ #include "driver_nl80211.h" +/* support for extack if compilation headers are too old */ +#ifndef NETLINK_EXT_ACK +#define NETLINK_EXT_ACK 11 +enum nlmsgerr_attrs { + NLMSGERR_ATTR_UNUSED, + NLMSGERR_ATTR_MSG, + NLMSGERR_ATTR_OFFS, + NLMSGERR_ATTR_COOKIE, + + __NLMSGERR_ATTR_MAX, + NLMSGERR_ATTR_MAX = __NLMSGERR_ATTR_MAX - 1 +}; +#endif +#ifndef NLM_F_CAPPED +#define NLM_F_CAPPED 0x100 +#endif +#ifndef NLM_F_ACK_TLVS +#define NLM_F_ACK_TLVS 0x200 +#endif +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + #ifndef CONFIG_LIBNL20 /* * libnl 1.1 has a bug, it tries to allocate socket numbers densely @@ -129,7 +153,7 @@ static void nl_destroy_handles(struct nl_handle **handle) static void nl80211_register_eloop_read(struct nl_handle **handle, eloop_sock_handler handler, - void *eloop_data) + void *eloop_data, int persist) { #ifdef CONFIG_LIBNL20 /* @@ -150,13 +174,17 @@ static void nl80211_register_eloop_read(struct nl_handle **handle, nl_socket_set_nonblocking(*handle); eloop_register_read_sock(nl_socket_get_fd(*handle), handler, eloop_data, *handle); - *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID); + if (!persist) + *handle = (void *) (((intptr_t) *handle) ^ + ELOOP_SOCKET_INVALID); } -static void nl80211_destroy_eloop_handle(struct nl_handle **handle) +static void nl80211_destroy_eloop_handle(struct nl_handle **handle, int persist) { - *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID); + if (!persist) + *handle = (void *) (((intptr_t) *handle) ^ + ELOOP_SOCKET_INVALID); eloop_unregister_read_sock(nl_socket_get_fd(*handle)); nl_destroy_handles(handle); } @@ -204,6 +232,8 @@ static int nl80211_set_param(void *priv, const char *param); static int nl80211_put_mesh_config(struct nl_msg *msg, struct wpa_driver_mesh_bss_params *params); #endif /* CONFIG_MESH */ +static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, + int reason); /* Converts nl80211_chan_width to a common format */ @@ -295,8 +325,35 @@ static int finish_handler(struct nl_msg *msg, void *arg) static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) { + struct nlmsghdr *nlh = (struct nlmsghdr *) err - 1; + int len = nlh->nlmsg_len; + struct nlattr *attrs; + struct nlattr *tb[NLMSGERR_ATTR_MAX + 1]; int *ret = arg; + int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh); + *ret = err->error; + + if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS)) + return NL_SKIP; + + if (!(nlh->nlmsg_flags & NLM_F_CAPPED)) + ack_len += err->msg.nlmsg_len - sizeof(*nlh); + + if (len <= ack_len) + return NL_STOP; + + attrs = (void *) ((unsigned char *) nlh + ack_len); + len -= ack_len; + + nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, len, NULL); + if (tb[NLMSGERR_ATTR_MSG]) { + len = strnlen((char *) nla_data(tb[NLMSGERR_ATTR_MSG]), + nla_len(tb[NLMSGERR_ATTR_MSG])); + wpa_printf(MSG_ERROR, "nl80211: kernel reports: %*s", + len, (char *) nla_data(tb[NLMSGERR_ATTR_MSG])); + } + return NL_SKIP; } @@ -335,7 +392,7 @@ static int send_and_recv(struct nl80211_global *global, void *valid_data) { struct nl_cb *cb; - int err = -ENOMEM; + int err = -ENOMEM, opt; if (!msg) return -ENOMEM; @@ -344,6 +401,11 @@ static int send_and_recv(struct nl80211_global *global, if (!cb) goto out; + /* try to set NETLINK_EXT_ACK to 1, ignoring errors */ + opt = 1; + setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK, + NETLINK_EXT_ACK, &opt, sizeof(opt)); + err = nl_send_auto_complete(nl_handle, msg); if (err < 0) goto out; @@ -673,6 +735,7 @@ nl80211_get_wiphy_data_ap(struct i802_bss *bss) struct nl80211_wiphy_data *w; int wiphy_idx, found = 0; struct i802_bss *tmp_bss; + u8 channel; if (bss->wiphy_data != NULL) return bss->wiphy_data; @@ -692,30 +755,36 @@ nl80211_get_wiphy_data_ap(struct i802_bss *bss) dl_list_init(&w->bsss); dl_list_init(&w->drvs); - w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); - if (!w->nl_cb) { - os_free(w); - return NULL; - } - nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); - nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, process_beacon_event, - w); + /* Beacon frames not supported in IEEE 802.11ad */ + if (ieee80211_freq_to_chan(bss->freq, &channel) != + HOSTAPD_MODE_IEEE80211AD) { + w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!w->nl_cb) { + os_free(w); + return NULL; + } + nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, + no_seq_check, NULL); + nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, + process_beacon_event, w); + + w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb, + "wiphy beacons"); + if (w->nl_beacons == NULL) { + os_free(w); + return NULL; + } - w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb, - "wiphy beacons"); - if (w->nl_beacons == NULL) { - os_free(w); - return NULL; - } + if (nl80211_register_beacons(bss->drv, w)) { + nl_destroy_handles(&w->nl_beacons); + os_free(w); + return NULL; + } - if (nl80211_register_beacons(bss->drv, w)) { - nl_destroy_handles(&w->nl_beacons); - os_free(w); - return NULL; + nl80211_register_eloop_read(&w->nl_beacons, + nl80211_recv_beacons, w, 0); } - nl80211_register_eloop_read(&w->nl_beacons, nl80211_recv_beacons, w); - dl_list_add(&nl80211_wiphys, &w->list); add: @@ -761,7 +830,8 @@ static void nl80211_put_wiphy_data_ap(struct i802_bss *bss) if (!dl_list_empty(&w->bsss)) return; - nl80211_destroy_eloop_handle(&w->nl_beacons); + if (w->nl_beacons) + nl80211_destroy_eloop_handle(&w->nl_beacons, 0); nl_cb_put(w->nl_cb); dl_list_del(&w->list); @@ -900,7 +970,8 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, nl80211_check_global(drv->global); wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed " "interface"); - wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL); + if (wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL) < 0) + return -1; return 1; } @@ -909,19 +980,58 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, static struct wpa_driver_nl80211_data * -nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len) +nl80211_find_drv(struct nl80211_global *global, int idx, u8 *buf, size_t len, + int *init_failed) { struct wpa_driver_nl80211_data *drv; + int res; + + if (init_failed) + *init_failed = 0; dl_list_for_each(drv, &global->interfaces, struct wpa_driver_nl80211_data, list) { - if (wpa_driver_nl80211_own_ifindex(drv, idx, buf, len) || - have_ifidx(drv, idx, IFIDX_ANY)) + res = wpa_driver_nl80211_own_ifindex(drv, idx, buf, len); + if (res < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Found matching own interface, but failed to complete reinitialization"); + if (init_failed) + *init_failed = 1; + return drv; + } + if (res > 0 || have_ifidx(drv, idx, IFIDX_ANY)) return drv; } return NULL; } +static void nl80211_refresh_mac(struct wpa_driver_nl80211_data *drv, + int ifindex, int notify) +{ + struct i802_bss *bss; + u8 addr[ETH_ALEN]; + + bss = get_bss_ifindex(drv, ifindex); + if (bss && + linux_get_ifhwaddr(drv->global->ioctl_sock, + bss->ifname, addr) < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: %s: failed to re-read MAC address", + bss->ifname); + } else if (bss && os_memcmp(addr, bss->addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Own MAC address on ifindex %d (%s) changed from " + MACSTR " to " MACSTR, + ifindex, bss->ifname, + MAC2STR(bss->addr), MAC2STR(addr)); + os_memcpy(bss->addr, addr, ETH_ALEN); + if (notify) + wpa_supplicant_event(drv->ctx, + EVENT_INTERFACE_MAC_CHANGED, NULL); + } +} + + static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, u8 *buf, size_t len) @@ -934,6 +1044,7 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, char namebuf[IFNAMSIZ]; char ifname[IFNAMSIZ + 1]; char extra[100], *pos, *end; + int init_failed; extra[0] = '\0'; pos = extra; @@ -978,9 +1089,11 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); - drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); + drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, &init_failed); if (!drv) goto event_newlink; + if (init_failed) + return; /* do not update interface state */ if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) { namebuf[0] = '\0'; @@ -989,6 +1102,8 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down " "event since interface %s is up", namebuf); drv->ignore_if_down_event = 0; + /* Re-read MAC address as it may have changed */ + nl80211_refresh_mac(drv, ifi->ifi_index, 1); return; } wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)", @@ -1012,18 +1127,27 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, * dynamic interfaces */ drv = nl80211_find_drv(global, ifi->ifi_index, - buf, len); + buf, len, NULL); if (!drv) return; } } if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) { + namebuf[0] = '\0'; if (if_indextoname(ifi->ifi_index, namebuf) && linux_iface_up(drv->global->ioctl_sock, namebuf) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " "event since interface %s is down", namebuf); + return; + } + wpa_printf(MSG_DEBUG, "nl80211: Interface up (%s/%s)", + namebuf, ifname); + if (os_strcmp(drv->first_bss->ifname, ifname) != 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Not the main interface (%s) - do not indicate interface up", + drv->first_bss->ifname); } else if (if_nametoindex(drv->first_bss->ifname) == 0) { wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up " "event since interface %s does not exist", @@ -1033,29 +1157,9 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx, "event since interface %s is marked " "removed", drv->first_bss->ifname); } else { - struct i802_bss *bss; - u8 addr[ETH_ALEN]; - /* Re-read MAC address as it may have changed */ - bss = get_bss_ifindex(drv, ifi->ifi_index); - if (bss && - linux_get_ifhwaddr(drv->global->ioctl_sock, - bss->ifname, addr) < 0) { - wpa_printf(MSG_DEBUG, - "nl80211: %s: failed to re-read MAC address", - bss->ifname); - } else if (bss && - os_memcmp(addr, bss->addr, ETH_ALEN) != 0) { - wpa_printf(MSG_DEBUG, - "nl80211: Own MAC address on ifindex %d (%s) changed from " - MACSTR " to " MACSTR, - ifi->ifi_index, bss->ifname, - MAC2STR(bss->addr), - MAC2STR(addr)); - os_memcpy(bss->addr, addr, ETH_ALEN); - } + nl80211_refresh_mac(drv, ifi->ifi_index, 0); - wpa_printf(MSG_DEBUG, "nl80211: Interface up"); drv->if_disabled = 0; wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL); @@ -1157,7 +1261,7 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); - drv = nl80211_find_drv(global, ifi->ifi_index, buf, len); + drv = nl80211_find_drv(global, ifi->ifi_index, buf, len, NULL); if (ifi->ifi_family == AF_BRIDGE && brid && drv) { /* device has been removed from bridge */ @@ -1181,16 +1285,108 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx, } +struct nl80211_get_assoc_freq_arg { + struct wpa_driver_nl80211_data *drv; + unsigned int assoc_freq; + unsigned int ibss_freq; + u8 assoc_bssid[ETH_ALEN]; + u8 assoc_ssid[SSID_MAX_LEN]; + u8 assoc_ssid_len; +}; + +static int nl80211_get_assoc_freq_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *bss[NL80211_BSS_MAX + 1]; + static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = { + [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC }, + [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC }, + [NL80211_BSS_STATUS] = { .type = NLA_U32 }, + }; + struct nl80211_get_assoc_freq_arg *ctx = arg; + enum nl80211_bss_status status; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[NL80211_ATTR_BSS] || + nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], + bss_policy) || + !bss[NL80211_BSS_STATUS]) + return NL_SKIP; + + status = nla_get_u32(bss[NL80211_BSS_STATUS]); + if (status == NL80211_BSS_STATUS_ASSOCIATED && + bss[NL80211_BSS_FREQUENCY]) { + ctx->assoc_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz", + ctx->assoc_freq); + } + if (status == NL80211_BSS_STATUS_IBSS_JOINED && + bss[NL80211_BSS_FREQUENCY]) { + ctx->ibss_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz", + ctx->ibss_freq); + } + if (status == NL80211_BSS_STATUS_ASSOCIATED && + bss[NL80211_BSS_BSSID]) { + os_memcpy(ctx->assoc_bssid, + nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN); + wpa_printf(MSG_DEBUG, "nl80211: Associated with " + MACSTR, MAC2STR(ctx->assoc_bssid)); + } + + if (status == NL80211_BSS_STATUS_ASSOCIATED && + bss[NL80211_BSS_INFORMATION_ELEMENTS]) { + const u8 *ie, *ssid; + size_t ie_len; + + ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]); + ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); + ssid = get_ie(ie, ie_len, WLAN_EID_SSID); + if (ssid && ssid[1] > 0 && ssid[1] <= SSID_MAX_LEN) { + ctx->assoc_ssid_len = ssid[1]; + os_memcpy(ctx->assoc_ssid, ssid + 2, ssid[1]); + } + } + + return NL_SKIP; +} + + +int nl80211_get_assoc_ssid(struct wpa_driver_nl80211_data *drv, u8 *ssid) +{ + struct nl_msg *msg; + int ret; + struct nl80211_get_assoc_freq_arg arg; + + msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN); + os_memset(&arg, 0, sizeof(arg)); + arg.drv = drv; + ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler, + &arg); + if (ret == 0) { + os_memcpy(ssid, arg.assoc_ssid, arg.assoc_ssid_len); + return arg.assoc_ssid_len; + } + wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d (%s)", + ret, strerror(-ret)); + return ret; +} + + unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv) { struct nl_msg *msg; int ret; - struct nl80211_bss_info_arg arg; + struct nl80211_get_assoc_freq_arg arg; msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN); os_memset(&arg, 0, sizeof(arg)); arg.drv = drv; - ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); + ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler, + &arg); if (ret == 0) { unsigned int freq = drv->nlmode == NL80211_IFTYPE_ADHOC ? arg.ibss_freq : arg.assoc_freq; @@ -1273,7 +1469,7 @@ int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv, { struct nl_msg *msg; - sig->current_signal = -9999; + sig->current_signal = -WPA_INVALID_NOISE; sig->current_txrate = 0; if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) || @@ -1335,7 +1531,7 @@ int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv, { struct nl_msg *msg; - sig_change->current_noise = 9999; + sig_change->current_noise = WPA_INVALID_NOISE; sig_change->frequency = drv->assoc_freq; msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); @@ -1505,7 +1701,7 @@ static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global) nl80211_register_eloop_read(&global->nl_event, wpa_driver_nl80211_event_receive, - global->nl_cb); + global->nl_cb, 0); return 0; @@ -1871,7 +2067,7 @@ static void nl80211_mgmt_handle_register_eloop(struct i802_bss *bss) { nl80211_register_eloop_read(&bss->nl_mgmt, wpa_driver_nl80211_event_receive, - bss->nl_cb); + bss->nl_cb, 0); } @@ -1884,6 +2080,25 @@ static int nl80211_register_action_frame(struct i802_bss *bss, } +static int nl80211_init_connect_handle(struct i802_bss *bss) +{ + if (bss->nl_connect) { + wpa_printf(MSG_DEBUG, + "nl80211: Connect handle already created (nl_connect=%p)", + bss->nl_connect); + return -1; + } + + bss->nl_connect = nl_create_handle(bss->nl_cb, "connect"); + if (!bss->nl_connect) + return -1; + nl80211_register_eloop_read(&bss->nl_connect, + wpa_driver_nl80211_event_receive, + bss->nl_cb, 1); + return 0; +} + + static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) { struct wpa_driver_nl80211_data *drv = bss->drv; @@ -1894,7 +2109,9 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP " "handle %p", bss->nl_mgmt); - if (drv->nlmode == NL80211_IFTYPE_ADHOC) { + if (drv->nlmode == NL80211_IFTYPE_ADHOC || + ((drv->capa.flags & WPA_DRIVER_FLAGS_SAE) && + !(drv->capa.flags & WPA_DRIVER_FLAGS_SME))) { u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4); /* register for any AUTH message */ @@ -1906,7 +2123,7 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) if (nl80211_register_action_frame(bss, (u8 *) "\x01\x04", 2) < 0) ret = -1; #endif /* CONFIG_INTERWORKING */ -#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) +#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING) || defined(CONFIG_DPP) /* GAS Initial Request */ if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0) ret = -1; @@ -1931,7 +2148,7 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) /* Protected GAS Comeback Response */ if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0d", 2) < 0) ret = -1; -#endif /* CONFIG_P2P || CONFIG_INTERWORKING */ +#endif /* CONFIG_P2P || CONFIG_INTERWORKING || CONFIG_DPP */ #ifdef CONFIG_P2P /* P2P Public Action */ if (nl80211_register_action_frame(bss, @@ -1944,6 +2161,13 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) 5) < 0) ret = -1; #endif /* CONFIG_P2P */ +#ifdef CONFIG_DPP + /* DPP Public Action */ + if (nl80211_register_action_frame(bss, + (u8 *) "\x04\x09\x50\x6f\x9a\x1a", + 6) < 0) + ret = -1; +#endif /* CONFIG_DPP */ #ifdef CONFIG_IEEE80211W /* SA Query Response */ if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0) @@ -1976,6 +2200,11 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss) /* WNM-Sleep Mode Response */ if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0) ret = -1; +#ifdef CONFIG_WNM + /* WNM - Collocated Interference Request */ + if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x0b", 2) < 0) + ret = -1; +#endif /* CONFIG_WNM */ #ifdef CONFIG_HS20 /* WNM-Notification */ @@ -2070,6 +2299,9 @@ static int nl80211_action_subscribe_ap(struct i802_bss *bss) /* RRM Measurement Report */ if (nl80211_register_action_frame(bss, (u8 *) "\x05\x01", 2) < 0) ret = -1; + /* RRM Link Measurement Report */ + if (nl80211_register_action_frame(bss, (u8 *) "\x05\x03", 2) < 0) + ret = -1; /* RRM Neighbor Report Request */ if (nl80211_register_action_frame(bss, (u8 *) "\x05\x04", 2) < 0) ret = -1; @@ -2141,9 +2373,6 @@ static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss) if (nl80211_register_spurious_class3(bss)) goto out_err; - if (nl80211_get_wiphy_data_ap(bss) == NULL) - goto out_err; - nl80211_mgmt_handle_register_eloop(bss); return 0; @@ -2178,7 +2407,7 @@ static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason) return; wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p " "(%s)", bss->nl_mgmt, reason); - nl80211_destroy_eloop_handle(&bss->nl_mgmt); + nl80211_destroy_eloop_handle(&bss->nl_mgmt, 0); nl80211_put_wiphy_data_ap(bss); } @@ -2394,16 +2623,20 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv, if (drv->vendor_cmd_test_avail) qca_vendor_test(drv); + nl80211_init_connect_handle(bss); + return 0; } -static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv) +static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss) { struct nl_msg *msg; + struct wpa_driver_nl80211_data *drv = bss->drv; wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)", drv->ifindex); + nl80211_put_wiphy_data_ap(bss); msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON); return send_and_recv_msgs(drv, msg, NULL, NULL); } @@ -2438,9 +2671,11 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) wpa_printf(MSG_INFO, "nl80211: Failed to remove " "interface %s from bridge %s: %s", bss->ifname, bss->brname, strerror(errno)); - if (drv->rtnl_sk) - nl80211_handle_destroy(drv->rtnl_sk); } + + if (drv->rtnl_sk) + nl80211_handle_destroy(drv->rtnl_sk); + if (bss->added_bridge) { if (linux_set_iface_flags(drv->global->ioctl_sock, bss->brname, 0) < 0) @@ -2456,7 +2691,7 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) nl80211_remove_monitor_interface(drv); if (is_ap_interface(drv->nlmode)) - wpa_driver_nl80211_del_beacon(drv); + wpa_driver_nl80211_del_beacon(bss); if (drv->eapol_sock >= 0) { eloop_unregister_read_sock(drv->eapol_sock); @@ -2505,6 +2740,9 @@ static void wpa_driver_nl80211_deinit(struct i802_bss *bss) nl80211_del_p2pdev(bss); } + if (bss->nl_connect) + nl80211_destroy_eloop_handle(&bss->nl_connect, 1); + nl80211_destroy_bss(drv->first_bss); os_free(drv->filter_ssids); @@ -2530,30 +2768,30 @@ static u32 wpa_alg_to_cipher_suite(enum wpa_alg alg, size_t key_len) switch (alg) { case WPA_ALG_WEP: if (key_len == 5) - return WLAN_CIPHER_SUITE_WEP40; - return WLAN_CIPHER_SUITE_WEP104; + return RSN_CIPHER_SUITE_WEP40; + return RSN_CIPHER_SUITE_WEP104; case WPA_ALG_TKIP: - return WLAN_CIPHER_SUITE_TKIP; + return RSN_CIPHER_SUITE_TKIP; case WPA_ALG_CCMP: - return WLAN_CIPHER_SUITE_CCMP; + return RSN_CIPHER_SUITE_CCMP; case WPA_ALG_GCMP: - return WLAN_CIPHER_SUITE_GCMP; + return RSN_CIPHER_SUITE_GCMP; case WPA_ALG_CCMP_256: - return WLAN_CIPHER_SUITE_CCMP_256; + return RSN_CIPHER_SUITE_CCMP_256; case WPA_ALG_GCMP_256: - return WLAN_CIPHER_SUITE_GCMP_256; + return RSN_CIPHER_SUITE_GCMP_256; case WPA_ALG_IGTK: - return WLAN_CIPHER_SUITE_AES_CMAC; + return RSN_CIPHER_SUITE_AES_128_CMAC; case WPA_ALG_BIP_GMAC_128: - return WLAN_CIPHER_SUITE_BIP_GMAC_128; + return RSN_CIPHER_SUITE_BIP_GMAC_128; case WPA_ALG_BIP_GMAC_256: - return WLAN_CIPHER_SUITE_BIP_GMAC_256; + return RSN_CIPHER_SUITE_BIP_GMAC_256; case WPA_ALG_BIP_CMAC_256: - return WLAN_CIPHER_SUITE_BIP_CMAC_256; + return RSN_CIPHER_SUITE_BIP_CMAC_256; case WPA_ALG_SMS4: - return WLAN_CIPHER_SUITE_SMS4; + return RSN_CIPHER_SUITE_SMS4; case WPA_ALG_KRK: - return WLAN_CIPHER_SUITE_KRK; + return RSN_CIPHER_SUITE_KRK; case WPA_ALG_NONE: case WPA_ALG_PMK: wpa_printf(MSG_ERROR, "nl80211: Unexpected encryption algorithm %d", @@ -2571,21 +2809,21 @@ static u32 wpa_cipher_to_cipher_suite(unsigned int cipher) { switch (cipher) { case WPA_CIPHER_CCMP_256: - return WLAN_CIPHER_SUITE_CCMP_256; + return RSN_CIPHER_SUITE_CCMP_256; case WPA_CIPHER_GCMP_256: - return WLAN_CIPHER_SUITE_GCMP_256; + return RSN_CIPHER_SUITE_GCMP_256; case WPA_CIPHER_CCMP: - return WLAN_CIPHER_SUITE_CCMP; + return RSN_CIPHER_SUITE_CCMP; case WPA_CIPHER_GCMP: - return WLAN_CIPHER_SUITE_GCMP; + return RSN_CIPHER_SUITE_GCMP; case WPA_CIPHER_TKIP: - return WLAN_CIPHER_SUITE_TKIP; + return RSN_CIPHER_SUITE_TKIP; case WPA_CIPHER_WEP104: - return WLAN_CIPHER_SUITE_WEP104; + return RSN_CIPHER_SUITE_WEP104; case WPA_CIPHER_WEP40: - return WLAN_CIPHER_SUITE_WEP40; + return RSN_CIPHER_SUITE_WEP40; case WPA_CIPHER_GTK_NOT_USED: - return WLAN_CIPHER_SUITE_NO_GROUP_ADDR; + return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED; } return 0; @@ -2598,19 +2836,19 @@ static int wpa_cipher_to_cipher_suites(unsigned int ciphers, u32 suites[], int num_suites = 0; if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP_256) - suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP_256; + suites[num_suites++] = RSN_CIPHER_SUITE_CCMP_256; if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP_256) - suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP_256; + suites[num_suites++] = RSN_CIPHER_SUITE_GCMP_256; if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP) - suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP; + suites[num_suites++] = RSN_CIPHER_SUITE_CCMP; if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP) - suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP; + suites[num_suites++] = RSN_CIPHER_SUITE_GCMP; if (num_suites < max_suites && ciphers & WPA_CIPHER_TKIP) - suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP; + suites[num_suites++] = RSN_CIPHER_SUITE_TKIP; if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP104) - suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104; + suites[num_suites++] = RSN_CIPHER_SUITE_WEP104; if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP40) - suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40; + suites[num_suites++] = RSN_CIPHER_SUITE_WEP40; return num_suites; } @@ -2647,6 +2885,44 @@ static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv, #endif /* CONFIG_DRIVER_NL80211_QCA */ +static int nl80211_set_pmk(struct wpa_driver_nl80211_data *drv, + const u8 *key, size_t key_len, + const u8 *addr) +{ + struct nl_msg *msg = NULL; + int ret; + + /* + * If the authenticator address is not set, assume it is + * the current BSSID. + */ + if (!addr && drv->associated) + addr = drv->bssid; + else if (!addr) + return -1; + + wpa_printf(MSG_DEBUG, "nl80211: Set PMK to the driver for " MACSTR, + MAC2STR(addr)); + wpa_hexdump_key(MSG_DEBUG, "nl80211: PMK", key, key_len); + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_PMK); + if (!msg || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || + nla_put(msg, NL80211_ATTR_PMK, key_len, key)) { + nl80211_nlmsg_clear(msg); + nlmsg_free(msg); + return -ENOBUFS; + } + + ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1); + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Set PMK failed: ret=%d (%s)", + ret, strerror(-ret)); + } + + return ret; +} + + static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, @@ -2685,6 +2961,10 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss, } #endif /* CONFIG_DRIVER_NL80211_QCA */ + if (alg == WPA_ALG_PMK && + (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) + return nl80211_set_pmk(drv, key, key_len, addr); + if (alg == WPA_ALG_NONE) { msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY); if (!msg) @@ -2868,8 +3148,8 @@ static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params, params->wep_key[i]) || nla_put_u32(msg, NL80211_KEY_CIPHER, params->wep_key_len[i] == 5 ? - WLAN_CIPHER_SUITE_WEP40 : - WLAN_CIPHER_SUITE_WEP104) || + RSN_CIPHER_SUITE_WEP40 : + RSN_CIPHER_SUITE_WEP104) || nla_put_u8(msg, NL80211_KEY_IDX, i) || (i == params->wep_tx_keyidx && nla_put_flag(msg, NL80211_KEY_DEFAULT))) @@ -2885,7 +3165,8 @@ static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params, int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, const u8 *addr, int cmd, u16 reason_code, - int local_state_change) + int local_state_change, + struct nl_handle *nl_connect) { int ret; struct nl_msg *msg; @@ -2899,7 +3180,10 @@ int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, return -1; } - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (nl_connect) + ret = send_and_recv(drv->global, nl_connect, msg, NULL, NULL); + else + ret = send_and_recv_msgs(drv, msg, NULL, NULL); if (ret) { wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: MLME command failed: reason=%u ret=%d (%s)", @@ -2910,20 +3194,22 @@ int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv, - int reason_code) + int reason_code, + struct nl_handle *nl_connect) { int ret; + int drv_associated = drv->associated; wpa_printf(MSG_DEBUG, "%s(reason_code=%d)", __func__, reason_code); nl80211_mark_disconnected(drv); /* Disconnect command doesn't need BSSID - it uses cached value */ ret = wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT, - reason_code, 0); + reason_code, 0, nl_connect); /* * For locally generated disconnect, supplicant already generates a * DEAUTH event, so ignore the event from NL80211. */ - drv->ignore_next_local_disconnect = ret == 0; + drv->ignore_next_local_disconnect = drv_associated && (ret == 0); return ret; } @@ -2934,23 +3220,31 @@ static int wpa_driver_nl80211_deauthenticate(struct i802_bss *bss, { struct wpa_driver_nl80211_data *drv = bss->drv; int ret; + int drv_associated = drv->associated; if (drv->nlmode == NL80211_IFTYPE_ADHOC) { nl80211_mark_disconnected(drv); return nl80211_leave_ibss(drv, 1); } - if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) - return wpa_driver_nl80211_disconnect(drv, reason_code); + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { + struct nl_handle *nl_connect = NULL; + + if (bss->use_nl_connect) + nl_connect = bss->nl_connect; + return wpa_driver_nl80211_disconnect(drv, reason_code, + nl_connect); + } wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)", __func__, MAC2STR(addr), reason_code); nl80211_mark_disconnected(drv); ret = wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, - reason_code, 0); + reason_code, 0, NULL); /* * For locally generated deauthenticate, supplicant already generates a * DEAUTH event, so ignore the event from NL80211. */ - drv->ignore_next_local_deauth = ret == 0; + drv->ignore_next_local_deauth = drv_associated && (ret == 0); + return ret; } @@ -3020,6 +3314,27 @@ static void nl80211_unmask_11b_rates(struct i802_bss *bss) } +static enum nl80211_auth_type get_nl_auth_type(int wpa_auth_alg) +{ + if (wpa_auth_alg & WPA_AUTH_ALG_OPEN) + return NL80211_AUTHTYPE_OPEN_SYSTEM; + if (wpa_auth_alg & WPA_AUTH_ALG_SHARED) + return NL80211_AUTHTYPE_SHARED_KEY; + if (wpa_auth_alg & WPA_AUTH_ALG_LEAP) + return NL80211_AUTHTYPE_NETWORK_EAP; + if (wpa_auth_alg & WPA_AUTH_ALG_FT) + return NL80211_AUTHTYPE_FT; + if (wpa_auth_alg & WPA_AUTH_ALG_SAE) + return NL80211_AUTHTYPE_SAE; + if (wpa_auth_alg & WPA_AUTH_ALG_FILS) + return NL80211_AUTHTYPE_FILS_SK; + if (wpa_auth_alg & WPA_AUTH_ALG_FILS_SK_PFS) + return NL80211_AUTHTYPE_FILS_SK_PFS; + + return NL80211_AUTHTYPE_MAX; +} + + static int wpa_driver_nl80211_authenticate( struct i802_bss *bss, struct wpa_driver_auth_params *params) { @@ -3095,27 +3410,17 @@ retry: if (params->ie && nla_put(msg, NL80211_ATTR_IE, params->ie_len, params->ie)) goto fail; - if (params->sae_data) { - wpa_hexdump(MSG_DEBUG, " * SAE data", params->sae_data, - params->sae_data_len); - if (nla_put(msg, NL80211_ATTR_SAE_DATA, params->sae_data_len, - params->sae_data)) + if (params->auth_data) { + wpa_hexdump(MSG_DEBUG, " * auth_data", params->auth_data, + params->auth_data_len); + if (nla_put(msg, NL80211_ATTR_SAE_DATA, params->auth_data_len, + params->auth_data)) goto fail; } - if (params->auth_alg & WPA_AUTH_ALG_OPEN) - type = NL80211_AUTHTYPE_OPEN_SYSTEM; - else if (params->auth_alg & WPA_AUTH_ALG_SHARED) - type = NL80211_AUTHTYPE_SHARED_KEY; - else if (params->auth_alg & WPA_AUTH_ALG_LEAP) - type = NL80211_AUTHTYPE_NETWORK_EAP; - else if (params->auth_alg & WPA_AUTH_ALG_FT) - type = NL80211_AUTHTYPE_FT; - else if (params->auth_alg & WPA_AUTH_ALG_SAE) - type = NL80211_AUTHTYPE_SAE; - else - goto fail; + type = get_nl_auth_type(params->auth_alg); wpa_printf(MSG_DEBUG, " * Auth Type %d", type); - if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type)) + if (type == NL80211_AUTHTYPE_MAX || + nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type)) goto fail; if (params->local_state_change) { wpa_printf(MSG_DEBUG, " * Local state change only"); @@ -3127,11 +3432,11 @@ retry: msg = NULL; if (ret) { wpa_dbg(drv->ctx, MSG_DEBUG, - "nl80211: MLME command failed (auth): ret=%d (%s)", - ret, strerror(-ret)); + "nl80211: MLME command failed (auth): count=%d ret=%d (%s)", + count, ret, strerror(-ret)); count++; - if (ret == -EALREADY && count == 1 && params->bssid && - !params->local_state_change) { + if ((ret == -EALREADY || ret == -EEXIST) && count == 1 && + params->bssid && !params->local_state_change) { /* * mac80211 does not currently accept new * authentication if we are already authenticated. As a @@ -3515,6 +3820,147 @@ static int nl80211_set_mesh_config(void *priv, #endif /* CONFIG_MESH */ +static int nl80211_put_beacon_rate(struct nl_msg *msg, const u64 flags, + struct wpa_driver_ap_params *params) +{ + struct nlattr *bands, *band; + struct nl80211_txrate_vht vht_rate; + + if (!params->freq || + (params->beacon_rate == 0 && + params->rate_type == BEACON_RATE_LEGACY)) + return 0; + + bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES); + if (!bands) + return -1; + + switch (params->freq->mode) { + case HOSTAPD_MODE_IEEE80211B: + case HOSTAPD_MODE_IEEE80211G: + band = nla_nest_start(msg, NL80211_BAND_2GHZ); + break; + case HOSTAPD_MODE_IEEE80211A: + band = nla_nest_start(msg, NL80211_BAND_5GHZ); + break; + case HOSTAPD_MODE_IEEE80211AD: + band = nla_nest_start(msg, NL80211_BAND_60GHZ); + break; + default: + return 0; + } + + if (!band) + return -1; + + os_memset(&vht_rate, 0, sizeof(vht_rate)); + + switch (params->rate_type) { + case BEACON_RATE_LEGACY: + if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY)) { + wpa_printf(MSG_INFO, + "nl80211: Driver does not support setting Beacon frame rate (legacy)"); + return -1; + } + + if (nla_put_u8(msg, NL80211_TXRATE_LEGACY, + (u8) params->beacon_rate / 5) || + nla_put(msg, NL80211_TXRATE_HT, 0, NULL) || + (params->freq->vht_enabled && + nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate), + &vht_rate))) + return -1; + + wpa_printf(MSG_DEBUG, " * beacon_rate = legacy:%u (* 100 kbps)", + params->beacon_rate); + break; + case BEACON_RATE_HT: + if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_HT)) { + wpa_printf(MSG_INFO, + "nl80211: Driver does not support setting Beacon frame rate (HT)"); + return -1; + } + if (nla_put(msg, NL80211_TXRATE_LEGACY, 0, NULL) || + nla_put_u8(msg, NL80211_TXRATE_HT, params->beacon_rate) || + (params->freq->vht_enabled && + nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate), + &vht_rate))) + return -1; + wpa_printf(MSG_DEBUG, " * beacon_rate = HT-MCS %u", + params->beacon_rate); + break; + case BEACON_RATE_VHT: + if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_VHT)) { + wpa_printf(MSG_INFO, + "nl80211: Driver does not support setting Beacon frame rate (VHT)"); + return -1; + } + vht_rate.mcs[0] = BIT(params->beacon_rate); + if (nla_put(msg, NL80211_TXRATE_LEGACY, 0, NULL)) + return -1; + if (nla_put(msg, NL80211_TXRATE_HT, 0, NULL)) + return -1; + if (nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate), + &vht_rate)) + return -1; + wpa_printf(MSG_DEBUG, " * beacon_rate = VHT-MCS %u", + params->beacon_rate); + break; + } + + nla_nest_end(msg, band); + nla_nest_end(msg, bands); + + return 0; +} + + +static int nl80211_set_multicast_to_unicast(struct i802_bss *bss, + int multicast_to_unicast) +{ + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret; + + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_MULTICAST_TO_UNICAST); + if (!msg || + (multicast_to_unicast && + nla_put_flag(msg, NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED))) { + wpa_printf(MSG_ERROR, + "nl80211: Failed to build NL80211_CMD_SET_MULTICAST_TO_UNICAST msg for %s", + bss->ifname); + nlmsg_free(msg); + return -ENOBUFS; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + + switch (ret) { + case 0: + wpa_printf(MSG_DEBUG, + "nl80211: multicast to unicast %s on interface %s", + multicast_to_unicast ? "enabled" : "disabled", + bss->ifname); + break; + case -EOPNOTSUPP: + if (!multicast_to_unicast) + break; + wpa_printf(MSG_INFO, + "nl80211: multicast to unicast not supported on interface %s", + bss->ifname); + break; + default: + wpa_printf(MSG_ERROR, + "nl80211: %s multicast to unicast failed with %d (%s) on interface %s", + multicast_to_unicast ? "enabling" : "disabling", + ret, strerror(-ret), bss->ifname); + break; + } + + return ret; +} + + static int wpa_driver_nl80211_set_ap(void *priv, struct wpa_driver_ap_params *params) { @@ -3538,6 +3984,9 @@ static int wpa_driver_nl80211_set_ap(void *priv, beacon_set); if (beacon_set) cmd = NL80211_CMD_SET_BEACON; + else if (!drv->device_ap_sme && !drv->use_monitor && + !nl80211_get_wiphy_data_ap(bss)) + return -ENOBUFS; wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head", params->head, params->head_len); @@ -3545,6 +3994,8 @@ static int wpa_driver_nl80211_set_ap(void *priv, params->tail, params->tail_len); wpa_printf(MSG_DEBUG, "nl80211: ifindex=%d", bss->ifindex); wpa_printf(MSG_DEBUG, "nl80211: beacon_int=%d", params->beacon_int); + wpa_printf(MSG_DEBUG, "nl80211: beacon_rate=%u", params->beacon_rate); + wpa_printf(MSG_DEBUG, "nl80211: rate_type=%d", params->rate_type); wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period); wpa_hexdump_ascii(MSG_DEBUG, "nl80211: ssid", params->ssid, params->ssid_len); @@ -3554,6 +4005,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, nla_put(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len, params->tail) || nl80211_put_beacon_int(msg, params->beacon_int) || + nl80211_put_beacon_rate(msg, drv->capa.flags, params) || nl80211_put_dtim_period(msg, params->dtim_period) || nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) goto fail; @@ -3616,9 +4068,9 @@ static int wpa_driver_nl80211_set_ap(void *priv, params->key_mgmt_suites); num_suites = 0; if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X) - suites[num_suites++] = WLAN_AKM_SUITE_8021X; + suites[num_suites++] = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; if (params->key_mgmt_suites & WPA_KEY_MGMT_PSK) - suites[num_suites++] = WLAN_AKM_SUITE_PSK; + suites[num_suites++] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; if (num_suites && nla_put(msg, NL80211_ATTR_AKM_SUITES, num_suites * sizeof(u32), suites)) @@ -3664,7 +4116,7 @@ static int wpa_driver_nl80211_set_ap(void *priv, smps_mode = NL80211_SMPS_OFF; break; } - if (nla_put_u32(msg, NL80211_ATTR_SMPS_MODE, smps_mode)) + if (nla_put_u8(msg, NL80211_ATTR_SMPS_MODE, smps_mode)) goto fail; } @@ -3731,6 +4183,8 @@ static int wpa_driver_nl80211_set_ap(void *priv, nl80211_set_bss(bss, params->cts_protect, params->preamble, params->short_slot_time, params->ht_opmode, params->isolate, params->basic_rates); + nl80211_set_multicast_to_unicast(bss, + params->multicast_to_unicast); if (beacon_set && params->freq && params->freq->bandwidth != bss->bandwidth) { wpa_printf(MSG_DEBUG, @@ -4464,6 +4918,7 @@ static void nl80211_teardown_ap(struct i802_bss *bss) else nl80211_mgmt_unsubscribe(bss, "AP teardown"); + nl80211_put_wiphy_data_ap(bss); bss->beacon_set = 0; } @@ -4808,10 +5263,54 @@ fail: } +static int nl80211_put_fils_connect_params(struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params, + struct nl_msg *msg) +{ + if (params->fils_erp_username_len) { + wpa_hexdump_ascii(MSG_DEBUG, " * FILS ERP EMSKname/username", + params->fils_erp_username, + params->fils_erp_username_len); + if (nla_put(msg, NL80211_ATTR_FILS_ERP_USERNAME, + params->fils_erp_username_len, + params->fils_erp_username)) + return -1; + } + + if (params->fils_erp_realm_len) { + wpa_hexdump_ascii(MSG_DEBUG, " * FILS ERP Realm", + params->fils_erp_realm, + params->fils_erp_realm_len); + if (nla_put(msg, NL80211_ATTR_FILS_ERP_REALM, + params->fils_erp_realm_len, params->fils_erp_realm)) + return -1; + } + + wpa_printf(MSG_DEBUG, " * FILS ERP next seq %u", + params->fils_erp_next_seq_num); + if (nla_put_u16(msg, NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, + params->fils_erp_next_seq_num)) + return -1; + + if (params->fils_erp_rrk_len) { + wpa_printf(MSG_DEBUG, " * FILS ERP rRK (len=%lu)", + (unsigned long) params->fils_erp_rrk_len); + if (nla_put(msg, NL80211_ATTR_FILS_ERP_RRK, + params->fils_erp_rrk_len, params->fils_erp_rrk)) + return -1; + } + + return 0; +} + + static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, struct wpa_driver_associate_params *params, struct nl_msg *msg) { + if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER)) + return -1; + if (params->bssid) { wpa_printf(MSG_DEBUG, " * bssid=" MACSTR, MAC2STR(params->bssid)); @@ -4912,40 +5411,64 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 || params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 || params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B || - params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { - int mgmt = WLAN_AKM_SUITE_PSK; + params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 || + params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA256 || + params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA384 || + params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA256 || + params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA384 || + params->key_mgmt_suite == WPA_KEY_MGMT_OWE || + params->key_mgmt_suite == WPA_KEY_MGMT_DPP) { + int mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; switch (params->key_mgmt_suite) { case WPA_KEY_MGMT_CCKM: - mgmt = WLAN_AKM_SUITE_CCKM; + mgmt = RSN_AUTH_KEY_MGMT_CCKM; break; case WPA_KEY_MGMT_IEEE8021X: - mgmt = WLAN_AKM_SUITE_8021X; + mgmt = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; break; case WPA_KEY_MGMT_FT_IEEE8021X: - mgmt = WLAN_AKM_SUITE_FT_8021X; + mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X; break; case WPA_KEY_MGMT_FT_PSK: - mgmt = WLAN_AKM_SUITE_FT_PSK; + mgmt = RSN_AUTH_KEY_MGMT_FT_PSK; break; case WPA_KEY_MGMT_IEEE8021X_SHA256: - mgmt = WLAN_AKM_SUITE_8021X_SHA256; + mgmt = RSN_AUTH_KEY_MGMT_802_1X_SHA256; break; case WPA_KEY_MGMT_PSK_SHA256: - mgmt = WLAN_AKM_SUITE_PSK_SHA256; + mgmt = RSN_AUTH_KEY_MGMT_PSK_SHA256; break; case WPA_KEY_MGMT_OSEN: - mgmt = WLAN_AKM_SUITE_OSEN; + mgmt = RSN_AUTH_KEY_MGMT_OSEN; break; case WPA_KEY_MGMT_IEEE8021X_SUITE_B: - mgmt = WLAN_AKM_SUITE_8021X_SUITE_B; + mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; break; case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: - mgmt = WLAN_AKM_SUITE_8021X_SUITE_B_192; + mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; + break; + case WPA_KEY_MGMT_FILS_SHA256: + mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA256; + break; + case WPA_KEY_MGMT_FILS_SHA384: + mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA384; + break; + case WPA_KEY_MGMT_FT_FILS_SHA256: + mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256; + break; + case WPA_KEY_MGMT_FT_FILS_SHA384: + mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384; + break; + case WPA_KEY_MGMT_OWE: + mgmt = RSN_AUTH_KEY_MGMT_OWE; + break; + case WPA_KEY_MGMT_DPP: + mgmt = RSN_AUTH_KEY_MGMT_DPP; break; case WPA_KEY_MGMT_PSK: default: - mgmt = WLAN_AKM_SUITE_PSK; + mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; break; } wpa_printf(MSG_DEBUG, " * akm=0x%x", mgmt); @@ -4953,6 +5476,14 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, return -1; } + /* Add PSK in case of 4-way handshake offload */ + if (params->psk && + (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) { + wpa_hexdump_key(MSG_DEBUG, " * PSK", params->psk, 32); + if (nla_put(msg, NL80211_ATTR_PMK, 32, params->psk)) + return -1; + } + if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT)) return -1; @@ -4964,10 +5495,6 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))) return -1; - if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED && - nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED)) - return -1; - if (params->rrm_used) { u32 drv_rrm_flags = drv->capa.rrm_flags; if ((!((drv_rrm_flags & @@ -5000,13 +5527,23 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv, drv->connect_reassoc = 1; } + if ((params->auth_alg & WPA_AUTH_ALG_FILS) && + nl80211_put_fils_connect_params(drv, params, msg) != 0) + return -1; + + if ((params->auth_alg & WPA_AUTH_ALG_SAE) && + (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) && + nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT)) + return -1; + return 0; } static int wpa_driver_nl80211_try_connect( struct wpa_driver_nl80211_data *drv, - struct wpa_driver_associate_params *params) + struct wpa_driver_associate_params *params, + struct nl_handle *nl_connect) { struct nl_msg *msg; enum nl80211_auth_type type; @@ -5034,6 +5571,15 @@ static int wpa_driver_nl80211_try_connect( if (ret) goto fail; + if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED && + nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED)) + goto fail; + + if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_OPTIONAL && + (drv->capa.flags & WPA_DRIVER_FLAGS_MFP_OPTIONAL) && + nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_OPTIONAL)) + goto fail; + algs = 0; if (params->auth_alg & WPA_AUTH_ALG_OPEN) algs++; @@ -5041,25 +5587,20 @@ static int wpa_driver_nl80211_try_connect( algs++; if (params->auth_alg & WPA_AUTH_ALG_LEAP) algs++; + if (params->auth_alg & WPA_AUTH_ALG_FILS) + algs++; + if (params->auth_alg & WPA_AUTH_ALG_FT) + algs++; if (algs > 1) { wpa_printf(MSG_DEBUG, " * Leave out Auth Type for automatic " "selection"); goto skip_auth_type; } - if (params->auth_alg & WPA_AUTH_ALG_OPEN) - type = NL80211_AUTHTYPE_OPEN_SYSTEM; - else if (params->auth_alg & WPA_AUTH_ALG_SHARED) - type = NL80211_AUTHTYPE_SHARED_KEY; - else if (params->auth_alg & WPA_AUTH_ALG_LEAP) - type = NL80211_AUTHTYPE_NETWORK_EAP; - else if (params->auth_alg & WPA_AUTH_ALG_FT) - type = NL80211_AUTHTYPE_FT; - else - goto fail; - + type = get_nl_auth_type(params->auth_alg); wpa_printf(MSG_DEBUG, " * Auth Type %d", type); - if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type)) + if (type == NL80211_AUTHTYPE_MAX || + nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type)) goto fail; skip_auth_type: @@ -5067,7 +5608,11 @@ skip_auth_type: if (ret) goto fail; - ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (nl_connect) + ret = send_and_recv(drv->global, nl_connect, msg, NULL, NULL); + else + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; if (ret) { wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d " @@ -5086,7 +5631,8 @@ fail: static int wpa_driver_nl80211_connect( struct wpa_driver_nl80211_data *drv, - struct wpa_driver_associate_params *params) + struct wpa_driver_associate_params *params, + struct nl_handle *nl_connect) { int ret; @@ -5096,7 +5642,7 @@ static int wpa_driver_nl80211_connect( else os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN); - ret = wpa_driver_nl80211_try_connect(drv, params); + ret = wpa_driver_nl80211_try_connect(drv, params, nl_connect); if (ret == -EALREADY) { /* * cfg80211 does not currently accept new connections if @@ -5107,9 +5653,9 @@ static int wpa_driver_nl80211_connect( "disconnecting before reassociation " "attempt"); if (wpa_driver_nl80211_disconnect( - drv, WLAN_REASON_PREV_AUTH_NOT_VALID)) + drv, WLAN_REASON_PREV_AUTH_NOT_VALID, nl_connect)) return -1; - ret = wpa_driver_nl80211_try_connect(drv, params); + ret = wpa_driver_nl80211_try_connect(drv, params, nl_connect); } return ret; } @@ -5134,10 +5680,18 @@ static int wpa_driver_nl80211_associate( if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) { enum nl80211_iftype nlmode = params->p2p ? NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION; + struct nl_handle *nl_connect = NULL; if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0) return -1; - return wpa_driver_nl80211_connect(drv, params); + if (params->auth_alg & WPA_AUTH_ALG_SAE) { + nl_connect = bss->nl_connect; + bss->use_nl_connect = 1; + } else { + bss->use_nl_connect = 0; + } + + return wpa_driver_nl80211_connect(drv, params, nl_connect); } nl80211_mark_disconnected(drv); @@ -5152,6 +5706,26 @@ static int wpa_driver_nl80211_associate( if (ret) goto fail; + if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED && + nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED)) + goto fail; + + if (params->fils_kek) { + wpa_printf(MSG_DEBUG, " * FILS KEK (len=%u)", + (unsigned int) params->fils_kek_len); + if (nla_put(msg, NL80211_ATTR_FILS_KEK, params->fils_kek_len, + params->fils_kek)) + goto fail; + } + if (params->fils_nonces) { + wpa_hexdump(MSG_DEBUG, " * FILS nonces (for AAD)", + params->fils_nonces, + params->fils_nonces_len); + if (nla_put(msg, NL80211_ATTR_FILS_NONCES, + params->fils_nonces_len, params->fils_nonces)) + goto fail; + } + ret = send_and_recv_msgs(drv, msg, NULL, NULL); msg = NULL; if (ret) { @@ -5568,6 +6142,17 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 }, [NL80211_STA_INFO_RX_BYTES64] = { .type = NLA_U64 }, [NL80211_STA_INFO_TX_BYTES64] = { .type = NLA_U64 }, + [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 }, + [NL80211_STA_INFO_ACK_SIGNAL] = { .type = NLA_U8 }, + }; + struct nlattr *rate[NL80211_RATE_INFO_MAX + 1]; + static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = { + [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 }, + [NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 }, + [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 }, + [NL80211_RATE_INFO_VHT_MCS] = { .type = NLA_U8 }, + [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG }, + [NL80211_RATE_INFO_VHT_NSS] = { .type = NLA_U8 }, }; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), @@ -5619,6 +6204,72 @@ static int get_sta_handler(struct nl_msg *msg, void *arg) if (stats[NL80211_STA_INFO_TX_FAILED]) data->tx_retry_failed = nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]); + if (stats[NL80211_STA_INFO_SIGNAL]) + data->signal = nla_get_u8(stats[NL80211_STA_INFO_SIGNAL]); + if (stats[NL80211_STA_INFO_ACK_SIGNAL]) { + data->last_ack_rssi = + nla_get_u8(stats[NL80211_STA_INFO_ACK_SIGNAL]); + data->flags |= STA_DRV_DATA_LAST_ACK_RSSI; + } + + if (stats[NL80211_STA_INFO_TX_BITRATE] && + nla_parse_nested(rate, NL80211_RATE_INFO_MAX, + stats[NL80211_STA_INFO_TX_BITRATE], + rate_policy) == 0) { + if (rate[NL80211_RATE_INFO_BITRATE32]) + data->current_tx_rate = + nla_get_u32(rate[NL80211_RATE_INFO_BITRATE32]); + else if (rate[NL80211_RATE_INFO_BITRATE]) + data->current_tx_rate = + nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]); + + if (rate[NL80211_RATE_INFO_MCS]) { + data->tx_mcs = nla_get_u8(rate[NL80211_RATE_INFO_MCS]); + data->flags |= STA_DRV_DATA_TX_MCS; + } + if (rate[NL80211_RATE_INFO_VHT_MCS]) { + data->tx_vhtmcs = + nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]); + data->flags |= STA_DRV_DATA_TX_VHT_MCS; + } + if (rate[NL80211_RATE_INFO_SHORT_GI]) + data->flags |= STA_DRV_DATA_TX_SHORT_GI; + if (rate[NL80211_RATE_INFO_VHT_NSS]) { + data->tx_vht_nss = + nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]); + data->flags |= STA_DRV_DATA_TX_VHT_NSS; + } + } + + if (stats[NL80211_STA_INFO_RX_BITRATE] && + nla_parse_nested(rate, NL80211_RATE_INFO_MAX, + stats[NL80211_STA_INFO_RX_BITRATE], + rate_policy) == 0) { + if (rate[NL80211_RATE_INFO_BITRATE32]) + data->current_rx_rate = + nla_get_u32(rate[NL80211_RATE_INFO_BITRATE32]); + else if (rate[NL80211_RATE_INFO_BITRATE]) + data->current_rx_rate = + nla_get_u16(rate[NL80211_RATE_INFO_BITRATE]); + + if (rate[NL80211_RATE_INFO_MCS]) { + data->rx_mcs = + nla_get_u8(rate[NL80211_RATE_INFO_MCS]); + data->flags |= STA_DRV_DATA_RX_MCS; + } + if (rate[NL80211_RATE_INFO_VHT_MCS]) { + data->rx_vhtmcs = + nla_get_u8(rate[NL80211_RATE_INFO_VHT_MCS]); + data->flags |= STA_DRV_DATA_RX_VHT_MCS; + } + if (rate[NL80211_RATE_INFO_SHORT_GI]) + data->flags |= STA_DRV_DATA_RX_SHORT_GI; + if (rate[NL80211_RATE_INFO_VHT_NSS]) { + data->rx_vht_nss = + nla_get_u8(rate[NL80211_RATE_INFO_VHT_NSS]); + data->flags |= STA_DRV_DATA_RX_VHT_NSS; + } + } return NL_SKIP; } @@ -5629,8 +6280,6 @@ static int i802_read_sta_data(struct i802_bss *bss, { struct nl_msg *msg; - os_memset(data, 0, sizeof(*data)); - if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_STATION)) || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) { nlmsg_free(msg); @@ -5648,6 +6297,7 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs, struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; struct nlattr *txq, *params; + int res; msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY); if (!msg) @@ -5693,7 +6343,11 @@ static int i802_set_tx_queue_params(void *priv, int queue, int aifs, nla_nest_end(msg, txq); - if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + res = send_and_recv_msgs(drv, msg, NULL, NULL); + wpa_printf(MSG_DEBUG, + "nl80211: TX queue param set: queue=%d aifs=%d cw_min=%d cw_max=%d burst_time=%d --> res=%d", + queue, aifs, cw_min, cw_max, burst_time, res); + if (res == 0) return 0; msg = NULL; fail: @@ -5736,6 +6390,7 @@ static int i802_get_inact_sec(void *priv, const u8 *addr) struct hostap_sta_driver_data data; int ret; + os_memset(&data, 0, sizeof(data)); data.inactive_msec = (unsigned long) -1; ret = i802_read_sta_data(priv, &data, addr); if (ret == -ENOENT) @@ -5761,6 +6416,14 @@ static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; struct ieee80211_mgmt mgmt; + u8 channel; + + if (ieee80211_freq_to_chan(bss->freq, &channel) == + HOSTAPD_MODE_IEEE80211AD) { + /* Deauthentication is not used in DMG/IEEE 802.11ad; + * disassociate the STA instead. */ + return i802_sta_disassoc(priv, own_addr, addr, reason); + } if (is_mesh_interface(drv->nlmode)) return -1; @@ -5942,8 +6605,16 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, struct i802_bss *bss = priv; struct wpa_driver_nl80211_data *drv = bss->drv; char name[IFNAMSIZ + 1]; + union wpa_event_data event; + int ret; + + ret = os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid); + if (ret >= (int) sizeof(name)) + wpa_printf(MSG_WARNING, + "nl80211: WDS interface name was truncated"); + else if (ret < 0) + return ret; - os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid); if (ifname_wds) os_strlcpy(ifname_wds, name, IFNAMSIZ + 1); @@ -5960,6 +6631,14 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, linux_br_add_if(drv->global->ioctl_sock, bridge_ifname, name) < 0) return -1; + + os_memset(&event, 0, sizeof(event)); + event.wds_sta_interface.sta_addr = addr; + event.wds_sta_interface.ifname = name; + event.wds_sta_interface.istatus = INTERFACE_ADDED; + wpa_supplicant_event(bss->ctx, + EVENT_WDS_STA_INTERFACE_STATUS, + &event); } if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) { wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA " @@ -5973,6 +6652,12 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val, i802_set_sta_vlan(priv, addr, bss->ifname, 0); nl80211_remove_iface(drv, if_nametoindex(name)); + os_memset(&event, 0, sizeof(event)); + event.wds_sta_interface.sta_addr = addr; + event.wds_sta_interface.ifname = name; + event.wds_sta_interface.istatus = INTERFACE_REMOVED; + wpa_supplicant_event(bss->ctx, EVENT_WDS_STA_INTERFACE_STATUS, + &event); return 0; } } @@ -6026,8 +6711,10 @@ static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, bss->br_ifindex = br_ifindex; if (linux_br_get(in_br, ifname) == 0) { - if (os_strcmp(in_br, brname) == 0) + if (os_strcmp(in_br, brname) == 0) { + bss->already_in_bridge = 1; return 0; /* already in the bridge */ + } wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from " "bridge %s", ifname, in_br); @@ -6036,7 +6723,7 @@ static int i802_check_bridge(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_ERROR, "nl80211: Failed to " "remove interface %s from bridge " "%s: %s", - ifname, brname, strerror(errno)); + ifname, in_br, strerror(errno)); return -1; } } @@ -6124,7 +6811,7 @@ static void *i802_init(struct hostapd_data *hapd, add_ifidx(drv, br_ifindex, drv->ifindex); #ifdef CONFIG_LIBNL3_ROUTE - if (bss->added_if_into_bridge) { + if (bss->added_if_into_bridge || bss->already_in_bridge) { drv->rtnl_sk = nl_socket_alloc(); if (drv->rtnl_sk == NULL) { wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock"); @@ -6474,7 +7161,7 @@ static int wpa_driver_nl80211_if_remove(struct i802_bss *bss, wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context"); nl80211_teardown_ap(bss); if (!bss->added_if && !drv->first_bss->next) - wpa_driver_nl80211_del_beacon(drv); + wpa_driver_nl80211_del_beacon(bss); nl80211_destroy_bss(bss); if (!bss->added_if) i802_set_iface_flags(bss, 0); @@ -6598,6 +7285,14 @@ static int wpa_driver_nl80211_send_action(struct i802_bss *bss, os_memcpy(hdr->addr2, src, ETH_ALEN); os_memcpy(hdr->addr3, bssid, ETH_ALEN); + if (os_memcmp(bss->addr, src, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "nl80211: Use random TA " MACSTR, + MAC2STR(src)); + os_memcpy(bss->rand_addr, src, ETH_ALEN); + } else { + os_memset(bss->rand_addr, 0, ETH_ALEN); + } + if (is_ap_interface(drv->nlmode) && (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) || (int) freq == bss->freq || drv->device_ap_sme || @@ -6745,7 +7440,7 @@ static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report) } else if (bss->nl_preq) { wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request " "reporting nl_preq=%p", bss->nl_preq); - nl80211_destroy_eloop_handle(&bss->nl_preq); + nl80211_destroy_eloop_handle(&bss->nl_preq, 0); } return 0; } @@ -6770,7 +7465,7 @@ static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report) nl80211_register_eloop_read(&bss->nl_preq, wpa_driver_nl80211_event_receive, - bss->nl_cb); + bss->nl_cb, 0); return 0; @@ -6836,7 +7531,7 @@ static int wpa_driver_nl80211_deinit_ap(void *priv) struct wpa_driver_nl80211_data *drv = bss->drv; if (!is_ap_interface(drv->nlmode)) return -1; - wpa_driver_nl80211_del_beacon(drv); + wpa_driver_nl80211_del_beacon(bss); bss->beacon_set = 0; /* @@ -6856,7 +7551,7 @@ static int wpa_driver_nl80211_stop_ap(void *priv) struct wpa_driver_nl80211_data *drv = bss->drv; if (!is_ap_interface(drv->nlmode)) return -1; - wpa_driver_nl80211_del_beacon(drv); + wpa_driver_nl80211_del_beacon(bss); bss->beacon_set = 0; return 0; } @@ -7085,7 +7780,7 @@ static void nl80211_global_deinit(void *priv) nl_destroy_handles(&global->nl); if (global->nl_event) - nl80211_destroy_eloop_handle(&global->nl_event); + nl80211_destroy_eloop_handle(&global->nl_event, 0); nl_cb_put(global->nl_cb); @@ -7104,14 +7799,24 @@ static const char * nl80211_get_radio_name(void *priv) } -static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid, - const u8 *pmkid) +static int nl80211_pmkid(struct i802_bss *bss, int cmd, + struct wpa_pmkid_params *params) { struct nl_msg *msg; + const size_t PMK_MAX_LEN = 48; /* current cfg80211 limit */ if (!(msg = nl80211_bss_msg(bss, 0, cmd)) || - (pmkid && nla_put(msg, NL80211_ATTR_PMKID, 16, pmkid)) || - (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))) { + (params->pmkid && + nla_put(msg, NL80211_ATTR_PMKID, 16, params->pmkid)) || + (params->bssid && + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid)) || + (params->ssid_len && + nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) || + (params->fils_cache_id && + nla_put(msg, NL80211_ATTR_FILS_CACHE_ID, 2, + params->fils_cache_id)) || + (params->pmk_len && params->pmk_len <= PMK_MAX_LEN && + nla_put(msg, NL80211_ATTR_PMK, params->pmk_len, params->pmk))) { nlmsg_free(msg); return -ENOBUFS; } @@ -7120,28 +7825,68 @@ static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid, } -static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) +static int nl80211_add_pmkid(void *priv, struct wpa_pmkid_params *params) { struct i802_bss *bss = priv; - wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid)); - return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid); + int ret; + + if (params->bssid) + wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, + MAC2STR(params->bssid)); + else if (params->fils_cache_id && params->ssid_len) { + wpa_printf(MSG_DEBUG, + "nl80211: Add PMKSA for cache id %02x%02x SSID %s", + params->fils_cache_id[0], params->fils_cache_id[1], + wpa_ssid_txt(params->ssid, params->ssid_len)); + } + + ret = nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, params); + if (ret < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: NL80211_CMD_SET_PMKSA failed: %d (%s)", + ret, strerror(-ret)); + } + + return ret; } -static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid) +static int nl80211_remove_pmkid(void *priv, struct wpa_pmkid_params *params) { struct i802_bss *bss = priv; - wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR, - MAC2STR(bssid)); - return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid); + int ret; + + if (params->bssid) + wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR, + MAC2STR(params->bssid)); + else if (params->fils_cache_id && params->ssid_len) { + wpa_printf(MSG_DEBUG, + "nl80211: Delete PMKSA for cache id %02x%02x SSID %s", + params->fils_cache_id[0], params->fils_cache_id[1], + wpa_ssid_txt(params->ssid, params->ssid_len)); + } + + ret = nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, params); + if (ret < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: NL80211_CMD_DEL_PMKSA failed: %d (%s)", + ret, strerror(-ret)); + } + + return ret; } static int nl80211_flush_pmkid(void *priv) { struct i802_bss *bss = priv; + struct nl_msg *msg; + wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs"); - return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL); + msg = nl80211_bss_msg(bss, 0, NL80211_CMD_FLUSH_PMKSA); + if (!msg) + return -ENOBUFS; + return send_and_recv_msgs(bss->drv, msg, NULL, NULL); } @@ -7336,7 +8081,7 @@ static void nl80211_set_rekey_info(void *priv, const u8 *kek, size_t kek_len, if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) || !(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) || nla_put(msg, NL80211_REKEY_DATA_KEK, kek_len, kek) || - nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck) || + (kck_len && nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck)) || nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN, replay_ctr)) { nl80211_nlmsg_clear(msg); @@ -7424,6 +8169,7 @@ static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr, static int nl80211_set_power_save(struct i802_bss *bss, int enabled) { struct nl_msg *msg; + int ret; if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_POWER_SAVE)) || nla_put_u32(msg, NL80211_ATTR_PS_STATE, @@ -7431,7 +8177,15 @@ static int nl80211_set_power_save(struct i802_bss *bss, int enabled) nlmsg_free(msg); return -ENOBUFS; } - return send_and_recv_msgs(bss->drv, msg, NULL, NULL); + + ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL); + if (ret < 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Setting PS state %s failed: %d (%s)", + enabled ? "enabled" : "disabled", + ret, strerror(-ret)); + } + return ret; } @@ -7545,6 +8299,7 @@ static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer) struct wpa_driver_nl80211_data *drv = bss->drv; struct nl_msg *msg; enum nl80211_tdls_operation nl80211_oper; + int res; if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) return -EOPNOTSUPP; @@ -7580,7 +8335,11 @@ static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer) return -ENOBUFS; } - return send_and_recv_msgs(drv, msg, NULL, NULL); + res = send_and_recv_msgs(drv, msg, NULL, NULL); + wpa_printf(MSG_DEBUG, "nl80211: TDLS_OPER: oper=%d mac=" MACSTR + " --> res=%d (%s)", nl80211_oper, MAC2STR(peer), res, + strerror(-res)); + return res; } @@ -7738,6 +8497,8 @@ static int driver_nl80211_read_sta_data(void *priv, const u8 *addr) { struct i802_bss *bss = priv; + + os_memset(data, 0, sizeof(*data)); return i802_read_sta_data(bss, data, addr); } @@ -7842,7 +8603,7 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) "brname=%s\n" "addr=" MACSTR "\n" "freq=%d\n" - "%s%s%s%s%s", + "%s%s%s%s%s%s", bss->ifindex, bss->ifname, bss->brname, @@ -7851,6 +8612,7 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen) bss->beacon_set ? "beacon_set=1\n" : "", bss->added_if_into_bridge ? "added_if_into_bridge=1\n" : "", + bss->already_in_bridge ? "already_in_bridge=1\n" : "", bss->added_bridge ? "added_bridge=1\n" : "", bss->in_deinit ? "in_deinit=1\n" : "", bss->if_dynamic ? "if_dynamic=1\n" : ""); @@ -8026,8 +8788,9 @@ static int nl80211_switch_channel(void *priv, struct csa_settings *settings) return -EOPNOTSUPP; } - if ((drv->nlmode != NL80211_IFTYPE_AP) && - (drv->nlmode != NL80211_IFTYPE_P2P_GO)) + if (drv->nlmode != NL80211_IFTYPE_AP && + drv->nlmode != NL80211_IFTYPE_P2P_GO && + drv->nlmode != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP; /* @@ -8388,6 +9151,95 @@ static int nl80211_roaming(void *priv, int allowed, const u8 *bssid) return send_and_recv_msgs(drv, msg, NULL, NULL); } + + +static int nl80211_disable_fils(void *priv, int disable) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *params; + + wpa_printf(MSG_DEBUG, "nl80211: Disable FILS=%d", disable); + + if (!drv->set_wifi_conf_vendor_cmd_avail) + return -1; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION) || + !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_DISABLE_FILS, + disable)) { + nlmsg_free(msg); + return -1; + } + nla_nest_end(msg, params); + + return send_and_recv_msgs(drv, msg, NULL, NULL); +} + + +/* Reserved QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID value for wpa_supplicant */ +#define WPA_SUPPLICANT_CLIENT_ID 1 + +static int nl80211_set_bssid_blacklist(void *priv, unsigned int num_bssid, + const u8 *bssid) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *params, *nlbssids, *attr; + unsigned int i; + + wpa_printf(MSG_DEBUG, "nl80211: Set blacklist BSSID (num=%u)", + num_bssid); + + if (!drv->roam_vendor_cmd_avail) + return -1; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_ROAM) || + !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD, + QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID) || + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID, + WPA_SUPPLICANT_CLIENT_ID) || + nla_put_u32(msg, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID, + num_bssid)) + goto fail; + + nlbssids = nla_nest_start( + msg, QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS); + if (!nlbssids) + goto fail; + + for (i = 0; i < num_bssid; i++) { + attr = nla_nest_start(msg, i); + if (!attr) + goto fail; + if (nla_put(msg, + QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID, + ETH_ALEN, &bssid[i * ETH_ALEN])) + goto fail; + wpa_printf(MSG_DEBUG, "nl80211: BSSID[%u]: " MACSTR, i, + MAC2STR(&bssid[i * ETH_ALEN])); + nla_nest_end(msg, attr); + } + nla_nest_end(msg, nlbssids); + nla_nest_end(msg, params); + + return send_and_recv_msgs(drv, msg, NULL, NULL); + +fail: + nlmsg_free(msg); + return -1; +} + #endif /* CONFIG_DRIVER_NL80211_QCA */ @@ -8470,11 +9322,14 @@ static int nl80211_put_mesh_config(struct nl_msg *msg, return -1; if (((params->flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS) && - nla_put_u32(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, - params->auto_plinks)) || + nla_put_u8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, + params->auto_plinks)) || ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS) && nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS, - params->max_peer_links))) + params->max_peer_links)) || + ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_RSSI_THRESHOLD) && + nla_put_u32(msg, NL80211_MESHCONF_RSSI_THRESHOLD, + params->rssi_threshold))) return -1; /* @@ -9307,6 +10162,304 @@ static int nl80211_p2p_lo_stop(void *priv) return send_and_recv_msgs(drv, msg, NULL, NULL); } + +static int nl80211_set_tdls_mode(void *priv, int tdls_external_control) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *params; + int ret; + u32 tdls_mode; + + wpa_printf(MSG_DEBUG, + "nl80211: Set TDKS mode: tdls_external_control=%d", + tdls_external_control); + + if (tdls_external_control == 1) + tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT | + QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL; + else + tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS)) + goto fail; + + params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!params) + goto fail; + + if (nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE, + tdls_mode)) + goto fail; + + nla_nest_end(msg, params); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_ERROR, + "nl80211: Set TDLS mode failed: ret=%d (%s)", + ret, strerror(-ret)); + goto fail; + } + return 0; +fail: + nlmsg_free(msg); + return -1; +} + + +#ifdef CONFIG_MBO + +static enum mbo_transition_reject_reason +nl80211_mbo_reject_reason_mapping(enum qca_wlan_btm_candidate_status status) +{ + switch (status) { + case QCA_STATUS_REJECT_EXCESSIVE_FRAME_LOSS_EXPECTED: + return MBO_TRANSITION_REJECT_REASON_FRAME_LOSS; + case QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED: + return MBO_TRANSITION_REJECT_REASON_DELAY; + case QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY: + return MBO_TRANSITION_REJECT_REASON_QOS_CAPACITY; + case QCA_STATUS_REJECT_LOW_RSSI: + return MBO_TRANSITION_REJECT_REASON_RSSI; + case QCA_STATUS_REJECT_HIGH_INTERFERENCE: + return MBO_TRANSITION_REJECT_REASON_INTERFERENCE; + case QCA_STATUS_REJECT_UNKNOWN: + default: + return MBO_TRANSITION_REJECT_REASON_UNSPECIFIED; + } +} + + +static void nl80211_parse_btm_candidate_info(struct candidate_list *candidate, + struct nlattr *tb[], int num) +{ + enum qca_wlan_btm_candidate_status status; + char buf[50]; + + os_memcpy(candidate->bssid, + nla_data(tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID]), + ETH_ALEN); + + status = nla_get_u32( + tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS]); + candidate->is_accept = status == QCA_STATUS_ACCEPT; + candidate->reject_reason = nl80211_mbo_reject_reason_mapping(status); + + if (candidate->is_accept) + os_snprintf(buf, sizeof(buf), "Accepted"); + else + os_snprintf(buf, sizeof(buf), + "Rejected, Reject_reason: %d", + candidate->reject_reason); + wpa_printf(MSG_DEBUG, "nl80211: BSSID[%d]: " MACSTR " %s", + num, MAC2STR(candidate->bssid), buf); +} + + +static int +nl80211_get_bss_transition_status_handler(struct nl_msg *msg, void *arg) +{ + struct wpa_bss_candidate_info *info = arg; + struct candidate_list *candidate = info->candidates; + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1]; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1]; + static struct nla_policy policy[ + QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] = { + .minlen = ETH_ALEN + }, + [QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS] = { + .type = NLA_U32, + }, + }; + struct nlattr *attr; + int rem; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + u8 num; + + num = info->num; /* number of candidates sent to driver */ + info->num = 0; + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb_msg[NL80211_ATTR_VENDOR_DATA] || + nla_parse_nested(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX, + tb_msg[NL80211_ATTR_VENDOR_DATA], NULL) || + !tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO]) + return NL_SKIP; + + wpa_printf(MSG_DEBUG, + "nl80211: WNM Candidate list received from driver"); + nla_for_each_nested(attr, + tb_vendor[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO], + rem) { + if (info->num >= num || + nla_parse_nested( + tb, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX, + attr, policy) || + !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID] || + !tb[QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS]) + break; + + nl80211_parse_btm_candidate_info(candidate, tb, info->num); + + candidate++; + info->num++; + } + + return NL_SKIP; +} + + +static struct wpa_bss_candidate_info * +nl80211_get_bss_transition_status(void *priv, struct wpa_bss_trans_info *params) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *attr, *attr1, *attr2; + struct wpa_bss_candidate_info *info; + u8 i; + int ret; + u8 *pos; + + if (!drv->fetch_bss_trans_status) + return NULL; + + info = os_zalloc(sizeof(*info)); + if (!info) + return NULL; + /* Allocate memory for number of candidates sent to driver */ + info->candidates = os_calloc(params->n_candidates, + sizeof(*info->candidates)); + if (!info->candidates) { + os_free(info); + return NULL; + } + + /* Copy the number of candidates being sent to driver. This is used in + * nl80211_get_bss_transition_status_handler() to limit the number of + * candidates that can be populated in info->candidates and will be + * later overwritten with the actual number of candidates received from + * the driver. + */ + info->num = params->n_candidates; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS)) + goto fail; + + attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!attr) + goto fail; + + if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON, + params->mbo_transition_reason)) + goto fail; + + attr1 = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO); + if (!attr1) + goto fail; + + wpa_printf(MSG_DEBUG, + "nl80211: WNM Candidate list info sending to driver: mbo_transition_reason: %d n_candidates: %d", + params->mbo_transition_reason, params->n_candidates); + pos = params->bssid; + for (i = 0; i < params->n_candidates; i++) { + wpa_printf(MSG_DEBUG, "nl80211: BSSID[%d]: " MACSTR, i, + MAC2STR(pos)); + attr2 = nla_nest_start(msg, i); + if (!attr2 || + nla_put(msg, QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID, + ETH_ALEN, pos)) + goto fail; + pos += ETH_ALEN; + nla_nest_end(msg, attr2); + } + + nla_nest_end(msg, attr1); + nla_nest_end(msg, attr); + + ret = send_and_recv_msgs(drv, msg, + nl80211_get_bss_transition_status_handler, + info); + msg = NULL; + if (ret) { + wpa_printf(MSG_ERROR, + "nl80211: WNM Get BSS transition status failed: ret=%d (%s)", + ret, strerror(-ret)); + goto fail; + } + return info; + +fail: + nlmsg_free(msg); + os_free(info->candidates); + os_free(info); + return NULL; +} + + +/** + * nl80211_ignore_assoc_disallow - Configure driver to ignore assoc_disallow + * @priv: Pointer to private driver data from wpa_driver_nl80211_init() + * @ignore_assoc_disallow: 0 to not ignore, 1 to ignore + * Returns: 0 on success, -1 on failure + */ +static int nl80211_ignore_assoc_disallow(void *priv, int ignore_disallow) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + struct nlattr *attr; + int ret = -1; + + if (!drv->set_wifi_conf_vendor_cmd_avail) + return -1; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION)) + goto fail; + + attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!attr) + goto fail; + + wpa_printf(MSG_DEBUG, "nl80211: Set ignore_assoc_disallow %d", + ignore_disallow); + if (nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED, + ignore_disallow)) + goto fail; + + nla_nest_end(msg, attr); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_ERROR, + "nl80211: Set ignore_assoc_disallow failed: ret=%d (%s)", + ret, strerror(-ret)); + goto fail; + } + +fail: + nlmsg_free(msg); + return ret; +} + +#endif /* CONFIG_MBO */ + #endif /* CONFIG_DRIVER_NL80211_QCA */ @@ -9434,6 +10587,88 @@ static int nl80211_get_ext_capab(void *priv, enum wpa_driver_if_type type, } +static int nl80211_update_connection_params( + void *priv, struct wpa_driver_associate_params *params, + enum wpa_drv_update_connect_params_mask mask) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg; + int ret = -1; + enum nl80211_auth_type type; + + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_CONNECT_PARAMS); + if (!msg) + goto fail; + + wpa_printf(MSG_DEBUG, "nl80211: Update connection params (ifindex=%d)", + drv->ifindex); + + if ((mask & WPA_DRV_UPDATE_ASSOC_IES) && params->wpa_ie) { + if (nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len, + params->wpa_ie)) + goto fail; + wpa_hexdump(MSG_DEBUG, " * IEs", params->wpa_ie, + params->wpa_ie_len); + } + + if (mask & WPA_DRV_UPDATE_AUTH_TYPE) { + type = get_nl_auth_type(params->auth_alg); + if (type == NL80211_AUTHTYPE_MAX || + nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type)) + goto fail; + wpa_printf(MSG_DEBUG, " * Auth Type %d", type); + } + + if ((mask & WPA_DRV_UPDATE_FILS_ERP_INFO) && + nl80211_put_fils_connect_params(drv, params, msg)) + goto fail; + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: Update connect params command failed: ret=%d (%s)", + ret, strerror(-ret)); + +fail: + nlmsg_free(msg); + return ret; +} + + +static int nl80211_send_external_auth_status(void *priv, + struct external_auth *params) +{ + struct i802_bss *bss = priv; + struct wpa_driver_nl80211_data *drv = bss->drv; + struct nl_msg *msg = NULL; + int ret = -1; + + wpa_dbg(drv->ctx, MSG_DEBUG, + "nl80211: External auth status: %u", params->status); + + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_EXTERNAL_AUTH); + if (!msg || + nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, params->status) || + nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, + params->ssid) || + nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid)) + goto fail; + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, + "nl80211: External Auth status update failed: ret=%d (%s)", + ret, strerror(-ret)); + goto fail; + } +fail: + nlmsg_free(msg); + return ret; +} + + const struct wpa_driver_ops wpa_driver_nl80211_ops = { .name = "nl80211", .desc = "Linux nl80211/cfg80211", @@ -9543,6 +10778,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .get_ifindex = nl80211_get_ifindex, #ifdef CONFIG_DRIVER_NL80211_QCA .roaming = nl80211_roaming, + .disable_fils = nl80211_disable_fils, .do_acs = wpa_driver_do_acs, .set_band = nl80211_set_band, .get_pref_freq_list = nl80211_get_pref_freq_list, @@ -9550,7 +10786,15 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = { .p2p_lo_start = nl80211_p2p_lo_start, .p2p_lo_stop = nl80211_p2p_lo_stop, .set_default_scan_ies = nl80211_set_default_scan_ies, + .set_tdls_mode = nl80211_set_tdls_mode, +#ifdef CONFIG_MBO + .get_bss_transition_status = nl80211_get_bss_transition_status, + .ignore_assoc_disallow = nl80211_ignore_assoc_disallow, +#endif /* CONFIG_MBO */ + .set_bssid_blacklist = nl80211_set_bssid_blacklist, #endif /* CONFIG_DRIVER_NL80211_QCA */ .configure_data_frame_filters = nl80211_configure_data_frame_filters, .get_ext_capab = nl80211_get_ext_capab, + .update_connect_params = nl80211_update_connection_params, + .send_external_auth_status = nl80211_send_external_auth_status, }; diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h index d0ec48c9f9734..bc562ba7a8cca 100644 --- a/src/drivers/driver_nl80211.h +++ b/src/drivers/driver_nl80211.h @@ -60,11 +60,13 @@ struct i802_bss { char brname[IFNAMSIZ]; unsigned int beacon_set:1; unsigned int added_if_into_bridge:1; + unsigned int already_in_bridge:1; unsigned int added_bridge:1; unsigned int in_deinit:1; unsigned int wdev_id_set:1; unsigned int added_if:1; unsigned int static_ap:1; + unsigned int use_nl_connect:1; u8 addr[ETH_ALEN]; @@ -73,11 +75,12 @@ struct i802_bss { int if_dynamic; void *ctx; - struct nl_handle *nl_preq, *nl_mgmt; + struct nl_handle *nl_preq, *nl_mgmt, *nl_connect; struct nl_cb *nl_cb; struct nl80211_wiphy_data *wiphy_data; struct dl_list wiphy_list; + u8 rand_addr[ETH_ALEN]; }; struct wpa_driver_nl80211_data { @@ -160,6 +163,9 @@ struct wpa_driver_nl80211_data { unsigned int scan_vendor_cmd_avail:1; unsigned int connect_reassoc:1; unsigned int set_wifi_conf_vendor_cmd_avail:1; + unsigned int he_capab_vendor_cmd_avail:1; + unsigned int fetch_bss_trans_status:1; + unsigned int roam_vendor_cmd_avail:1; u64 vendor_scan_cookie; u64 remain_on_chan_cookie; @@ -208,6 +214,8 @@ struct wpa_driver_nl80211_data { * (NL80211_CMD_VENDOR). 0 if no pending scan request. */ int last_scan_cmd; + + struct he_capabilities he_capab; }; struct nl_msg; @@ -228,6 +236,7 @@ int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, void *arg, int use_existing); void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx); unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv); +int nl80211_get_assoc_ssid(struct wpa_driver_nl80211_data *drv, u8 *ssid); enum chan_width convert2width(int width); void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv); struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv, @@ -244,7 +253,8 @@ int wpa_driver_nl80211_set_mode(struct i802_bss *bss, enum nl80211_iftype nlmode); int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, const u8 *addr, int cmd, u16 reason_code, - int local_state_change); + int local_state_change, + struct nl_handle *nl_connect); int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv); void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv); @@ -254,7 +264,8 @@ int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv, int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv); struct hostapd_hw_modes * -nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags); +nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags, + u8 *dfs_domain); int process_global_event(struct nl_msg *msg, void *arg); int process_bss_event(struct nl_msg *msg, void *arg); @@ -282,15 +293,6 @@ int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon, /* driver_nl80211_scan.c */ -struct nl80211_bss_info_arg { - struct wpa_driver_nl80211_data *drv; - struct wpa_scan_results *res; - unsigned int assoc_freq; - unsigned int ibss_freq; - u8 assoc_bssid[ETH_ALEN]; -}; - -int bss_info_handler(struct nl_msg *msg, void *arg); void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx); int wpa_driver_nl80211_scan(struct i802_bss *bss, struct wpa_driver_scan_params *params); @@ -299,7 +301,7 @@ int wpa_driver_nl80211_sched_scan(void *priv, int wpa_driver_nl80211_stop_sched_scan(void *priv); struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv); void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv); -int wpa_driver_nl80211_abort_scan(void *priv); +int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie); int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, struct wpa_driver_scan_params *params); int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len); diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c index 6adc3f6d33dcd..7b360d209aba2 100644 --- a/src/drivers/driver_nl80211_capa.c +++ b/src/drivers/driver_nl80211_capa.c @@ -12,8 +12,8 @@ #include <netlink/genl/genl.h> #include "utils/common.h" -#include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "common/wpa_common.h" #include "common/qca-vendor.h" #include "common/qca-vendor-attr.h" #include "driver_nl80211.h" @@ -266,40 +266,40 @@ static void wiphy_info_cipher_suites(struct wiphy_info_data *info, c >> 24, (c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff); switch (c) { - case WLAN_CIPHER_SUITE_CCMP_256: + case RSN_CIPHER_SUITE_CCMP_256: info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256; break; - case WLAN_CIPHER_SUITE_GCMP_256: + case RSN_CIPHER_SUITE_GCMP_256: info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256; break; - case WLAN_CIPHER_SUITE_CCMP: + case RSN_CIPHER_SUITE_CCMP: info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP; break; - case WLAN_CIPHER_SUITE_GCMP: + case RSN_CIPHER_SUITE_GCMP: info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP; break; - case WLAN_CIPHER_SUITE_TKIP: + case RSN_CIPHER_SUITE_TKIP: info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP; break; - case WLAN_CIPHER_SUITE_WEP104: + case RSN_CIPHER_SUITE_WEP104: info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104; break; - case WLAN_CIPHER_SUITE_WEP40: + case RSN_CIPHER_SUITE_WEP40: info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40; break; - case WLAN_CIPHER_SUITE_AES_CMAC: + case RSN_CIPHER_SUITE_AES_128_CMAC: info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP; break; - case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case RSN_CIPHER_SUITE_BIP_GMAC_128: info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128; break; - case WLAN_CIPHER_SUITE_BIP_GMAC_256: + case RSN_CIPHER_SUITE_BIP_GMAC_256: info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256; break; - case WLAN_CIPHER_SUITE_BIP_CMAC_256: + case RSN_CIPHER_SUITE_BIP_CMAC_256: info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256; break; - case WLAN_CIPHER_SUITE_NO_GROUP_ADDR: + case RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED: info->capa->enc |= WPA_DRIVER_CAPA_ENC_GTK_NOT_USED; break; } @@ -362,6 +362,72 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info, if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_RRM)) capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_RRM; + + if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_FILS_STA)) + capa->flags |= WPA_DRIVER_FLAGS_SUPPORT_FILS; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_BEACON_RATE_LEGACY)) + capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_BEACON_RATE_HT)) + capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_HT; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_BEACON_RATE_VHT)) + capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_VHT; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_SET_SCAN_DWELL)) + capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_SCAN_START_TIME) && + ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_BSS_PARENT_TSF) && + ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_SET_SCAN_DWELL)) + capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT; + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA)) + capa->flags |= WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA; + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED)) + capa->flags |= WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED; + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI)) + capa->flags |= WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI; + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_FILS_SK_OFFLOAD)) + capa->flags |= WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK) && + ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X)) + capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_MFP_OPTIONAL)) + capa->flags |= WPA_DRIVER_FLAGS_MFP_OPTIONAL; + + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_DFS_OFFLOAD)) + capa->flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD; + +#ifdef CONFIG_MBO + if (ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME) && + ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP) && + ext_feature_isset(ext_features, len, + NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE) && + ext_feature_isset( + ext_features, len, + NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION)) + capa->flags |= WPA_DRIVER_FLAGS_OCE_STA; +#endif /* CONFIG_MBO */ } @@ -510,23 +576,22 @@ static void wiphy_info_extended_capab(struct wpa_driver_nl80211_data *drv, nl80211_iftype_str(capa->iftype)); len = nla_len(tb1[NL80211_ATTR_EXT_CAPA]); - capa->ext_capa = os_malloc(len); + capa->ext_capa = os_memdup(nla_data(tb1[NL80211_ATTR_EXT_CAPA]), + len); if (!capa->ext_capa) goto err; - os_memcpy(capa->ext_capa, nla_data(tb1[NL80211_ATTR_EXT_CAPA]), - len); capa->ext_capa_len = len; wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities", capa->ext_capa, capa->ext_capa_len); len = nla_len(tb1[NL80211_ATTR_EXT_CAPA_MASK]); - capa->ext_capa_mask = os_malloc(len); + capa->ext_capa_mask = + os_memdup(nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]), + len); if (!capa->ext_capa_mask) goto err; - os_memcpy(capa->ext_capa_mask, - nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]), len); wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities mask", capa->ext_capa_mask, capa->ext_capa_len); @@ -708,6 +773,15 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) case QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION: drv->set_wifi_conf_vendor_cmd_avail = 1; break; + case QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES: + drv->he_capab_vendor_cmd_avail = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS: + drv->fetch_bss_trans_status = 1; + break; + case QCA_NL80211_VENDOR_SUBCMD_ROAM: + drv->roam_vendor_cmd_avail = 1; + break; #endif /* CONFIG_DRIVER_NL80211_QCA */ } } @@ -744,6 +818,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg) capa->max_csa_counters = nla_get_u8(tb[NL80211_ATTR_MAX_CSA_COUNTERS]); + if (tb[NL80211_ATTR_WIPHY_SELF_MANAGED_REG]) + capa->flags |= WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY; + return NL_SKIP; } @@ -877,6 +954,100 @@ static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv) } +static int qca_nl80211_he_capab_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct he_capabilities *he_capab = arg; + struct nlattr *nl_vend; + struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX + 1]; + size_t len; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_VENDOR_DATA]) + return NL_SKIP; + + nl_vend = tb[NL80211_ATTR_VENDOR_DATA]; + nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX, + nla_data(nl_vend), nla_len(nl_vend), NULL); + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED]) { + u8 he_supported; + + he_supported = nla_get_u8( + tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED]); + wpa_printf(MSG_DEBUG, "nl80211: HE capabilities supported: %u", + he_supported); + he_capab->he_supported = he_supported; + if (!he_supported) + return NL_SKIP; + } + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]) { + len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]); + + if (len > sizeof(he_capab->phy_cap)) + len = sizeof(he_capab->phy_cap); + os_memcpy(he_capab->phy_cap, + nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]), + len); + } + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_CAPAB]) + he_capab->mac_cap = + nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_CAPAB]); + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_MCS]) + he_capab->mcs = + nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_MCS]); + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_NUM_SS]) + he_capab->ppet.numss_m1 = + nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_NUM_SS]); + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK]) + he_capab->ppet.ru_count = + nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK]); + + if (tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]) { + len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]); + + if (len > sizeof(he_capab->ppet.ppet16_ppet8_ru3_ru0)) + len = sizeof(he_capab->ppet.ppet16_ppet8_ru3_ru0); + os_memcpy(he_capab->ppet.ppet16_ppet8_ru3_ru0, + nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]), + len); + } + + return NL_SKIP; +} + + +static void qca_nl80211_check_he_capab(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + int ret; + + if (!drv->he_capab_vendor_cmd_avail) + return; + + if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES)) { + nlmsg_free(msg); + return; + } + + ret = send_and_recv_msgs(drv, msg, qca_nl80211_he_capab_handler, + &drv->he_capab); + if (!ret && drv->he_capab.he_supported) + drv->capa.flags |= WPA_DRIVER_FLAGS_HE_CAPABILITIES; +} + + struct features_info { u8 *flags; size_t flags_len; @@ -973,6 +1144,12 @@ static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv) drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS; if (check_feature(QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD, &info)) drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD; + if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA, &info)) + drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA; + if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_AP, &info)) + drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_AP; + if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON, &info)) + drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA_CFON; os_free(info.flags); } @@ -994,7 +1171,19 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK | WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B | - WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192; + WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192 | + WPA_DRIVER_CAPA_KEY_MGMT_OWE | + WPA_DRIVER_CAPA_KEY_MGMT_DPP; + + if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 | + WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 | + WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 | + WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384; + else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD) + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 | + WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384; + drv->capa.auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | WPA_DRIVER_AUTH_LEAP; @@ -1046,8 +1235,10 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv) drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS; #ifdef CONFIG_DRIVER_NL80211_QCA - qca_nl80211_check_dfs_capa(drv); + if (!(info.capa->flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) + qca_nl80211_check_dfs_capa(drv); qca_nl80211_get_features(drv); + qca_nl80211_check_he_capab(drv); /* * To enable offchannel simultaneous support in wpa_supplicant, the @@ -1069,6 +1260,7 @@ struct phy_info_arg { struct hostapd_hw_modes *modes; int last_mode, last_chan_idx; int failed; + u8 dfs_domain; }; static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa, @@ -1388,14 +1580,13 @@ wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes, mode11g = &modes[mode11g_idx]; mode->num_channels = mode11g->num_channels; - mode->channels = os_malloc(mode11g->num_channels * + mode->channels = os_memdup(mode11g->channels, + mode11g->num_channels * sizeof(struct hostapd_channel_data)); if (mode->channels == NULL) { (*num_modes)--; return modes; /* Could not add 802.11b mode */ } - os_memcpy(mode->channels, mode11g->channels, - mode11g->num_channels * sizeof(struct hostapd_channel_data)); mode->num_rates = 0; mode->rates = os_malloc(4 * sizeof(int)); @@ -1598,6 +1789,20 @@ static void nl80211_reg_rule_vht(struct nlattr *tb[], } +static void nl80211_set_dfs_domain(enum nl80211_dfs_regions region, + u8 *dfs_domain) +{ + if (region == NL80211_DFS_FCC) + *dfs_domain = HOSTAPD_DFS_REGION_FCC; + else if (region == NL80211_DFS_ETSI) + *dfs_domain = HOSTAPD_DFS_REGION_ETSI; + else if (region == NL80211_DFS_JP) + *dfs_domain = HOSTAPD_DFS_REGION_JP; + else + *dfs_domain = 0; +} + + static const char * dfs_domain_name(enum nl80211_dfs_regions region) { switch (region) { @@ -1644,6 +1849,7 @@ static int nl80211_get_reg(struct nl_msg *msg, void *arg) if (tb_msg[NL80211_ATTR_DFS_REGION]) { enum nl80211_dfs_regions dfs_domain; dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]); + nl80211_set_dfs_domain(dfs_domain, &results->dfs_domain); wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)", (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), dfs_domain_name(dfs_domain)); @@ -1715,12 +1921,20 @@ static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv, return -ENOMEM; nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG); + if (drv->capa.flags & WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY) { + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx)) { + nlmsg_free(msg); + return -1; + } + } + return send_and_recv_msgs(drv, msg, nl80211_get_reg, results); } struct hostapd_hw_modes * -nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) +nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags, + u8 *dfs_domain) { u32 feat; struct i802_bss *bss = priv; @@ -1732,10 +1946,12 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) .modes = NULL, .last_mode = -1, .failed = 0, + .dfs_domain = 0, }; *num_modes = 0; *flags = 0; + *dfs_domain = 0; feat = get_nl80211_protocol_features(drv); if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP) @@ -1756,8 +1972,12 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) os_free(result.modes[i].rates); } os_free(result.modes); + *num_modes = 0; return NULL; } + + *dfs_domain = result.dfs_domain; + return wpa_driver_nl80211_postprocess_modes(result.modes, num_modes); } diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c index 762e3acc28079..205b4cd4b0829 100644 --- a/src/drivers/driver_nl80211_event.c +++ b/src/drivers/driver_nl80211_event.c @@ -1,6 +1,6 @@ /* * Driver interaction with Linux nl80211/cfg80211 - Event processing - * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi> * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net> * Copyright (c) 2009-2010, Atheros Communications * @@ -131,6 +131,11 @@ static const char * nl80211_command_to_string(enum nl80211_commands cmd) C2S(NL80211_CMD_SET_QOS_MAP) C2S(NL80211_CMD_ADD_TX_TS) C2S(NL80211_CMD_DEL_TX_TS) + C2S(NL80211_CMD_WIPHY_REG_CHANGE) + C2S(NL80211_CMD_PORT_AUTHORIZED) + C2S(NL80211_CMD_EXTERNAL_AUTH) + C2S(NL80211_CMD_STA_OPMODE_CHANGED) + C2S(NL80211_CMD_CONTROL_PORT_FRAME) default: return "NL80211_CMD_UNKNOWN"; } @@ -206,6 +211,7 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, const struct ieee80211_mgmt *mgmt; union wpa_event_data event; u16 status; + int ssid_len; if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) && drv->force_connect_cmd) { @@ -247,6 +253,8 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN); os_memset(&event, 0, sizeof(event)); + event.assoc_info.resp_frame = frame; + event.assoc_info.resp_frame_len = len; if (len > 24 + sizeof(mgmt->u.assoc_resp)) { event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable; event.assoc_info.resp_ies_len = @@ -255,6 +263,16 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv, event.assoc_info.freq = drv->assoc_freq; + /* When this association was initiated outside of wpa_supplicant, + * drv->ssid needs to be set here to satisfy later checking. */ + ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid); + if (ssid_len > 0) { + drv->ssid_len = ssid_len; + wpa_printf(MSG_DEBUG, + "nl80211: Set drv->ssid based on scan res info to '%s'", + wpa_ssid_txt(drv->ssid, drv->ssid_len)); + } + nl80211_parse_wmm_params(wmm, &event.assoc_info.wmm_params); wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); @@ -266,15 +284,20 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, struct nlattr *addr, struct nlattr *req_ie, struct nlattr *resp_ie, struct nlattr *timed_out, + struct nlattr *timeout_reason, struct nlattr *authorized, struct nlattr *key_replay_ctr, struct nlattr *ptk_kck, struct nlattr *ptk_kek, - struct nlattr *subnet_status) + struct nlattr *subnet_status, + struct nlattr *fils_erp_next_seq_num, + struct nlattr *fils_pmk, + struct nlattr *fils_pmkid) { union wpa_event_data event; - const u8 *ssid; + const u8 *ssid = NULL; u16 status_code; + int ssid_len; if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) { /* @@ -324,6 +347,27 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, } event.assoc_reject.status_code = status_code; event.assoc_reject.timed_out = timed_out != NULL; + if (timed_out && timeout_reason) { + enum nl80211_timeout_reason reason; + + reason = nla_get_u32(timeout_reason); + switch (reason) { + case NL80211_TIMEOUT_SCAN: + event.assoc_reject.timeout_reason = "scan"; + break; + case NL80211_TIMEOUT_AUTH: + event.assoc_reject.timeout_reason = "auth"; + break; + case NL80211_TIMEOUT_ASSOC: + event.assoc_reject.timeout_reason = "assoc"; + break; + default: + break; + } + } + if (fils_erp_next_seq_num) + event.assoc_reject.fils_erp_next_seq_num = + nla_get_u16(fils_erp_next_seq_num); wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event); return; } @@ -345,6 +389,10 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, if (ssid && ssid[1] > 0 && ssid[1] <= 32) { drv->ssid_len = ssid[1]; os_memcpy(drv->ssid, ssid + 2, ssid[1]); + wpa_printf(MSG_DEBUG, + "nl80211: Set drv->ssid based on req_ie to '%s'", + wpa_ssid_txt(drv->ssid, + drv->ssid_len)); } } } @@ -355,6 +403,16 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, event.assoc_info.freq = nl80211_get_assoc_freq(drv); + if ((!ssid || ssid[1] == 0 || ssid[1] > 32) && + (ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid)) > 0) { + /* When this connection was initiated outside of wpa_supplicant, + * drv->ssid needs to be set here to satisfy later checking. */ + drv->ssid_len = ssid_len; + wpa_printf(MSG_DEBUG, + "nl80211: Set drv->ssid based on scan res info to '%s'", + wpa_ssid_txt(drv->ssid, drv->ssid_len)); + } + if (authorized && nla_get_u8(authorized)) { event.assoc_info.authorized = 1; wpa_printf(MSG_DEBUG, "nl80211: connection authorized"); @@ -383,6 +441,18 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv, event.assoc_info.subnet_status = nla_get_u8(subnet_status); } + if (fils_erp_next_seq_num) + event.assoc_info.fils_erp_next_seq_num = + nla_get_u16(fils_erp_next_seq_num); + + if (fils_pmk) { + event.assoc_info.fils_pmk = nla_data(fils_pmk); + event.assoc_info.fils_pmk_len = nla_len(fils_pmk); + } + + if (fils_pmkid) + event.assoc_info.fils_pmkid = nla_data(fils_pmkid); + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event); } @@ -516,6 +586,7 @@ static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv, data.ch_switch.cf2 = nla_get_u32(cf2); bss->freq = data.ch_switch.freq; + drv->assoc_freq = data.ch_switch.freq; wpa_supplicant_event(bss->ctx, EVENT_CH_SWITCH, &data); } @@ -607,7 +678,7 @@ static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv, cookie_val = nla_get_u64(cookie); wpa_printf(MSG_DEBUG, "nl80211: Action TX status:" - " cookie=0%llx%s (ack=%d)", + " cookie=0x%llx%s (ack=%d)", (long long unsigned int) cookie_val, cookie_val == drv->send_action_cookie ? " (match)" : " (unknown)", ack != NULL); @@ -683,12 +754,12 @@ static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv, * disconnection event for the old AP may show up after * we have started connection with the new AP. */ - wpa_printf(MSG_DEBUG, - "nl80211: Ignore deauth/disassoc event from old AP " - MACSTR - " when already connecting with " MACSTR, - MAC2STR(bssid), - MAC2STR(drv->auth_attempt_bssid)); + wpa_printf(MSG_DEBUG, + "nl80211: Ignore deauth/disassoc event from old AP " + MACSTR + " when already connecting with " MACSTR, + MAC2STR(bssid), + MAC2STR(drv->auth_attempt_bssid)); return; } @@ -831,6 +902,8 @@ static void mlme_event(struct i802_bss *bss, MAC2STR(data + 4 + ETH_ALEN)); if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) && os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 && + (is_zero_ether_addr(bss->rand_addr) || + os_memcmp(bss->rand_addr, data + 4, ETH_ALEN) != 0) && os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) { wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event " "for foreign address", bss->ifname); @@ -1081,6 +1154,16 @@ static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted, wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s", msg); } + + if (tb[NL80211_ATTR_SCAN_START_TIME_TSF] && + tb[NL80211_ATTR_SCAN_START_TIME_TSF_BSSID]) { + info->scan_start_tsf = + nla_get_u64(tb[NL80211_ATTR_SCAN_START_TIME_TSF]); + os_memcpy(info->scan_start_tsf_bssid, + nla_data(tb[NL80211_ATTR_SCAN_START_TIME_TSF_BSSID]), + ETH_ALEN); + } + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event); } @@ -1093,6 +1176,10 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 }, [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, [NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_BEACON_LOSS_EVENT] = { .type = NLA_FLAG }, }; struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1]; enum nl80211_cqm_rssi_threshold_event event; @@ -1114,12 +1201,39 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, return; os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN); + ed.low_ack.num_packets = + nla_get_u32(cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]); + wpa_printf(MSG_DEBUG, "nl80211: Packet loss event for " MACSTR + " (num_packets %u)", + MAC2STR(ed.low_ack.addr), ed.low_ack.num_packets); wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed); return; } - if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) + if (cqm[NL80211_ATTR_CQM_BEACON_LOSS_EVENT]) { + wpa_printf(MSG_DEBUG, "nl80211: Beacon loss event"); + wpa_supplicant_event(drv->ctx, EVENT_BEACON_LOSS, NULL); + return; + } + + if (cqm[NL80211_ATTR_CQM_TXE_RATE] && + cqm[NL80211_ATTR_CQM_TXE_PKTS] && + cqm[NL80211_ATTR_CQM_TXE_INTVL] && + cqm[NL80211_ATTR_MAC]) { + wpa_printf(MSG_DEBUG, "nl80211: CQM TXE event for " MACSTR + " (rate: %u pkts: %u interval: %u)", + MAC2STR((u8 *) nla_data(cqm[NL80211_ATTR_MAC])), + nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_RATE]), + nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_PKTS]), + nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_INTVL])); + return; + } + + if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) { + wpa_printf(MSG_DEBUG, + "nl80211: Not a CQM RSSI threshold event"); return; + } event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]); if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) { @@ -1130,8 +1244,12 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv, wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor " "event: RSSI low"); ed.signal_change.above_threshold = 0; - } else + } else { + wpa_printf(MSG_DEBUG, + "nl80211: Unknown CQM RSSI threshold event: %d", + event); return; + } res = nl80211_get_link_signal(drv, &sig); if (res == 0) { @@ -1473,6 +1591,13 @@ static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv, case NL80211_RADAR_NOP_FINISHED: wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data); break; + case NL80211_RADAR_PRE_CAC_EXPIRED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_PRE_CAC_EXPIRED, + &data); + break; + case NL80211_RADAR_CAC_STARTED: + wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data); + break; default: wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d " "received", event_type); @@ -1650,12 +1775,15 @@ static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv, tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE], - NULL, + NULL, NULL, tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK], tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK], - tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS]); + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK], + tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID]); } @@ -2055,6 +2183,146 @@ static void nl80211_reg_change_event(struct wpa_driver_nl80211_data *drv, } +static void nl80211_external_auth(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data event; + enum nl80211_external_auth_action act; + + if (!tb[NL80211_ATTR_AKM_SUITES] || + !tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION] || + !tb[NL80211_ATTR_BSSID] || + !tb[NL80211_ATTR_SSID]) + return; + + os_memset(&event, 0, sizeof(event)); + act = nla_get_u32(tb[NL80211_ATTR_EXTERNAL_AUTH_ACTION]); + switch (act) { + case NL80211_EXTERNAL_AUTH_START: + event.external_auth.action = EXT_AUTH_START; + break; + case NL80211_EXTERNAL_AUTH_ABORT: + event.external_auth.action = EXT_AUTH_ABORT; + break; + default: + return; + } + + event.external_auth.key_mgmt_suite = + nla_get_u32(tb[NL80211_ATTR_AKM_SUITES]); + + event.external_auth.ssid_len = nla_len(tb[NL80211_ATTR_SSID]); + if (event.external_auth.ssid_len > SSID_MAX_LEN) + return; + os_memcpy(event.external_auth.ssid, nla_data(tb[NL80211_ATTR_SSID]), + event.external_auth.ssid_len); + + os_memcpy(event.external_auth.bssid, nla_data(tb[NL80211_ATTR_BSSID]), + ETH_ALEN); + + wpa_printf(MSG_DEBUG, + "nl80211: External auth action: %u, AKM: 0x%x", + event.external_auth.action, + event.external_auth.key_mgmt_suite); + wpa_supplicant_event(drv->ctx, EVENT_EXTERNAL_AUTH, &event); +} + + +static void nl80211_port_authorized(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + const u8 *addr; + + if (!tb[NL80211_ATTR_MAC] || + nla_len(tb[NL80211_ATTR_MAC]) != ETH_ALEN) { + wpa_printf(MSG_DEBUG, + "nl80211: Ignore port authorized event without BSSID"); + return; + } + + addr = nla_data(tb[NL80211_ATTR_MAC]); + if (os_memcmp(addr, drv->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Ignore port authorized event for " MACSTR + " (not the currently connected BSSID " MACSTR ")", + MAC2STR(addr), MAC2STR(drv->bssid)); + return; + } + + wpa_supplicant_event(drv->ctx, EVENT_PORT_AUTHORIZED, NULL); +} + + +static void nl80211_sta_opmode_change_event(struct wpa_driver_nl80211_data *drv, + struct nlattr **tb) +{ + union wpa_event_data ed; + u8 smps_mode, max_bw; + + if (!tb[NL80211_ATTR_MAC] || + (!tb[NL80211_ATTR_CHANNEL_WIDTH] && + !tb[NL80211_ATTR_SMPS_MODE] && + !tb[NL80211_ATTR_NSS])) + return; + + ed.sta_opmode.smps_mode = SMPS_INVALID; + ed.sta_opmode.chan_width = CHAN_WIDTH_UNKNOWN; + ed.sta_opmode.rx_nss = 0xff; + ed.sta_opmode.addr = nla_data(tb[NL80211_ATTR_MAC]); + + if (tb[NL80211_ATTR_SMPS_MODE]) { + smps_mode = nla_get_u8(tb[NL80211_ATTR_SMPS_MODE]); + switch (smps_mode) { + case NL80211_SMPS_OFF: + ed.sta_opmode.smps_mode = SMPS_OFF; + break; + case NL80211_SMPS_STATIC: + ed.sta_opmode.smps_mode = SMPS_STATIC; + break; + case NL80211_SMPS_DYNAMIC: + ed.sta_opmode.smps_mode = SMPS_DYNAMIC; + break; + default: + ed.sta_opmode.smps_mode = SMPS_INVALID; + break; + } + } + + if (tb[NL80211_ATTR_CHANNEL_WIDTH]) { + max_bw = nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]); + switch (max_bw) { + case NL80211_CHAN_WIDTH_20_NOHT: + ed.sta_opmode.chan_width = CHAN_WIDTH_20_NOHT; + break; + case NL80211_CHAN_WIDTH_20: + ed.sta_opmode.chan_width = CHAN_WIDTH_20; + break; + case NL80211_CHAN_WIDTH_40: + ed.sta_opmode.chan_width = CHAN_WIDTH_40; + break; + case NL80211_CHAN_WIDTH_80: + ed.sta_opmode.chan_width = CHAN_WIDTH_80; + break; + case NL80211_CHAN_WIDTH_80P80: + ed.sta_opmode.chan_width = CHAN_WIDTH_80P80; + break; + case NL80211_CHAN_WIDTH_160: + ed.sta_opmode.chan_width = CHAN_WIDTH_160; + break; + default: + ed.sta_opmode.chan_width = CHAN_WIDTH_UNKNOWN; + break; + + } + } + + if (tb[NL80211_ATTR_NSS]) + ed.sta_opmode.rx_nss = nla_get_u8(tb[NL80211_ATTR_NSS]); + + wpa_supplicant_event(drv->ctx, EVENT_STATION_OPMODE_CHANGED, &ed); +} + + static void do_process_drv_event(struct i802_bss *bss, int cmd, struct nlattr **tb) { @@ -2113,9 +2381,10 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, case NL80211_CMD_NEW_SCAN_RESULTS: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: New scan results available"); + if (drv->last_scan_cmd != NL80211_CMD_VENDOR) + drv->scan_state = SCAN_COMPLETED; drv->scan_complete_events = 1; if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) { - drv->scan_state = SCAN_COMPLETED; eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); drv->last_scan_cmd = 0; @@ -2132,8 +2401,9 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, break; case NL80211_CMD_SCAN_ABORTED: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted"); - if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) { + if (drv->last_scan_cmd != NL80211_CMD_VENDOR) drv->scan_state = SCAN_ABORTED; + if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) { /* * Need to indicate that scan results are available in * order not to make wpa_supplicant stop its scanning. @@ -2168,7 +2438,13 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, tb[NL80211_ATTR_REQ_IE], tb[NL80211_ATTR_RESP_IE], tb[NL80211_ATTR_TIMED_OUT], - NULL, NULL, NULL, NULL, NULL); + tb[NL80211_ATTR_TIMEOUT_REASON], + NULL, NULL, NULL, + tb[NL80211_ATTR_FILS_KEK], + NULL, + tb[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM], + tb[NL80211_ATTR_PMK], + tb[NL80211_ATTR_PMKID]); break; case NL80211_CMD_CH_SWITCH_NOTIFY: mlme_event_ch_switch(drv, @@ -2200,6 +2476,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, nl80211_cqm_event(drv, tb); break; case NL80211_CMD_REG_CHANGE: + case NL80211_CMD_WIPHY_REG_CHANGE: nl80211_reg_change_event(drv, tb); break; case NL80211_CMD_REG_BEACON_HINT: @@ -2245,6 +2522,12 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd, case NL80211_CMD_NEW_PEER_CANDIDATE: nl80211_new_peer_candidate(drv, tb); break; + case NL80211_CMD_PORT_AUTHORIZED: + nl80211_port_authorized(drv, tb); + break; + case NL80211_CMD_STA_OPMODE_CHANGED: + nl80211_sta_opmode_change_event(drv, tb); + break; default: wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", cmd); @@ -2259,10 +2542,11 @@ int process_global_event(struct nl_msg *msg, void *arg) struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct wpa_driver_nl80211_data *drv, *tmp; - int ifidx = -1; + int ifidx = -1, wiphy_idx = -1, wiphy_idx_rx = -1; struct i802_bss *bss; u64 wdev_id = 0; int wdev_id_set = 0; + int wiphy_idx_set = 0; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); @@ -2272,13 +2556,19 @@ int process_global_event(struct nl_msg *msg, void *arg) else if (tb[NL80211_ATTR_WDEV]) { wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]); wdev_id_set = 1; + } else if (tb[NL80211_ATTR_WIPHY]) { + wiphy_idx_rx = nla_get_u32(tb[NL80211_ATTR_WIPHY]); + wiphy_idx_set = 1; } dl_list_for_each_safe(drv, tmp, &global->interfaces, struct wpa_driver_nl80211_data, list) { for (bss = drv->first_bss; bss; bss = bss->next) { - if ((ifidx == -1 && !wdev_id_set) || + if (wiphy_idx_set) + wiphy_idx = nl80211_get_wiphy_index(bss); + if ((ifidx == -1 && !wiphy_idx_set && !wdev_id_set) || ifidx == bss->ifindex || + (wiphy_idx_set && wiphy_idx == wiphy_idx_rx) || (wdev_id_set && bss->wdev_id_set && wdev_id == bss->wdev_id)) { do_process_drv_event(bss, gnlh->cmd, tb); @@ -2323,6 +2613,9 @@ int process_bss_event(struct nl_msg *msg, void *arg) case NL80211_CMD_UNEXPECTED_4ADDR_FRAME: nl80211_spurious_frame(bss, tb, 1); break; + case NL80211_CMD_EXTERNAL_AUTH: + nl80211_external_auth(bss->drv, tb); + break; default: wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event " "(cmd=%d)", gnlh->cmd); diff --git a/src/drivers/driver_nl80211_monitor.c b/src/drivers/driver_nl80211_monitor.c index 9376d1143800d..f25cd792464a0 100644 --- a/src/drivers/driver_nl80211_monitor.c +++ b/src/drivers/driver_nl80211_monitor.c @@ -361,8 +361,17 @@ int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv) */ snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss->ifname + 4); } else { + int ret; + /* Non-P2P interface with AP functionality. */ - snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss->ifname); + ret = os_snprintf(buf, IFNAMSIZ, "mon.%s", + drv->first_bss->ifname); + if (ret >= (int) sizeof(buf)) + wpa_printf(MSG_DEBUG, + "nl80211: Monitor interface name has been truncated to %s", + buf); + else if (ret < 0) + return ret; } buf[IFNAMSIZ - 1] = '\0'; diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c index c115b6b31b7dc..33a8d359848fb 100644 --- a/src/drivers/driver_nl80211_scan.c +++ b/src/drivers/driver_nl80211_scan.c @@ -10,6 +10,7 @@ */ #include "includes.h" +#include <time.h> #include <netlink/genl/genl.h> #include "utils/common.h" @@ -20,6 +21,14 @@ #include "driver_nl80211.h" +#define MAX_NL80211_NOISE_FREQS 50 + +struct nl80211_noise_info { + u32 freq[MAX_NL80211_NOISE_FREQS]; + s8 noise[MAX_NL80211_NOISE_FREQS]; + unsigned int count; +}; + static int get_noise_for_scan_results(struct nl_msg *msg, void *arg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; @@ -29,9 +38,10 @@ static int get_noise_for_scan_results(struct nl_msg *msg, void *arg) [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, }; - struct wpa_scan_results *scan_results = arg; - struct wpa_scan_res *scan_res; - size_t i; + struct nl80211_noise_info *info = arg; + + if (info->count >= MAX_NL80211_NOISE_FREQS) + return NL_STOP; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); @@ -55,34 +65,81 @@ static int get_noise_for_scan_results(struct nl_msg *msg, void *arg) if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) return NL_SKIP; - for (i = 0; i < scan_results->num; ++i) { - scan_res = scan_results->res[i]; - if (!scan_res) - continue; - if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) != - scan_res->freq) - continue; - if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID)) - continue; - scan_res->noise = (s8) - nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); - scan_res->flags &= ~WPA_SCAN_NOISE_INVALID; - } + info->freq[info->count] = + nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]); + info->noise[info->count] = + (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + info->count++; return NL_SKIP; } static int nl80211_get_noise_for_scan_results( - struct wpa_driver_nl80211_data *drv, - struct wpa_scan_results *scan_res) + struct wpa_driver_nl80211_data *drv, struct nl80211_noise_info *info) { struct nl_msg *msg; + os_memset(info, 0, sizeof(*info)); msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY); - return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, - scan_res); + return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, info); +} + + +static int nl80211_abort_scan(struct i802_bss *bss) +{ + int ret; + struct nl_msg *msg; + struct wpa_driver_nl80211_data *drv = bss->drv; + + wpa_printf(MSG_DEBUG, "nl80211: Abort scan"); + msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ABORT_SCAN); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Abort scan failed: ret=%d (%s)", + ret, strerror(-ret)); + } + return ret; +} + + +#ifdef CONFIG_DRIVER_NL80211_QCA +static int nl80211_abort_vendor_scan(struct wpa_driver_nl80211_data *drv, + u64 scan_cookie) +{ + struct nl_msg *msg; + struct nlattr *params; + int ret; + + wpa_printf(MSG_DEBUG, "nl80211: Abort vendor scan with cookie 0x%llx", + (long long unsigned int) scan_cookie); + + msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR); + if (!msg || + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) || + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN) || + !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) || + nla_put_u64(msg, QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE, scan_cookie)) + goto fail; + + nla_nest_end(msg, params); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_INFO, + "nl80211: Aborting vendor scan with cookie 0x%llx failed: ret=%d (%s)", + (long long unsigned int) scan_cookie, ret, + strerror(-ret)); + goto fail; + } + return 0; +fail: + nlmsg_free(msg); + return -1; } +#endif /* CONFIG_DRIVER_NL80211_QCA */ /** @@ -98,7 +155,13 @@ void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) struct wpa_driver_nl80211_data *drv = eloop_ctx; wpa_printf(MSG_DEBUG, "nl80211: Scan timeout - try to abort it"); - if (!wpa_driver_nl80211_abort_scan(drv->first_bss)) +#ifdef CONFIG_DRIVER_NL80211_QCA + if (drv->vendor_scan_cookie && + nl80211_abort_vendor_scan(drv, drv->vendor_scan_cookie) == 0) + return; +#endif /* CONFIG_DRIVER_NL80211_QCA */ + if (!drv->vendor_scan_cookie && + nl80211_abort_scan(drv->first_bss) == 0) return; wpa_printf(MSG_DEBUG, "nl80211: Failed to abort scan"); @@ -206,6 +269,34 @@ nl80211_scan_common(struct i802_bss *bss, u8 cmd, } } + if (params->duration) { + if (!(drv->capa.rrm_flags & + WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL) || + nla_put_u16(msg, NL80211_ATTR_MEASUREMENT_DURATION, + params->duration)) + goto fail; + + if (params->duration_mandatory && + nla_put_flag(msg, + NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY)) + goto fail; + } + + if (params->oce_scan) { + wpa_printf(MSG_DEBUG, + "nl80211: Add NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME"); + wpa_printf(MSG_DEBUG, + "nl80211: Add NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP"); + wpa_printf(MSG_DEBUG, + "nl80211: Add NL80211_SCAN_FLAG_OCE_PROBE_REQ_MIN_TX_RATE"); + wpa_printf(MSG_DEBUG, + "nl80211: Add NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION"); + scan_flags |= NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME | + NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP | + NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE | + NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION; + } + if (scan_flags && nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags)) goto fail; @@ -487,6 +578,44 @@ int wpa_driver_nl80211_sched_scan(void *priv, nla_nest_end(msg, match_sets); } + if (params->relative_rssi_set) { + struct nl80211_bss_select_rssi_adjust rssi_adjust; + + os_memset(&rssi_adjust, 0, sizeof(rssi_adjust)); + wpa_printf(MSG_DEBUG, "nl80211: Relative RSSI: %d", + params->relative_rssi); + if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI, + params->relative_rssi)) + goto fail; + + if (params->relative_adjust_rssi) { + int pref_band_set = 1; + + switch (params->relative_adjust_band) { + case WPA_SETBAND_5G: + rssi_adjust.band = NL80211_BAND_5GHZ; + break; + case WPA_SETBAND_2G: + rssi_adjust.band = NL80211_BAND_2GHZ; + break; + default: + pref_band_set = 0; + break; + } + rssi_adjust.delta = params->relative_adjust_rssi; + + if (pref_band_set && + nla_put(msg, NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST, + sizeof(rssi_adjust), &rssi_adjust)) + goto fail; + } + } + + if (params->sched_scan_start_delay && + nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_DELAY, + params->sched_scan_start_delay)) + goto fail; + ret = send_and_recv_msgs(drv, msg, NULL, NULL); /* TODO: if we get an error here, we should fall back to normal scan */ @@ -562,7 +691,9 @@ static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv, } -int bss_info_handler(struct nl_msg *msg, void *arg) +static struct wpa_scan_res * +nl80211_parse_bss_info(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg) { struct nlattr *tb[NL80211_ATTR_MAX + 1]; struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); @@ -579,50 +710,22 @@ int bss_info_handler(struct nl_msg *msg, void *arg) [NL80211_BSS_STATUS] = { .type = NLA_U32 }, [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 }, [NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC }, + [NL80211_BSS_PARENT_TSF] = { .type = NLA_U64 }, + [NL80211_BSS_PARENT_BSSID] = { .type = NLA_UNSPEC }, + [NL80211_BSS_LAST_SEEN_BOOTTIME] = { .type = NLA_U64 }, }; - struct nl80211_bss_info_arg *_arg = arg; - struct wpa_scan_results *res = _arg->res; - struct wpa_scan_res **tmp; struct wpa_scan_res *r; const u8 *ie, *beacon_ie; size_t ie_len, beacon_ie_len; u8 *pos; - size_t i; nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL); if (!tb[NL80211_ATTR_BSS]) - return NL_SKIP; + return NULL; if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], bss_policy)) - return NL_SKIP; - if (bss[NL80211_BSS_STATUS]) { - enum nl80211_bss_status status; - status = nla_get_u32(bss[NL80211_BSS_STATUS]); - if (status == NL80211_BSS_STATUS_ASSOCIATED && - bss[NL80211_BSS_FREQUENCY]) { - _arg->assoc_freq = - nla_get_u32(bss[NL80211_BSS_FREQUENCY]); - wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz", - _arg->assoc_freq); - } - if (status == NL80211_BSS_STATUS_IBSS_JOINED && - bss[NL80211_BSS_FREQUENCY]) { - _arg->ibss_freq = - nla_get_u32(bss[NL80211_BSS_FREQUENCY]); - wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz", - _arg->ibss_freq); - } - if (status == NL80211_BSS_STATUS_ASSOCIATED && - bss[NL80211_BSS_BSSID]) { - os_memcpy(_arg->assoc_bssid, - nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN); - wpa_printf(MSG_DEBUG, "nl80211: Associated with " - MACSTR, MAC2STR(_arg->assoc_bssid)); - } - } - if (!res) - return NL_SKIP; + return NULL; if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) { ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]); ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); @@ -638,13 +741,13 @@ int bss_info_handler(struct nl_msg *msg, void *arg) beacon_ie_len = 0; } - if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie, + if (nl80211_scan_filtered(drv, ie ? ie : beacon_ie, ie ? ie_len : beacon_ie_len)) - return NL_SKIP; + return NULL; r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len); if (r == NULL) - return NL_SKIP; + return NULL; if (bss[NL80211_BSS_BSSID]) os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN); @@ -673,6 +776,23 @@ int bss_info_handler(struct nl_msg *msg, void *arg) } if (bss[NL80211_BSS_SEEN_MS_AGO]) r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]); + if (bss[NL80211_BSS_LAST_SEEN_BOOTTIME]) { + u64 boottime; + struct timespec ts; + +#ifndef CLOCK_BOOTTIME +#define CLOCK_BOOTTIME 7 +#endif + if (clock_gettime(CLOCK_BOOTTIME, &ts) == 0) { + /* Use more accurate boottime information to update the + * scan result age since the driver reports this and + * CLOCK_BOOTTIME is available. */ + boottime = nla_get_u64( + bss[NL80211_BSS_LAST_SEEN_BOOTTIME]); + r->age = ((u64) ts.tv_sec * 1000000000 + + ts.tv_nsec - boottime) / 1000000; + } + } r->ie_len = ie_len; pos = (u8 *) (r + 1); if (ie) { @@ -695,40 +815,36 @@ int bss_info_handler(struct nl_msg *msg, void *arg) } } - /* - * cfg80211 maintains separate BSS table entries for APs if the same - * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does - * not use frequency as a separate key in the BSS table, so filter out - * duplicated entries. Prefer associated BSS entry in such a case in - * order to get the correct frequency into the BSS table. Similarly, - * prefer newer entries over older. - */ - for (i = 0; i < res->num; i++) { - const u8 *s1, *s2; - if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0) - continue; + if (bss[NL80211_BSS_PARENT_TSF] && bss[NL80211_BSS_PARENT_BSSID]) { + r->parent_tsf = nla_get_u64(bss[NL80211_BSS_PARENT_TSF]); + os_memcpy(r->tsf_bssid, nla_data(bss[NL80211_BSS_PARENT_BSSID]), + ETH_ALEN); + } - s1 = get_ie((u8 *) (res->res[i] + 1), - res->res[i]->ie_len, WLAN_EID_SSID); - s2 = get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID); - if (s1 == NULL || s2 == NULL || s1[1] != s2[1] || - os_memcmp(s1, s2, 2 + s1[1]) != 0) - continue; + return r; +} - /* Same BSSID,SSID was already included in scan results */ - wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result " - "for " MACSTR, MAC2STR(r->bssid)); - if (((r->flags & WPA_SCAN_ASSOCIATED) && - !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) || - r->age < res->res[i]->age) { - os_free(res->res[i]); - res->res[i] = r; - } else - os_free(r); +struct nl80211_bss_info_arg { + struct wpa_driver_nl80211_data *drv; + struct wpa_scan_results *res; +}; + +static int bss_info_handler(struct nl_msg *msg, void *arg) +{ + struct nl80211_bss_info_arg *_arg = arg; + struct wpa_scan_results *res = _arg->res; + struct wpa_scan_res **tmp; + struct wpa_scan_res *r; + + r = nl80211_parse_bss_info(_arg->drv, msg); + if (!r) return NL_SKIP; - } + if (!res) { + os_free(r); + return NL_SKIP; + } tmp = os_realloc_array(res->res, res->num + 1, sizeof(struct wpa_scan_res *)); if (tmp == NULL) { @@ -750,7 +866,32 @@ static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv, "mismatch (" MACSTR ")", MAC2STR(addr)); wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE, - WLAN_REASON_PREV_AUTH_NOT_VALID, 1); + WLAN_REASON_PREV_AUTH_NOT_VALID, 1, + NULL); + } +} + + +static void nl80211_check_bss_status(struct wpa_driver_nl80211_data *drv, + struct wpa_scan_res *r) +{ + if (!(r->flags & WPA_SCAN_ASSOCIATED)) + return; + + wpa_printf(MSG_DEBUG, "nl80211: Scan results indicate BSS status with " + MACSTR " as associated", MAC2STR(r->bssid)); + if (is_sta_interface(drv->nlmode) && !drv->associated) { + wpa_printf(MSG_DEBUG, + "nl80211: Local state (not associated) does not match with BSS state"); + clear_state_mismatch(drv, r->bssid); + } else if (is_sta_interface(drv->nlmode) && + os_memcmp(drv->bssid, r->bssid, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, + "nl80211: Local state (associated with " MACSTR + ") does not match with BSS state", + MAC2STR(drv->bssid)); + clear_state_mismatch(drv, r->bssid); + clear_state_mismatch(drv, drv->bssid); } } @@ -760,31 +901,22 @@ static void wpa_driver_nl80211_check_bss_status( { size_t i; - for (i = 0; i < res->num; i++) { - struct wpa_scan_res *r = res->res[i]; - - if (r->flags & WPA_SCAN_ASSOCIATED) { - wpa_printf(MSG_DEBUG, "nl80211: Scan results " - "indicate BSS status with " MACSTR - " as associated", - MAC2STR(r->bssid)); - if (is_sta_interface(drv->nlmode) && - !drv->associated) { - wpa_printf(MSG_DEBUG, "nl80211: Local state " - "(not associated) does not match " - "with BSS state"); - clear_state_mismatch(drv, r->bssid); - } else if (is_sta_interface(drv->nlmode) && - os_memcmp(drv->bssid, r->bssid, ETH_ALEN) != - 0) { - wpa_printf(MSG_DEBUG, "nl80211: Local state " - "(associated with " MACSTR ") does " - "not match with BSS state", - MAC2STR(drv->bssid)); - clear_state_mismatch(drv, r->bssid); - clear_state_mismatch(drv, drv->bssid); - } - } + for (i = 0; i < res->num; i++) + nl80211_check_bss_status(drv, res->res[i]); +} + + +static void nl80211_update_scan_res_noise(struct wpa_scan_res *res, + struct nl80211_noise_info *info) +{ + unsigned int i; + + for (i = 0; res && i < info->count; i++) { + if ((int) info->freq[i] != res->freq || + !(res->flags & WPA_SCAN_NOISE_INVALID)) + continue; + res->noise = info->noise[i]; + res->flags &= ~WPA_SCAN_NOISE_INVALID; } } @@ -810,9 +942,17 @@ nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv) arg.res = res; ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg); if (ret == 0) { + struct nl80211_noise_info info; + wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu " "BSSes)", (unsigned long) res->num); - nl80211_get_noise_for_scan_results(drv, res); + if (nl80211_get_noise_for_scan_results(drv, &info) == 0) { + size_t i; + + for (i = 0; i < res->num; ++i) + nl80211_update_scan_res_noise(res->res[i], + &info); + } return res; } wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " @@ -840,45 +980,57 @@ struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv) } -void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv) +struct nl80211_dump_scan_ctx { + struct wpa_driver_nl80211_data *drv; + int idx; +}; + +static int nl80211_dump_scan_handler(struct nl_msg *msg, void *arg) { - struct wpa_scan_results *res; - size_t i; + struct nl80211_dump_scan_ctx *ctx = arg; + struct wpa_scan_res *r; - res = nl80211_get_scan_results(drv); - if (res == NULL) { - wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results"); - return; - } + r = nl80211_parse_bss_info(ctx->drv, msg); + if (!r) + return NL_SKIP; + wpa_printf(MSG_DEBUG, "nl80211: %d " MACSTR " %d%s", + ctx->idx, MAC2STR(r->bssid), r->freq, + r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : ""); + ctx->idx++; + os_free(r); + return NL_SKIP; +} - wpa_printf(MSG_DEBUG, "nl80211: Scan result dump"); - for (i = 0; i < res->num; i++) { - struct wpa_scan_res *r = res->res[i]; - wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s", - (int) i, (int) res->num, MAC2STR(r->bssid), - r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : ""); - } - wpa_scan_results_free(res); +void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv) +{ + struct nl_msg *msg; + struct nl80211_dump_scan_ctx ctx; + + wpa_printf(MSG_DEBUG, "nl80211: Scan result dump"); + ctx.drv = drv; + ctx.idx = 0; + msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP, NL80211_CMD_GET_SCAN); + if (msg) + send_and_recv_msgs(drv, msg, nl80211_dump_scan_handler, &ctx); } -int wpa_driver_nl80211_abort_scan(void *priv) +int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie) { struct i802_bss *bss = priv; +#ifdef CONFIG_DRIVER_NL80211_QCA struct wpa_driver_nl80211_data *drv = bss->drv; - int ret; - struct nl_msg *msg; - - wpa_printf(MSG_DEBUG, "nl80211: Abort scan"); - msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ABORT_SCAN); - ret = send_and_recv_msgs(drv, msg, NULL, NULL); - if (ret) { - wpa_printf(MSG_DEBUG, "nl80211: Abort scan failed: ret=%d (%s)", - ret, strerror(-ret)); - } - return ret; + /* + * If scan_cookie is zero, a normal scan through kernel (cfg80211) + * was triggered, hence abort the cfg80211 scan instead of the vendor + * scan. + */ + if (drv->scan_vendor_cmd_avail && scan_cookie) + return nl80211_abort_vendor_scan(drv, scan_cookie); +#endif /* CONFIG_DRIVER_NL80211_QCA */ + return nl80211_abort_scan(bss); } @@ -1015,7 +1167,7 @@ int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, } if (scan_flags && - nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags)) + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS, scan_flags)) goto fail; if (params->p2p_probe) { @@ -1043,6 +1195,14 @@ int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, goto fail; } + if (params->bssid) { + wpa_printf(MSG_DEBUG, "nl80211: Scan for a specific BSSID: " + MACSTR, MAC2STR(params->bssid)); + if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_BSSID, ETH_ALEN, + params->bssid)) + goto fail; + } + nla_nest_end(msg, attr); ret = send_and_recv_msgs(drv, msg, scan_cookie_handler, &cookie); @@ -1056,6 +1216,8 @@ int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss, drv->vendor_scan_cookie = cookie; drv->scan_state = SCAN_REQUESTED; + /* Pass the cookie to the caller to help distinguish the scans. */ + params->scan_cookie = cookie; wpa_printf(MSG_DEBUG, "nl80211: Vendor scan requested (ret=%d) - scan timeout 30 seconds, scan cookie:0x%llx", diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c index 43d41937d474c..a3f0837e15696 100644 --- a/src/drivers/driver_privsep.c +++ b/src/drivers/driver_privsep.c @@ -97,15 +97,31 @@ static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd, return 0; } - + static int wpa_driver_privsep_scan(void *priv, struct wpa_driver_scan_params *params) { struct wpa_driver_privsep_data *drv = priv; - const u8 *ssid = params->ssids[0].ssid; - size_t ssid_len = params->ssids[0].ssid_len; + struct privsep_cmd_scan scan; + size_t i; + wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv); - return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, ssid, ssid_len, + os_memset(&scan, 0, sizeof(scan)); + scan.num_ssids = params->num_ssids; + for (i = 0; i < params->num_ssids; i++) { + if (!params->ssids[i].ssid) + continue; + scan.ssid_lens[i] = params->ssids[i].ssid_len; + os_memcpy(scan.ssids[i], params->ssids[i].ssid, + scan.ssid_lens[i]); + } + + for (i = 0; i < PRIVSEP_MAX_SCAN_FREQS && + params->freqs && params->freqs[i]; i++) + scan.freqs[i] = params->freqs[i]; + scan.num_freqs = i; + + return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, &scan, sizeof(scan), NULL, NULL); } @@ -168,12 +184,15 @@ wpa_driver_privsep_get_scan_results2(void *priv) if (len < 0 || len > 10000 || len > end - pos) break; - r = os_malloc(len); + r = os_memdup(pos, len); if (r == NULL) break; - os_memcpy(r, pos, len); pos += len; - if (sizeof(*r) + r->ie_len > (size_t) len) { + if (sizeof(*r) + r->ie_len + r->beacon_ie_len > (size_t) len) { + wpa_printf(MSG_ERROR, + "privsep: Invalid scan result len (%d + %d + %d > %d)", + (int) sizeof(*r), (int) r->ie_len, + (int) r->beacon_ie_len, len); os_free(r); break; } @@ -234,7 +253,7 @@ static int wpa_driver_privsep_authenticate( __func__, priv, params->freq, MAC2STR(params->bssid), params->auth_alg, params->local_state_change, params->p2p); - buflen = sizeof(*data) + params->ie_len + params->sae_data_len; + buflen = sizeof(*data) + params->ie_len + params->auth_data_len; data = os_zalloc(buflen); if (data == NULL) return -1; @@ -259,8 +278,8 @@ static int wpa_driver_privsep_authenticate( os_memcpy(pos, params->ie, params->ie_len); pos += params->ie_len; } - if (params->sae_data_len) - os_memcpy(pos, params->sae_data, params->sae_data_len); + if (params->auth_data_len) + os_memcpy(pos, params->auth_data, params->auth_data_len); res = wpa_priv_cmd(drv, PRIVSEP_CMD_AUTHENTICATE, data, buflen, NULL, NULL); @@ -464,19 +483,6 @@ static void wpa_driver_privsep_event_pmkid_candidate(void *ctx, u8 *buf, } -static void wpa_driver_privsep_event_stkstart(void *ctx, u8 *buf, size_t len) -{ - union wpa_event_data data; - - if (len != ETH_ALEN) - return; - - os_memset(&data, 0, sizeof(data)); - os_memcpy(data.stkstart.peer, buf, ETH_ALEN); - wpa_supplicant_event(ctx, EVENT_STKSTART, &data); -} - - static void wpa_driver_privsep_event_ft_response(void *ctx, u8 *buf, size_t len) { @@ -570,10 +576,6 @@ static void wpa_driver_privsep_receive(int sock, void *eloop_ctx, wpa_driver_privsep_event_pmkid_candidate(drv->ctx, event_buf, event_len); break; - case PRIVSEP_EVENT_STKSTART: - wpa_driver_privsep_event_stkstart(drv->ctx, event_buf, - event_len); - break; case PRIVSEP_EVENT_FT_RESPONSE: wpa_driver_privsep_event_ft_response(drv->ctx, event_buf, event_len); diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c index 791cd5d435d00..20abaab4cd84c 100644 --- a/src/drivers/driver_wext.c +++ b/src/drivers/driver_wext.c @@ -290,15 +290,6 @@ wpa_driver_wext_event_wireless_custom(void *ctx, char *custom) done: os_free(resp_ies); os_free(req_ies); -#ifdef CONFIG_PEERKEY - } else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) { - if (hwaddr_aton(custom + 17, data.stkstart.peer)) { - wpa_printf(MSG_DEBUG, "WEXT: unrecognized " - "STKSTART.request '%s'", custom + 17); - return; - } - wpa_supplicant_event(ctx, EVENT_STKSTART, &data); -#endif /* CONFIG_PEERKEY */ } } @@ -362,12 +353,11 @@ static int wpa_driver_wext_event_wireless_assocreqie( wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev, len); os_free(drv->assoc_req_ies); - drv->assoc_req_ies = os_malloc(len); + drv->assoc_req_ies = os_memdup(ev, len); if (drv->assoc_req_ies == NULL) { drv->assoc_req_ies_len = 0; return -1; } - os_memcpy(drv->assoc_req_ies, ev, len); drv->assoc_req_ies_len = len; return 0; @@ -383,12 +373,11 @@ static int wpa_driver_wext_event_wireless_assocrespie( wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev, len); os_free(drv->assoc_resp_ies); - drv->assoc_resp_ies = os_malloc(len); + drv->assoc_resp_ies = os_memdup(ev, len); if (drv->assoc_resp_ies == NULL) { drv->assoc_resp_ies_len = 0; return -1; } - os_memcpy(drv->assoc_resp_ies, ev, len); drv->assoc_resp_ies_len = len; return 0; @@ -472,7 +461,7 @@ static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, drv->assoc_resp_ies = NULL; wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); - + } else { wpa_driver_wext_event_assoc_ies(drv); wpa_supplicant_event(drv->ctx, EVENT_ASSOC, @@ -933,7 +922,7 @@ static int wext_add_hostap(struct wpa_driver_wext_data *drv) static void wext_check_hostap(struct wpa_driver_wext_data *drv) { - char buf[200], *pos; + char path[200], buf[200], *pos; ssize_t res; /* @@ -948,9 +937,9 @@ static void wext_check_hostap(struct wpa_driver_wext_data *drv) */ /* First, try to see if driver information is available from sysfs */ - snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/driver", + snprintf(path, sizeof(path), "/sys/class/net/%s/device/driver", drv->ifname); - res = readlink(buf, buf, sizeof(buf) - 1); + res = readlink(path, buf, sizeof(buf) - 1); if (res > 0) { buf[res] = '\0'; pos = strrchr(buf, '/'); @@ -1042,6 +1031,7 @@ void wpa_driver_wext_deinit(void *priv) wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 0); eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx); + eloop_cancel_timeout(wpa_driver_wext_send_rfkill, drv, drv->ctx); /* * Clear possibly configured driver parameters in order to make it @@ -2352,19 +2342,21 @@ static int wpa_driver_wext_pmksa(struct wpa_driver_wext_data *drv, } -static int wpa_driver_wext_add_pmkid(void *priv, const u8 *bssid, - const u8 *pmkid) +static int wpa_driver_wext_add_pmkid(void *priv, + struct wpa_pmkid_params *params) { struct wpa_driver_wext_data *drv = priv; - return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid); + return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, params->bssid, + params->pmkid); } -static int wpa_driver_wext_remove_pmkid(void *priv, const u8 *bssid, - const u8 *pmkid) +static int wpa_driver_wext_remove_pmkid(void *priv, + struct wpa_pmkid_params *params) { struct wpa_driver_wext_data *drv = priv; - return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid); + return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, params->bssid, + params->pmkid); } @@ -2436,8 +2428,8 @@ static int wpa_driver_wext_signal_poll(void *priv, struct wpa_signal_info *si) struct iwreq iwr; os_memset(si, 0, sizeof(*si)); - si->current_signal = -9999; - si->current_noise = 9999; + si->current_signal = -WPA_INVALID_NOISE; + si->current_noise = WPA_INVALID_NOISE; si->chanwidth = CHAN_WIDTH_UNKNOWN; os_memset(&iwr, 0, sizeof(iwr)); diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c index 422a22064ebe2..c7537b7c35eb5 100644 --- a/src/drivers/driver_wired.c +++ b/src/drivers/driver_wired.c @@ -12,6 +12,7 @@ #include "common.h" #include "eloop.h" #include "driver.h" +#include "driver_wired_common.h" #include <sys/ioctl.h> #undef IFNAMSIZ @@ -42,20 +43,12 @@ struct ieee8023_hdr { #pragma pack(pop) #endif /* _MSC_VER */ -static const u8 pae_group_addr[ETH_ALEN] = -{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; - struct wpa_driver_wired_data { - char ifname[IFNAMSIZ + 1]; - void *ctx; + struct driver_wired_common_data common; - int sock; /* raw packet socket for driver access */ int dhcp_sock; /* socket for dhcp packets */ int use_pae_group_addr; - - int pf_sock; - int membership, multi, iff_allmulti, iff_up; }; @@ -83,34 +76,6 @@ struct dhcp_message { }; -static int wired_multicast_membership(int sock, int ifindex, - const u8 *addr, int add) -{ -#ifdef __linux__ - struct packet_mreq mreq; - - if (sock < 0) - return -1; - - os_memset(&mreq, 0, sizeof(mreq)); - mreq.mr_ifindex = ifindex; - mreq.mr_type = PACKET_MR_MULTICAST; - mreq.mr_alen = ETH_ALEN; - os_memcpy(mreq.mr_address, addr, ETH_ALEN); - - if (setsockopt(sock, SOL_PACKET, - add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, - &mreq, sizeof(mreq)) < 0) { - wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno)); - return -1; - } - return 0; -#else /* __linux__ */ - return -1; -#endif /* __linux__ */ -} - - #ifdef __linux__ static void handle_data(void *ctx, unsigned char *buf, size_t len) { @@ -131,16 +96,16 @@ static void handle_data(void *ctx, unsigned char *buf, size_t len) hdr = (struct ieee8023_hdr *) buf; switch (ntohs(hdr->ethertype)) { - case ETH_P_PAE: - wpa_printf(MSG_MSGDUMP, "Received EAPOL packet"); - sa = hdr->src; - os_memset(&event, 0, sizeof(event)); - event.new_sta.addr = sa; - wpa_supplicant_event(ctx, EVENT_NEW_STA, &event); - - pos = (u8 *) (hdr + 1); - left = len - sizeof(*hdr); - drv_event_eapol_rx(ctx, sa, pos, left); + case ETH_P_PAE: + wpa_printf(MSG_MSGDUMP, "Received EAPOL packet"); + sa = hdr->src; + os_memset(&event, 0, sizeof(event)); + event.new_sta.addr = sa; + wpa_supplicant_event(ctx, EVENT_NEW_STA, &event); + + pos = (u8 *) (hdr + 1); + left = len - sizeof(*hdr); + drv_event_eapol_rx(ctx, sa, pos, left); break; default: @@ -208,21 +173,22 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) struct sockaddr_in addr2; int n = 1; - drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE)); - if (drv->sock < 0) { + drv->common.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE)); + if (drv->common.sock < 0) { wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s", strerror(errno)); return -1; } - if (eloop_register_read_sock(drv->sock, handle_read, drv->ctx, NULL)) { + if (eloop_register_read_sock(drv->common.sock, handle_read, + drv->common.ctx, NULL)) { wpa_printf(MSG_INFO, "Could not register read socket"); return -1; } os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); - if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { + os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name)); + if (ioctl(drv->common.sock, SIOCGIFINDEX, &ifr) != 0) { wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", strerror(errno)); return -1; @@ -234,13 +200,14 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", addr.sll_ifindex); - if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + if (bind(drv->common.sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) + { wpa_printf(MSG_ERROR, "bind: %s", strerror(errno)); return -1; } /* filter multicast address */ - if (wired_multicast_membership(drv->sock, ifr.ifr_ifindex, + if (wired_multicast_membership(drv->common.sock, ifr.ifr_ifindex, pae_group_addr, 1) < 0) { wpa_printf(MSG_ERROR, "wired: Failed to add multicast group " "membership"); @@ -248,8 +215,8 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) } os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); - if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) { + os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name)); + if (ioctl(drv->common.sock, SIOCGIFHWADDR, &ifr) != 0) { wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s", strerror(errno)); return -1; @@ -269,8 +236,8 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) return -1; } - if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, drv->ctx, - NULL)) { + if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, + drv->common.ctx, NULL)) { wpa_printf(MSG_INFO, "Could not register read socket"); return -1; } @@ -294,7 +261,7 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) } os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->ifname, IFNAMSIZ); + os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->common.ifname, IFNAMSIZ); if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE, (char *) &ifr, sizeof(ifr)) < 0) { wpa_printf(MSG_ERROR, @@ -343,7 +310,7 @@ static int wired_send_eapol(void *priv, const u8 *addr, pos = (u8 *) (hdr + 1); os_memcpy(pos, data, data_len); - res = send(drv->sock, (u8 *) hdr, len, 0); + res = send(drv->common.sock, (u8 *) hdr, len, 0); os_free(hdr); if (res < 0) { @@ -368,8 +335,9 @@ static void * wired_driver_hapd_init(struct hostapd_data *hapd, return NULL; } - drv->ctx = hapd; - os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname)); + drv->common.ctx = hapd; + os_strlcpy(drv->common.ifname, params->ifname, + sizeof(drv->common.ifname)); drv->use_pae_group_addr = params->use_pae_group_addr; if (wired_init_sockets(drv, params->own_addr)) { @@ -385,9 +353,9 @@ static void wired_driver_hapd_deinit(void *priv) { struct wpa_driver_wired_data *drv = priv; - if (drv->sock >= 0) { - eloop_unregister_read_sock(drv->sock); - close(drv->sock); + if (drv->common.sock >= 0) { + eloop_unregister_read_sock(drv->common.sock); + close(drv->common.sock); } if (drv->dhcp_sock >= 0) { @@ -399,227 +367,18 @@ static void wired_driver_hapd_deinit(void *priv) } -static int wpa_driver_wired_get_ssid(void *priv, u8 *ssid) -{ - ssid[0] = 0; - return 0; -} - - -static int wpa_driver_wired_get_bssid(void *priv, u8 *bssid) -{ - /* Report PAE group address as the "BSSID" for wired connection. */ - os_memcpy(bssid, pae_group_addr, ETH_ALEN); - return 0; -} - - -static int wpa_driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa) -{ - os_memset(capa, 0, sizeof(*capa)); - capa->flags = WPA_DRIVER_FLAGS_WIRED; - return 0; -} - - -static int wpa_driver_wired_get_ifflags(const char *ifname, int *flags) -{ - struct ifreq ifr; - int s; - - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s < 0) { - wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); - return -1; - } - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s", - strerror(errno)); - close(s); - return -1; - } - close(s); - *flags = ifr.ifr_flags & 0xffff; - return 0; -} - - -static int wpa_driver_wired_set_ifflags(const char *ifname, int flags) -{ - struct ifreq ifr; - int s; - - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s < 0) { - wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); - return -1; - } - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); - ifr.ifr_flags = flags & 0xffff; - if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s", - strerror(errno)); - close(s); - return -1; - } - close(s); - return 0; -} - - -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) -static int wpa_driver_wired_get_ifstatus(const char *ifname, int *status) -{ - struct ifmediareq ifmr; - int s; - - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s < 0) { - wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); - return -1; - } - - os_memset(&ifmr, 0, sizeof(ifmr)); - os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ); - if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s", - strerror(errno)); - close(s); - return -1; - } - close(s); - *status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) == - (IFM_ACTIVE | IFM_AVALID); - - return 0; -} -#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ - - -static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) -{ - struct ifreq ifr; - int s; - -#ifdef __sun__ - return -1; -#endif /* __sun__ */ - - s = socket(PF_INET, SOCK_DGRAM, 0); - if (s < 0) { - wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); - return -1; - } - - os_memset(&ifr, 0, sizeof(ifr)); - os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); -#ifdef __linux__ - ifr.ifr_hwaddr.sa_family = AF_UNSPEC; - os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); -#endif /* __linux__ */ -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) - { - struct sockaddr_dl *dlp; - dlp = (struct sockaddr_dl *) &ifr.ifr_addr; - dlp->sdl_len = sizeof(struct sockaddr_dl); - dlp->sdl_family = AF_LINK; - dlp->sdl_index = 0; - dlp->sdl_nlen = 0; - dlp->sdl_alen = ETH_ALEN; - dlp->sdl_slen = 0; - os_memcpy(LLADDR(dlp), addr, ETH_ALEN); - } -#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ -#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) - { - struct sockaddr *sap; - sap = (struct sockaddr *) &ifr.ifr_addr; - sap->sa_len = sizeof(struct sockaddr); - sap->sa_family = AF_UNSPEC; - os_memcpy(sap->sa_data, addr, ETH_ALEN); - } -#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */ - - if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) { - wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s", - strerror(errno)); - close(s); - return -1; - } - close(s); - return 0; -} - - static void * wpa_driver_wired_init(void *ctx, const char *ifname) { struct wpa_driver_wired_data *drv; - int flags; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) return NULL; - os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); - drv->ctx = ctx; - -#ifdef __linux__ - drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0); - if (drv->pf_sock < 0) - wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno)); -#else /* __linux__ */ - drv->pf_sock = -1; -#endif /* __linux__ */ - if (wpa_driver_wired_get_ifflags(ifname, &flags) == 0 && - !(flags & IFF_UP) && - wpa_driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) { - drv->iff_up = 1; - } - - if (wired_multicast_membership(drv->pf_sock, - if_nametoindex(drv->ifname), - pae_group_addr, 1) == 0) { - wpa_printf(MSG_DEBUG, "%s: Added multicast membership with " - "packet socket", __func__); - drv->membership = 1; - } else if (wpa_driver_wired_multi(ifname, pae_group_addr, 1) == 0) { - wpa_printf(MSG_DEBUG, "%s: Added multicast membership with " - "SIOCADDMULTI", __func__); - drv->multi = 1; - } else if (wpa_driver_wired_get_ifflags(ifname, &flags) < 0) { - wpa_printf(MSG_INFO, "%s: Could not get interface " - "flags", __func__); - os_free(drv); - return NULL; - } else if (flags & IFF_ALLMULTI) { - wpa_printf(MSG_DEBUG, "%s: Interface is already configured " - "for multicast", __func__); - } else if (wpa_driver_wired_set_ifflags(ifname, - flags | IFF_ALLMULTI) < 0) { - wpa_printf(MSG_INFO, "%s: Failed to enable allmulti", - __func__); + if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) { os_free(drv); return NULL; - } else { - wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", - __func__); - drv->iff_allmulti = 1; } -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) - { - int status; - wpa_printf(MSG_DEBUG, "%s: waiting for link to become active", - __func__); - while (wpa_driver_wired_get_ifstatus(ifname, &status) == 0 && - status == 0) - sleep(1); - } -#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ return drv; } @@ -628,41 +387,8 @@ static void * wpa_driver_wired_init(void *ctx, const char *ifname) static void wpa_driver_wired_deinit(void *priv) { struct wpa_driver_wired_data *drv = priv; - int flags; - - if (drv->membership && - wired_multicast_membership(drv->pf_sock, - if_nametoindex(drv->ifname), - pae_group_addr, 0) < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast " - "group (PACKET)", __func__); - } - - if (drv->multi && - wpa_driver_wired_multi(drv->ifname, pae_group_addr, 0) < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast " - "group (SIOCDELMULTI)", __func__); - } - - if (drv->iff_allmulti && - (wpa_driver_wired_get_ifflags(drv->ifname, &flags) < 0 || - wpa_driver_wired_set_ifflags(drv->ifname, - flags & ~IFF_ALLMULTI) < 0)) { - wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode", - __func__); - } - - if (drv->iff_up && - wpa_driver_wired_get_ifflags(drv->ifname, &flags) == 0 && - (flags & IFF_UP) && - wpa_driver_wired_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) { - wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down", - __func__); - } - - if (drv->pf_sock != -1) - close(drv->pf_sock); + driver_wired_deinit_common(&drv->common); os_free(drv); } @@ -673,9 +399,9 @@ const struct wpa_driver_ops wpa_driver_wired_ops = { .hapd_init = wired_driver_hapd_init, .hapd_deinit = wired_driver_hapd_deinit, .hapd_send_eapol = wired_send_eapol, - .get_ssid = wpa_driver_wired_get_ssid, - .get_bssid = wpa_driver_wired_get_bssid, - .get_capa = wpa_driver_wired_get_capa, + .get_ssid = driver_wired_get_ssid, + .get_bssid = driver_wired_get_bssid, + .get_capa = driver_wired_get_capa, .init = wpa_driver_wired_init, .deinit = wpa_driver_wired_deinit, }; diff --git a/src/drivers/driver_wired_common.c b/src/drivers/driver_wired_common.c new file mode 100644 index 0000000000000..a860b1c7db358 --- /dev/null +++ b/src/drivers/driver_wired_common.c @@ -0,0 +1,322 @@ +/* + * Common functions for Wired Ethernet driver interfaces + * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004, Gunter Burchardt <tira@isx.de> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "driver.h" +#include "driver_wired_common.h" + +#include <sys/ioctl.h> +#include <net/if.h> +#ifdef __linux__ +#include <netpacket/packet.h> +#include <net/if_arp.h> +#include <net/if.h> +#endif /* __linux__ */ +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +#include <net/if_dl.h> +#include <net/if_media.h> +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */ +#ifdef __sun__ +#include <sys/sockio.h> +#endif /* __sun__ */ + + +static int driver_wired_get_ifflags(const char *ifname, int *flags) +{ + struct ifreq ifr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s", + strerror(errno)); + close(s); + return -1; + } + close(s); + *flags = ifr.ifr_flags & 0xffff; + return 0; +} + + +static int driver_wired_set_ifflags(const char *ifname, int flags) +{ + struct ifreq ifr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags = flags & 0xffff; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s", + strerror(errno)); + close(s); + return -1; + } + close(s); + return 0; +} + + +static int driver_wired_multi(const char *ifname, const u8 *addr, int add) +{ + struct ifreq ifr; + int s; + +#ifdef __sun__ + return -1; +#endif /* __sun__ */ + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); +#ifdef __linux__ + ifr.ifr_hwaddr.sa_family = AF_UNSPEC; + os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); +#endif /* __linux__ */ +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) + { + struct sockaddr_dl *dlp; + + dlp = (struct sockaddr_dl *) &ifr.ifr_addr; + dlp->sdl_len = sizeof(struct sockaddr_dl); + dlp->sdl_family = AF_LINK; + dlp->sdl_index = 0; + dlp->sdl_nlen = 0; + dlp->sdl_alen = ETH_ALEN; + dlp->sdl_slen = 0; + os_memcpy(LLADDR(dlp), addr, ETH_ALEN); + } +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ +#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) + { + struct sockaddr *sap; + + sap = (struct sockaddr *) &ifr.ifr_addr; + sap->sa_len = sizeof(struct sockaddr); + sap->sa_family = AF_UNSPEC; + os_memcpy(sap->sa_data, addr, ETH_ALEN); + } +#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */ + + if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s", + strerror(errno)); + close(s); + return -1; + } + close(s); + return 0; +} + + +int wired_multicast_membership(int sock, int ifindex, const u8 *addr, int add) +{ +#ifdef __linux__ + struct packet_mreq mreq; + + if (sock < 0) + return -1; + + os_memset(&mreq, 0, sizeof(mreq)); + mreq.mr_ifindex = ifindex; + mreq.mr_type = PACKET_MR_MULTICAST; + mreq.mr_alen = ETH_ALEN; + os_memcpy(mreq.mr_address, addr, ETH_ALEN); + + if (setsockopt(sock, SOL_PACKET, + add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno)); + return -1; + } + return 0; +#else /* __linux__ */ + return -1; +#endif /* __linux__ */ +} + + +int driver_wired_get_ssid(void *priv, u8 *ssid) +{ + ssid[0] = 0; + return 0; +} + + +int driver_wired_get_bssid(void *priv, u8 *bssid) +{ + /* Report PAE group address as the "BSSID" for wired connection. */ + os_memcpy(bssid, pae_group_addr, ETH_ALEN); + return 0; +} + + +int driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + os_memset(capa, 0, sizeof(*capa)); + capa->flags = WPA_DRIVER_FLAGS_WIRED; + return 0; +} + + +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) +static int driver_wired_get_ifstatus(const char *ifname, int *status) +{ + struct ifmediareq ifmr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, "socket: %s", strerror(errno)); + return -1; + } + + os_memset(&ifmr, 0, sizeof(ifmr)); + os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ); + if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) { + wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s", + strerror(errno)); + close(s); + return -1; + } + close(s); + *status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID); + + return 0; +} +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ + + +int driver_wired_init_common(struct driver_wired_common_data *common, + const char *ifname, void *ctx) +{ + int flags; + + os_strlcpy(common->ifname, ifname, sizeof(common->ifname)); + common->ctx = ctx; + +#ifdef __linux__ + common->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0); + if (common->pf_sock < 0) + wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno)); +#else /* __linux__ */ + common->pf_sock = -1; +#endif /* __linux__ */ + + if (driver_wired_get_ifflags(ifname, &flags) == 0 && + !(flags & IFF_UP) && + driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) + common->iff_up = 1; + + if (wired_multicast_membership(common->pf_sock, + if_nametoindex(common->ifname), + pae_group_addr, 1) == 0) { + wpa_printf(MSG_DEBUG, + "%s: Added multicast membership with packet socket", + __func__); + common->membership = 1; + } else if (driver_wired_multi(ifname, pae_group_addr, 1) == 0) { + wpa_printf(MSG_DEBUG, + "%s: Added multicast membership with SIOCADDMULTI", + __func__); + common->multi = 1; + } else if (driver_wired_get_ifflags(ifname, &flags) < 0) { + wpa_printf(MSG_INFO, "%s: Could not get interface flags", + __func__); + return -1; + } else if (flags & IFF_ALLMULTI) { + wpa_printf(MSG_DEBUG, + "%s: Interface is already configured for multicast", + __func__); + } else if (driver_wired_set_ifflags(ifname, + flags | IFF_ALLMULTI) < 0) { + wpa_printf(MSG_INFO, "%s: Failed to enable allmulti", __func__); + return -1; + } else { + wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", __func__); + common->iff_allmulti = 1; + } +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) + { + int status; + + wpa_printf(MSG_DEBUG, "%s: waiting for link to become active", + __func__); + while (driver_wired_get_ifstatus(ifname, &status) == 0 && + status == 0) + sleep(1); + } +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ + + return 0; +} + + +void driver_wired_deinit_common(struct driver_wired_common_data *common) +{ + int flags; + + if (common->membership && + wired_multicast_membership(common->pf_sock, + if_nametoindex(common->ifname), + pae_group_addr, 0) < 0) { + wpa_printf(MSG_DEBUG, + "%s: Failed to remove PAE multicast group (PACKET)", + __func__); + } + + if (common->multi && + driver_wired_multi(common->ifname, pae_group_addr, 0) < 0) { + wpa_printf(MSG_DEBUG, + "%s: Failed to remove PAE multicast group (SIOCDELMULTI)", + __func__); + } + + if (common->iff_allmulti && + (driver_wired_get_ifflags(common->ifname, &flags) < 0 || + driver_wired_set_ifflags(common->ifname, + flags & ~IFF_ALLMULTI) < 0)) { + wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode", + __func__); + } + + if (common->iff_up && + driver_wired_get_ifflags(common->ifname, &flags) == 0 && + (flags & IFF_UP) && + driver_wired_set_ifflags(common->ifname, flags & ~IFF_UP) < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down", + __func__); + } + + if (common->pf_sock != -1) + close(common->pf_sock); +} diff --git a/src/drivers/driver_wired_common.h b/src/drivers/driver_wired_common.h new file mode 100644 index 0000000000000..2bb0710bb2754 --- /dev/null +++ b/src/drivers/driver_wired_common.h @@ -0,0 +1,34 @@ +/* + * Common definitions for Wired Ethernet driver interfaces + * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004, Gunter Burchardt <tira@isx.de> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef DRIVER_WIRED_COMMON_H +#define DRIVER_WIRED_COMMON_H + +struct driver_wired_common_data { + char ifname[IFNAMSIZ + 1]; + void *ctx; + + int sock; /* raw packet socket for driver access */ + int pf_sock; + int membership, multi, iff_allmulti, iff_up; +}; + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + +int wired_multicast_membership(int sock, int ifindex, const u8 *addr, int add); +int driver_wired_get_ssid(void *priv, u8 *ssid); +int driver_wired_get_bssid(void *priv, u8 *bssid); +int driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa); + +int driver_wired_init_common(struct driver_wired_common_data *common, + const char *ifname, void *ctx); +void driver_wired_deinit_common(struct driver_wired_common_data *common); + +#endif /* DRIVER_WIRED_COMMON_H */ diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c index 00773a7113f69..e95df6ddb20aa 100644 --- a/src/drivers/drivers.c +++ b/src/drivers/drivers.c @@ -34,6 +34,9 @@ const struct wpa_driver_ops *const wpa_drivers[] = #ifdef CONFIG_DRIVER_WIRED &wpa_driver_wired_ops, #endif /* CONFIG_DRIVER_WIRED */ +#ifdef CONFIG_DRIVER_MACSEC_LINUX + &wpa_driver_macsec_linux_ops, +#endif /* CONFIG_DRIVER_MACSEC_LINUX */ #ifdef CONFIG_DRIVER_MACSEC_QCA &wpa_driver_macsec_qca_ops, #endif /* CONFIG_DRIVER_MACSEC_QCA */ diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak index c6d3f8181808a..1496b47d0ad50 100644 --- a/src/drivers/drivers.mak +++ b/src/drivers/drivers.mak @@ -15,11 +15,24 @@ DRV_AP_LIBS = ifdef CONFIG_DRIVER_WIRED DRV_CFLAGS += -DCONFIG_DRIVER_WIRED DRV_OBJS += ../src/drivers/driver_wired.o +NEED_DRV_WIRED_COMMON=1 +endif + +ifdef CONFIG_DRIVER_MACSEC_LINUX +DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX +DRV_OBJS += ../src/drivers/driver_macsec_linux.o +NEED_DRV_WIRED_COMMON=1 +CONFIG_LIBNL3_ROUTE=y endif ifdef CONFIG_DRIVER_MACSEC_QCA DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_QCA DRV_OBJS += ../src/drivers/driver_macsec_qca.o +NEED_DRV_WIRED_COMMON=1 +endif + +ifdef NEED_DRV_WIRED_COMMON +DRV_OBJS += ../src/drivers/driver_wired_common.o endif ifdef CONFIG_DRIVER_NL80211 diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk index c6fe4c20c561b..cd25133af8088 100644 --- a/src/drivers/drivers.mk +++ b/src/drivers/drivers.mk @@ -15,6 +15,18 @@ DRV_AP_LIBS = ifdef CONFIG_DRIVER_WIRED DRV_CFLAGS += -DCONFIG_DRIVER_WIRED DRV_OBJS += src/drivers/driver_wired.c +NEED_DRV_WIRED_COMMON=1 +endif + +ifdef CONFIG_DRIVER_MACSEC_LINUX +DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX +DRV_OBJS += src/drivers/driver_macsec_linux.c +NEED_DRV_WIRED_COMMON=1 +CONFIG_LIBNL3_ROUTE=y +endif + +ifdef NEED_DRV_WIRED_COMMON +DRV_OBJS += src/drivers/driver_wired_common.c endif ifdef CONFIG_DRIVER_NL80211 diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h index 2206941514345..1766a12b231c7 100644 --- a/src/drivers/nl80211_copy.h +++ b/src/drivers/nl80211_copy.h @@ -10,7 +10,8 @@ * Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com> * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com> * Copyright 2008 Colin McCabe <colin@cozybit.com> - * Copyright 2015 Intel Deutschland GmbH + * Copyright 2015-2017 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -48,6 +49,7 @@ #define NL80211_MULTICAST_GROUP_REG "regulatory" #define NL80211_MULTICAST_GROUP_MLME "mlme" #define NL80211_MULTICAST_GROUP_VENDOR "vendor" +#define NL80211_MULTICAST_GROUP_NAN "nan" #define NL80211_MULTICAST_GROUP_TESTMODE "testmode" /** @@ -172,6 +174,67 @@ */ /** + * DOC: WPA/WPA2 EAPOL handshake offload + * + * By setting @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK flag drivers + * can indicate they support offloading EAPOL handshakes for WPA/WPA2 + * preshared key authentication. In %NL80211_CMD_CONNECT the preshared + * key should be specified using %NL80211_ATTR_PMK. Drivers supporting + * this offload may reject the %NL80211_CMD_CONNECT when no preshared + * key material is provided, for example when that driver does not + * support setting the temporal keys through %CMD_NEW_KEY. + * + * Similarly @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X flag can be + * set by drivers indicating offload support of the PTK/GTK EAPOL + * handshakes during 802.1X authentication. In order to use the offload + * the %NL80211_CMD_CONNECT should have %NL80211_ATTR_WANT_1X_4WAY_HS + * attribute flag. Drivers supporting this offload may reject the + * %NL80211_CMD_CONNECT when the attribute flag is not present. + * + * For 802.1X the PMK or PMK-R0 are set by providing %NL80211_ATTR_PMK + * using %NL80211_CMD_SET_PMK. For offloaded FT support also + * %NL80211_ATTR_PMKR0_NAME must be provided. + */ + +/** + * DOC: FILS shared key authentication offload + * + * FILS shared key authentication offload can be advertized by drivers by + * setting @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD flag. The drivers that support + * FILS shared key authentication offload should be able to construct the + * authentication and association frames for FILS shared key authentication and + * eventually do a key derivation as per IEEE 802.11ai. The below additional + * parameters should be given to driver in %NL80211_CMD_CONNECT and/or in + * %NL80211_CMD_UPDATE_CONNECT_PARAMS. + * %NL80211_ATTR_FILS_ERP_USERNAME - used to construct keyname_nai + * %NL80211_ATTR_FILS_ERP_REALM - used to construct keyname_nai + * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used to construct erp message + * %NL80211_ATTR_FILS_ERP_RRK - used to generate the rIK and rMSK + * rIK should be used to generate an authentication tag on the ERP message and + * rMSK should be used to derive a PMKSA. + * rIK, rMSK should be generated and keyname_nai, sequence number should be used + * as specified in IETF RFC 6696. + * + * When FILS shared key authentication is completed, driver needs to provide the + * below additional parameters to userspace, which can be either after setting + * up a connection or after roaming. + * %NL80211_ATTR_FILS_KEK - used for key renewal + * %NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM - used in further EAP-RP exchanges + * %NL80211_ATTR_PMKID - used to identify the PMKSA used/generated + * %Nl80211_ATTR_PMK - used to update PMKSA cache in userspace + * The PMKSA can be maintained in userspace persistently so that it can be used + * later after reboots or wifi turn off/on also. + * + * %NL80211_ATTR_FILS_CACHE_ID is the cache identifier advertized by a FILS + * capable AP supporting PMK caching. It specifies the scope within which the + * PMKSAs are cached in an ESS. %NL80211_CMD_SET_PMKSA and + * %NL80211_CMD_DEL_PMKSA are enhanced to allow support for PMKSA caching based + * on FILS cache identifier. Additionally %NL80211_ATTR_PMK is used with + * %NL80211_SET_PMKSA to specify the PMK corresponding to a PMKSA for driver to + * use in a FILS shared key connection with PMKSA caching. + */ + +/** * enum nl80211_commands - supported nl80211 commands * * @NL80211_CMD_UNSPEC: unspecified command to catch errors @@ -322,7 +385,7 @@ * @NL80211_CMD_GET_SCAN: get scan results * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the - * probe requests at CCK rate or not. %NL80211_ATTR_MAC can be used to + * probe requests at CCK rate or not. %NL80211_ATTR_BSSID can be used to * specify a BSSID to scan for; if not included, the wildcard BSSID will * be used. * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to @@ -350,7 +413,9 @@ * are used. Extra IEs can also be passed from the userspace by * using the %NL80211_ATTR_IE attribute. The first cycle of the * scheduled scan can be delayed by %NL80211_ATTR_SCHED_SCAN_DELAY - * is supplied. + * is supplied. If the device supports multiple concurrent scheduled + * scans, it will allow such when the caller provides the flag attribute + * %NL80211_ATTR_SCHED_SCAN_MULTI to indicate user-space support for it. * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if * scheduled scan is not running. The caller may assume that as soon * as the call returns, it is safe to start a new scheduled scan again. @@ -369,10 +434,18 @@ * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to * NL80211_CMD_GET_SURVEY and on the "scan" multicast group) * - * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry, using %NL80211_ATTR_MAC - * (for the BSSID) and %NL80211_ATTR_PMKID. + * @NL80211_CMD_SET_PMKSA: Add a PMKSA cache entry using %NL80211_ATTR_MAC + * (for the BSSID), %NL80211_ATTR_PMKID, and optionally %NL80211_ATTR_PMK + * (PMK is used for PTKSA derivation in case of FILS shared key offload) or + * using %NL80211_ATTR_SSID, %NL80211_ATTR_FILS_CACHE_ID, + * %NL80211_ATTR_PMKID, and %NL80211_ATTR_PMK in case of FILS + * authentication where %NL80211_ATTR_FILS_CACHE_ID is the identifier + * advertized by a FILS capable AP identifying the scope of PMKSA in an + * ESS. * @NL80211_CMD_DEL_PMKSA: Delete a PMKSA cache entry, using %NL80211_ATTR_MAC - * (for the BSSID) and %NL80211_ATTR_PMKID. + * (for the BSSID) and %NL80211_ATTR_PMKID or using %NL80211_ATTR_SSID, + * %NL80211_ATTR_FILS_CACHE_ID, and %NL80211_ATTR_PMKID in case of FILS + * authentication. * @NL80211_CMD_FLUSH_PMKSA: Flush all PMKSA cache entries. * * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain @@ -472,7 +545,8 @@ * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP, * %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT, * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, - * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, %NL80211_ATTR_MAC_HINT, and + * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, + * %NL80211_ATTR_CONTROL_PORT_OVER_NL80211, %NL80211_ATTR_MAC_HINT, and * %NL80211_ATTR_WIPHY_FREQ_HINT. * If included, %NL80211_ATTR_MAC and %NL80211_ATTR_WIPHY_FREQ are * restrictions on BSS selection, i.e., they effectively prevent roaming @@ -499,8 +573,14 @@ * authentication/association or not receiving a response from the AP. * Non-zero %NL80211_ATTR_STATUS_CODE value is indicated in that case as * well to remain backwards compatible. - * @NL80211_CMD_ROAM: request that the card roam (currently not implemented), - * sent as an event when the card/driver roamed by itself. + * When establishing a security association, drivers that support 4 way + * handshake offload should send %NL80211_CMD_PORT_AUTHORIZED event when + * the 4 way handshake is completed successfully. + * @NL80211_CMD_ROAM: Notification indicating the card/driver roamed by itself. + * When a security association was established with the new AP (e.g. if + * the FT protocol was used for roaming or the driver completed the 4 way + * handshake), this event should be followed by an + * %NL80211_CMD_PORT_AUTHORIZED event. * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify * userspace that a connection was dropped by the AP or due to other * reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and @@ -599,6 +679,20 @@ * * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface. * + * @NL80211_CMD_SET_MULTICAST_TO_UNICAST: Configure if this AP should perform + * multicast to unicast conversion. When enabled, all multicast packets + * with ethertype ARP, IPv4 or IPv6 (possibly within an 802.1Q header) + * will be sent out to each station once with the destination (multicast) + * MAC address replaced by the station's MAC address. Note that this may + * break certain expectations of the receiver, e.g. the ability to drop + * unicast IP packets encapsulated in multicast L2 frames, or the ability + * to not send destination unreachable messages in such cases. + * This can only be toggled per BSS. Configure this on an interface of + * type %NL80211_IFTYPE_AP. It applies to all its VLAN interfaces + * (%NL80211_IFTYPE_AP_VLAN), except for those in 4addr (WDS) mode. + * If %NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED is not present with this + * command, the feature is disabled. + * * @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial * mesh config parameters may be given. * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the @@ -838,6 +932,107 @@ * not running. The driver indicates the status of the scan through * cfg80211_scan_done(). * + * @NL80211_CMD_START_NAN: Start NAN operation, identified by its + * %NL80211_ATTR_WDEV interface. This interface must have been + * previously created with %NL80211_CMD_NEW_INTERFACE. After it + * has been started, the NAN interface will create or join a + * cluster. This command must have a valid + * %NL80211_ATTR_NAN_MASTER_PREF attribute and optional + * %NL80211_ATTR_BANDS attributes. If %NL80211_ATTR_BANDS is + * omitted or set to 0, it means don't-care and the device will + * decide what to use. After this command NAN functions can be + * added. + * @NL80211_CMD_STOP_NAN: Stop the NAN operation, identified by + * its %NL80211_ATTR_WDEV interface. + * @NL80211_CMD_ADD_NAN_FUNCTION: Add a NAN function. The function is defined + * with %NL80211_ATTR_NAN_FUNC nested attribute. When called, this + * operation returns the strictly positive and unique instance id + * (%NL80211_ATTR_NAN_FUNC_INST_ID) and a cookie (%NL80211_ATTR_COOKIE) + * of the function upon success. + * Since instance ID's can be re-used, this cookie is the right + * way to identify the function. This will avoid races when a termination + * event is handled by the user space after it has already added a new + * function that got the same instance id from the kernel as the one + * which just terminated. + * This cookie may be used in NAN events even before the command + * returns, so userspace shouldn't process NAN events until it processes + * the response to this command. + * Look at %NL80211_ATTR_SOCKET_OWNER as well. + * @NL80211_CMD_DEL_NAN_FUNCTION: Delete a NAN function by cookie. + * This command is also used as a notification sent when a NAN function is + * terminated. This will contain a %NL80211_ATTR_NAN_FUNC_INST_ID + * and %NL80211_ATTR_COOKIE attributes. + * @NL80211_CMD_CHANGE_NAN_CONFIG: Change current NAN + * configuration. NAN must be operational (%NL80211_CMD_START_NAN + * was executed). It must contain at least one of the following + * attributes: %NL80211_ATTR_NAN_MASTER_PREF, + * %NL80211_ATTR_BANDS. If %NL80211_ATTR_BANDS is omitted, the + * current configuration is not changed. If it is present but + * set to zero, the configuration is changed to don't-care + * (i.e. the device can decide what to do). + * @NL80211_CMD_NAN_FUNC_MATCH: Notification sent when a match is reported. + * This will contain a %NL80211_ATTR_NAN_MATCH nested attribute and + * %NL80211_ATTR_COOKIE. + * + * @NL80211_CMD_UPDATE_CONNECT_PARAMS: Update one or more connect parameters + * for subsequent roaming cases if the driver or firmware uses internal + * BSS selection. This command can be issued only while connected and it + * does not result in a change for the current association. Currently, + * only the %NL80211_ATTR_IE data is used and updated with this command. + * + * @NL80211_CMD_SET_PMK: For offloaded 4-Way handshake, set the PMK or PMK-R0 + * for the given authenticator address (specified with %NL80211_ATTR_MAC). + * When %NL80211_ATTR_PMKR0_NAME is set, %NL80211_ATTR_PMK specifies the + * PMK-R0, otherwise it specifies the PMK. + * @NL80211_CMD_DEL_PMK: For offloaded 4-Way handshake, delete the previously + * configured PMK for the authenticator address identified by + * %NL80211_ATTR_MAC. + * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates that the 4 way + * handshake was completed successfully by the driver. The BSSID is + * specified with %NL80211_ATTR_MAC. Drivers that support 4 way handshake + * offload should send this event after indicating 802.11 association with + * %NL80211_CMD_CONNECT or %NL80211_CMD_ROAM. If the 4 way handshake failed + * %NL80211_CMD_DISCONNECT should be indicated instead. + * + * @NL80211_CMD_CONTROL_PORT_FRAME: Control Port (e.g. PAE) frame TX request + * and RX notification. This command is used both as a request to transmit + * a control port frame and as a notification that a control port frame + * has been received. %NL80211_ATTR_FRAME is used to specify the + * frame contents. The frame is the raw EAPoL data, without ethernet or + * 802.11 headers. + * When used as an event indication %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, + * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT and %NL80211_ATTR_MAC are added + * indicating the protocol type of the received frame; whether the frame + * was received unencrypted and the MAC address of the peer respectively. + * + * @NL80211_CMD_RELOAD_REGDB: Request that the regdb firmware file is reloaded. + * + * @NL80211_CMD_EXTERNAL_AUTH: This interface is exclusively defined for host + * drivers that do not define separate commands for authentication and + * association, but rely on user space for the authentication to happen. + * This interface acts both as the event request (driver to user space) + * to trigger the authentication and command response (userspace to + * driver) to indicate the authentication status. + * + * User space uses the %NL80211_CMD_CONNECT command to the host driver to + * trigger a connection. The host driver selects a BSS and further uses + * this interface to offload only the authentication part to the user + * space. Authentication frames are passed between the driver and user + * space through the %NL80211_CMD_FRAME interface. Host driver proceeds + * further with the association after getting successful authentication + * status. User space indicates the authentication status through + * %NL80211_ATTR_STATUS_CODE attribute in %NL80211_CMD_EXTERNAL_AUTH + * command interface. + * + * Host driver reports this status on an authentication failure to the + * user space through the connect result as the user space would have + * initiated the connection through the connect request. + * + * @NL80211_CMD_STA_OPMODE_CHANGED: An event that notify station's + * ht opmode or vht opmode changes using any of %NL80211_ATTR_SMPS_MODE, + * %NL80211_ATTR_CHANNEL_WIDTH,%NL80211_ATTR_NSS attributes with its + * address(specified in %NL80211_ATTR_MAC). + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1026,6 +1221,30 @@ enum nl80211_commands { NL80211_CMD_ABORT_SCAN, + NL80211_CMD_START_NAN, + NL80211_CMD_STOP_NAN, + NL80211_CMD_ADD_NAN_FUNCTION, + NL80211_CMD_DEL_NAN_FUNCTION, + NL80211_CMD_CHANGE_NAN_CONFIG, + NL80211_CMD_NAN_MATCH, + + NL80211_CMD_SET_MULTICAST_TO_UNICAST, + + NL80211_CMD_UPDATE_CONNECT_PARAMS, + + NL80211_CMD_SET_PMK, + NL80211_CMD_DEL_PMK, + + NL80211_CMD_PORT_AUTHORIZED, + + NL80211_CMD_RELOAD_REGDB, + + NL80211_CMD_EXTERNAL_AUTH, + + NL80211_CMD_STA_OPMODE_CHANGED, + + NL80211_CMD_CONTROL_PORT_FRAME, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1248,8 +1467,12 @@ enum nl80211_commands { * * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is * used for the association (&enum nl80211_mfp, represented as a u32); - * this attribute can be used - * with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests + * this attribute can be used with %NL80211_CMD_ASSOCIATE and + * %NL80211_CMD_CONNECT requests. %NL80211_MFP_OPTIONAL is not allowed for + * %NL80211_CMD_ASSOCIATE since user space SME is expected and hence, it + * must have decided whether to use management frame protection or not. + * Setting %NL80211_MFP_OPTIONAL with a %NL80211_CMD_CONNECT request will + * let the driver (or the firmware) decide whether to use MFP or not. * * @NL80211_ATTR_STA_FLAGS2: Attribute containing a * &struct nl80211_sta_flag_update. @@ -1269,6 +1492,15 @@ enum nl80211_commands { * @NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: When included along with * %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, indicates that the custom * ethertype frames used for key negotiation must not be encrypted. + * @NL80211_ATTR_CONTROL_PORT_OVER_NL80211: A flag indicating whether control + * port frames (e.g. of type given in %NL80211_ATTR_CONTROL_PORT_ETHERTYPE) + * will be sent directly to the network interface or sent via the NL80211 + * socket. If this attribute is missing, then legacy behavior of sending + * control port frames directly to the network interface is used. If the + * flag is included, then control port frames are sent over NL80211 instead + * using %CMD_CONTROL_PORT_FRAME. If control port routing over NL80211 is + * to be used then userspace must also use the %NL80211_ATTR_SOCKET_OWNER + * flag. * * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver. * We recommend using nested, driver-specific attributes within this. @@ -1343,7 +1575,13 @@ enum nl80211_commands { * enum nl80211_band value is used as the index (nla_type() of the nested * data. If a band is not included, it will be configured to allow all * rates based on negotiated supported rates information. This attribute - * is used with %NL80211_CMD_SET_TX_BITRATE_MASK. + * is used with %NL80211_CMD_SET_TX_BITRATE_MASK and with starting AP, + * and joining mesh networks (not IBSS yet). In the later case, it must + * specify just a single bitrate, which is to be used for the beacon. + * The driver must also specify support for this with the extended + * features NL80211_EXT_FEATURE_BEACON_RATE_LEGACY, + * NL80211_EXT_FEATURE_BEACON_RATE_HT and + * NL80211_EXT_FEATURE_BEACON_RATE_VHT. * * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain * at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME. @@ -1589,8 +1827,16 @@ enum nl80211_commands { * the connection request from a station. nl80211_connect_failed_reason * enum has different reasons of connection failure. * - * @NL80211_ATTR_SAE_DATA: SAE elements in Authentication frames. This starts - * with the Authentication transaction sequence number field. + * @NL80211_ATTR_AUTH_DATA: Fields and elements in Authentication frames. + * This contains the authentication frame body (non-IE and IE data), + * excluding the Authentication algorithm number, i.e., starting at the + * Authentication transaction sequence number field. It is used with + * authentication algorithms that need special fields to be added into + * the frames (SAE and FILS). Currently, only the SAE cases use the + * initial two fields (Authentication transaction sequence number and + * Status code). However, those fields are included in the attribute data + * for all authentication algorithms to keep the attribute definition + * consistent. * * @NL80211_ATTR_VHT_CAPABILITY: VHT Capability information element (from * association request when used with NL80211_CMD_NEW_STATION) @@ -1691,7 +1937,9 @@ enum nl80211_commands { * * @NL80211_ATTR_OPMODE_NOTIF: Operating mode field from Operating Mode * Notification Element based on association request when used with - * %NL80211_CMD_NEW_STATION; u8 attribute. + * %NL80211_CMD_NEW_STATION or %NL80211_CMD_SET_STATION (only when + * %NL80211_FEATURE_FULL_AP_CLIENT_STATE is supported, or with TDLS); + * u8 attribute. * * @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if * %NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet) @@ -1733,6 +1981,19 @@ enum nl80211_commands { * regulatory indoor configuration would be owned by the netlink socket * that configured the indoor setting, and the indoor operation would be * cleared when the socket is closed. + * If set during NAN interface creation, the interface will be destroyed + * if the socket is closed just like any other interface. Moreover, NAN + * notifications will be sent in unicast to that socket. Without this + * attribute, the notifications will be sent to the %NL80211_MCGRP_NAN + * multicast group. + * If set during %NL80211_CMD_ASSOCIATE or %NL80211_CMD_CONNECT the + * station will deauthenticate when the socket is closed. + * If set during %NL80211_CMD_JOIN_IBSS the IBSS will be automatically + * torn down when the socket is closed. + * If set during %NL80211_CMD_JOIN_MESH the mesh setup will be + * automatically torn down when the socket is closed. + * If set during %NL80211_CMD_START_AP the AP will be automatically + * disabled when the socket is closed. * * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is * the TDLS link initiator. @@ -1867,6 +2128,119 @@ enum nl80211_commands { * @NL80211_ATTR_MESH_PEER_AID: Association ID for the mesh peer (u16). This is * used to pull the stored data for mesh peer in power save state. * + * @NL80211_ATTR_NAN_MASTER_PREF: the master preference to be used by + * %NL80211_CMD_START_NAN and optionally with + * %NL80211_CMD_CHANGE_NAN_CONFIG. Its type is u8 and it can't be 0. + * Also, values 1 and 255 are reserved for certification purposes and + * should not be used during a normal device operation. + * @NL80211_ATTR_BANDS: operating bands configuration. This is a u32 + * bitmask of BIT(NL80211_BAND_*) as described in %enum + * nl80211_band. For instance, for NL80211_BAND_2GHZ, bit 0 + * would be set. This attribute is used with + * %NL80211_CMD_START_NAN and %NL80211_CMD_CHANGE_NAN_CONFIG, and + * it is optional. If no bands are set, it means don't-care and + * the device will decide what to use. + * @NL80211_ATTR_NAN_FUNC: a function that can be added to NAN. See + * &enum nl80211_nan_func_attributes for description of this nested + * attribute. + * @NL80211_ATTR_NAN_MATCH: used to report a match. This is a nested attribute. + * See &enum nl80211_nan_match_attributes. + * @NL80211_ATTR_FILS_KEK: KEK for FILS (Re)Association Request/Response frame + * protection. + * @NL80211_ATTR_FILS_NONCES: Nonces (part of AAD) for FILS (Re)Association + * Request/Response frame protection. This attribute contains the 16 octet + * STA Nonce followed by 16 octets of AP Nonce. + * + * @NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED: Indicates whether or not multicast + * packets should be send out as unicast to all stations (flag attribute). + * + * @NL80211_ATTR_BSSID: The BSSID of the AP. Note that %NL80211_ATTR_MAC is also + * used in various commands/events for specifying the BSSID. + * + * @NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI: Relative RSSI threshold by which + * other BSSs has to be better or slightly worse than the current + * connected BSS so that they get reported to user space. + * This will give an opportunity to userspace to consider connecting to + * other matching BSSs which have better or slightly worse RSSI than + * the current connected BSS by using an offloaded operation to avoid + * unnecessary wakeups. + * + * @NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST: When present the RSSI level for BSSs in + * the specified band is to be adjusted before doing + * %NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparision to figure out + * better BSSs. The attribute value is a packed structure + * value as specified by &struct nl80211_bss_select_rssi_adjust. + * + * @NL80211_ATTR_TIMEOUT_REASON: The reason for which an operation timed out. + * u32 attribute with an &enum nl80211_timeout_reason value. This is used, + * e.g., with %NL80211_CMD_CONNECT event. + * + * @NL80211_ATTR_FILS_ERP_USERNAME: EAP Re-authentication Protocol (ERP) + * username part of NAI used to refer keys rRK and rIK. This is used with + * %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_ERP_REALM: EAP Re-authentication Protocol (ERP) realm part + * of NAI specifying the domain name of the ER server. This is used with + * %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM: Unsigned 16-bit ERP next sequence number + * to use in ERP messages. This is used in generating the FILS wrapped data + * for FILS authentication and is used with %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_ERP_RRK: ERP re-authentication Root Key (rRK) for the + * NAI specified by %NL80211_ATTR_FILS_ERP_USERNAME and + * %NL80211_ATTR_FILS_ERP_REALM. This is used for generating rIK and rMSK + * from successful FILS authentication and is used with + * %NL80211_CMD_CONNECT. + * + * @NL80211_ATTR_FILS_CACHE_ID: A 2-octet identifier advertized by a FILS AP + * identifying the scope of PMKSAs. This is used with + * @NL80211_CMD_SET_PMKSA and @NL80211_CMD_DEL_PMKSA. + * + * @NL80211_ATTR_PMK: attribute for passing PMK key material. Used with + * %NL80211_CMD_SET_PMKSA for the PMKSA identified by %NL80211_ATTR_PMKID. + * For %NL80211_CMD_CONNECT it is used to provide PSK for offloading 4-way + * handshake for WPA/WPA2-PSK networks. For 802.1X authentication it is + * used with %NL80211_CMD_SET_PMK. For offloaded FT support this attribute + * specifies the PMK-R0 if NL80211_ATTR_PMKR0_NAME is included as well. + * + * @NL80211_ATTR_SCHED_SCAN_MULTI: flag attribute which user-space shall use to + * indicate that it supports multiple active scheduled scan requests. + * @NL80211_ATTR_SCHED_SCAN_MAX_REQS: indicates maximum number of scheduled + * scan request that may be active for the device (u32). + * + * @NL80211_ATTR_WANT_1X_4WAY_HS: flag attribute which user-space can include + * in %NL80211_CMD_CONNECT to indicate that for 802.1X authentication it + * wants to use the supported offload of the 4-way handshake. + * @NL80211_ATTR_PMKR0_NAME: PMK-R0 Name for offloaded FT. + * @NL80211_ATTR_PORT_AUTHORIZED: (reserved) + * + * @NL80211_ATTR_EXTERNAL_AUTH_ACTION: Identify the requested external + * authentication operation (u32 attribute with an + * &enum nl80211_external_auth_action value). This is used with the + * %NL80211_CMD_EXTERNAL_AUTH request event. + * @NL80211_ATTR_EXTERNAL_AUTH_SUPPORT: Flag attribute indicating that the user + * space supports external authentication. This attribute shall be used + * only with %NL80211_CMD_CONNECT request. The driver may offload + * authentication processing to user space if this capability is indicated + * in NL80211_CMD_CONNECT requests from the user space. + * + * @NL80211_ATTR_NSS: Station's New/updated RX_NSS value notified using this + * u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED. + * + * @NL80211_ATTR_TXQ_STATS: TXQ statistics (nested attribute, see &enum + * nl80211_txq_stats) + * @NL80211_ATTR_TXQ_LIMIT: Total packet limit for the TXQ queues for this phy. + * The smaller of this and the memory limit is enforced. + * @NL80211_ATTR_TXQ_MEMORY_LIMIT: Total memory memory limit (in bytes) for the + * TXQ queues for this phy. The smaller of this and the packet limit is + * enforced. + * @NL80211_ATTR_TXQ_QUANTUM: TXQ scheduler quantum (bytes). Number of bytes + * a flow is assigned on each round of the DRR scheduler. + * @NL80211_ATTR_HE_CAPABILITY: HE Capability information element (from + * association request when used with NL80211_CMD_NEW_STATION). Can be set + * only if %NL80211_STA_FLAG_WME is set. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2125,7 +2499,7 @@ enum nl80211_attrs { NL80211_ATTR_CONN_FAILED_REASON, - NL80211_ATTR_SAE_DATA, + NL80211_ATTR_AUTH_DATA, NL80211_ATTR_VHT_CAPABILITY, @@ -2261,6 +2635,53 @@ enum nl80211_attrs { NL80211_ATTR_MESH_PEER_AID, + NL80211_ATTR_NAN_MASTER_PREF, + NL80211_ATTR_BANDS, + NL80211_ATTR_NAN_FUNC, + NL80211_ATTR_NAN_MATCH, + + NL80211_ATTR_FILS_KEK, + NL80211_ATTR_FILS_NONCES, + + NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED, + + NL80211_ATTR_BSSID, + + NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI, + NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST, + + NL80211_ATTR_TIMEOUT_REASON, + + NL80211_ATTR_FILS_ERP_USERNAME, + NL80211_ATTR_FILS_ERP_REALM, + NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, + NL80211_ATTR_FILS_ERP_RRK, + NL80211_ATTR_FILS_CACHE_ID, + + NL80211_ATTR_PMK, + + NL80211_ATTR_SCHED_SCAN_MULTI, + NL80211_ATTR_SCHED_SCAN_MAX_REQS, + + NL80211_ATTR_WANT_1X_4WAY_HS, + NL80211_ATTR_PMKR0_NAME, + NL80211_ATTR_PORT_AUTHORIZED, + + NL80211_ATTR_EXTERNAL_AUTH_ACTION, + NL80211_ATTR_EXTERNAL_AUTH_SUPPORT, + + NL80211_ATTR_NSS, + NL80211_ATTR_ACK_SIGNAL, + + NL80211_ATTR_CONTROL_PORT_OVER_NL80211, + + NL80211_ATTR_TXQ_STATS, + NL80211_ATTR_TXQ_LIMIT, + NL80211_ATTR_TXQ_MEMORY_LIMIT, + NL80211_ATTR_TXQ_QUANTUM, + + NL80211_ATTR_HE_CAPABILITY, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2272,6 +2693,7 @@ enum nl80211_attrs { #define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION #define NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG #define NL80211_ATTR_IFACE_SOCKET_OWNER NL80211_ATTR_SOCKET_OWNER +#define NL80211_ATTR_SAE_DATA NL80211_ATTR_AUTH_DATA /* * Allow user space programs to use #ifdef on new attributes by defining them @@ -2299,6 +2721,8 @@ enum nl80211_attrs { #define NL80211_ATTR_KEYS NL80211_ATTR_KEYS #define NL80211_ATTR_FEATURE_FLAGS NL80211_ATTR_FEATURE_FLAGS +#define NL80211_WIPHY_NAME_MAXLEN 64 + #define NL80211_MAX_SUPP_RATES 32 #define NL80211_MAX_SUPP_HT_RATES 77 #define NL80211_MAX_SUPP_REG_RULES 64 @@ -2307,7 +2731,8 @@ enum nl80211_attrs { #define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 #define NL80211_HT_CAPABILITY_LEN 26 #define NL80211_VHT_CAPABILITY_LEN 12 - +#define NL80211_HE_MIN_CAPABILITY_LEN 16 +#define NL80211_HE_MAX_CAPABILITY_LEN 51 #define NL80211_MAX_NR_CIPHER_SUITES 5 #define NL80211_MAX_NR_AKM_SUITES 2 @@ -2339,6 +2764,7 @@ enum nl80211_attrs { * commands to create and destroy one * @NL80211_IF_TYPE_OCB: Outside Context of a BSS * This mode corresponds to the MIB variable dot11OCBActivated=true + * @NL80211_IFTYPE_NAN: NAN device interface type (not a netdev) * @NL80211_IFTYPE_MAX: highest interface type number currently defined * @NUM_NL80211_IFTYPES: number of defined interface types * @@ -2359,6 +2785,7 @@ enum nl80211_iftype { NL80211_IFTYPE_P2P_GO, NL80211_IFTYPE_P2P_DEVICE, NL80211_IFTYPE_OCB, + NL80211_IFTYPE_NAN, /* keep last */ NUM_NL80211_IFTYPES, @@ -2433,6 +2860,38 @@ struct nl80211_sta_flag_update { } __attribute__((packed)); /** + * enum nl80211_he_gi - HE guard interval + * @NL80211_RATE_INFO_HE_GI_0_8: 0.8 usec + * @NL80211_RATE_INFO_HE_GI_1_6: 1.6 usec + * @NL80211_RATE_INFO_HE_GI_3_2: 3.2 usec + */ +enum nl80211_he_gi { + NL80211_RATE_INFO_HE_GI_0_8, + NL80211_RATE_INFO_HE_GI_1_6, + NL80211_RATE_INFO_HE_GI_3_2, +}; + +/** + * enum nl80211_he_ru_alloc - HE RU allocation values + * @NL80211_RATE_INFO_HE_RU_ALLOC_26: 26-tone RU allocation + * @NL80211_RATE_INFO_HE_RU_ALLOC_52: 52-tone RU allocation + * @NL80211_RATE_INFO_HE_RU_ALLOC_106: 106-tone RU allocation + * @NL80211_RATE_INFO_HE_RU_ALLOC_242: 242-tone RU allocation + * @NL80211_RATE_INFO_HE_RU_ALLOC_484: 484-tone RU allocation + * @NL80211_RATE_INFO_HE_RU_ALLOC_996: 996-tone RU allocation + * @NL80211_RATE_INFO_HE_RU_ALLOC_2x996: 2x996-tone RU allocation + */ +enum nl80211_he_ru_alloc { + NL80211_RATE_INFO_HE_RU_ALLOC_26, + NL80211_RATE_INFO_HE_RU_ALLOC_52, + NL80211_RATE_INFO_HE_RU_ALLOC_106, + NL80211_RATE_INFO_HE_RU_ALLOC_242, + NL80211_RATE_INFO_HE_RU_ALLOC_484, + NL80211_RATE_INFO_HE_RU_ALLOC_996, + NL80211_RATE_INFO_HE_RU_ALLOC_2x996, +}; + +/** * enum nl80211_rate_info - bitrate information * * These attribute types are used with %NL80211_STA_INFO_TXRATE @@ -2464,6 +2923,13 @@ struct nl80211_sta_flag_update { * @NL80211_RATE_INFO_5_MHZ_WIDTH: 5 MHz width - note that this is * a legacy rate and will be reported as the actual bitrate, i.e. * a quarter of the base (20 MHz) rate + * @NL80211_RATE_INFO_HE_MCS: HE MCS index (u8, 0-11) + * @NL80211_RATE_INFO_HE_NSS: HE NSS value (u8, 1-8) + * @NL80211_RATE_INFO_HE_GI: HE guard interval identifier + * (u8, see &enum nl80211_he_gi) + * @NL80211_RATE_INFO_HE_DCM: HE DCM value (u8, 0/1) + * @NL80211_RATE_INFO_RU_ALLOC: HE RU allocation, if not present then + * non-OFDMA was used (u8, see &enum nl80211_he_ru_alloc) * @__NL80211_RATE_INFO_AFTER_LAST: internal use */ enum nl80211_rate_info { @@ -2480,6 +2946,11 @@ enum nl80211_rate_info { NL80211_RATE_INFO_160_MHZ_WIDTH, NL80211_RATE_INFO_10_MHZ_WIDTH, NL80211_RATE_INFO_5_MHZ_WIDTH, + NL80211_RATE_INFO_HE_MCS, + NL80211_RATE_INFO_HE_NSS, + NL80211_RATE_INFO_HE_GI, + NL80211_RATE_INFO_HE_DCM, + NL80211_RATE_INFO_HE_RU_ALLOC, /* keep last */ __NL80211_RATE_INFO_AFTER_LAST, @@ -2578,6 +3049,8 @@ enum nl80211_sta_bss_param { * @NL80211_STA_INFO_RX_DURATION: aggregate PPDU duration for all frames * received from the station (u64, usec) * @NL80211_STA_INFO_PAD: attribute used for padding for 64-bit alignment + * @NL80211_STA_INFO_ACK_SIGNAL: signal strength of the last ACK frame(u8, dBm) + * @NL80211_STA_INFO_ACK_SIGNAL_AVG: avg signal strength of ACK frames (s8, dBm) * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -2616,12 +3089,18 @@ enum nl80211_sta_info { NL80211_STA_INFO_TID_STATS, NL80211_STA_INFO_RX_DURATION, NL80211_STA_INFO_PAD, + NL80211_STA_INFO_ACK_SIGNAL, + NL80211_STA_INFO_ACK_SIGNAL_AVG, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1 }; +/* we renamed this - stay compatible */ +#define NL80211_STA_INFO_DATA_ACK_SIGNAL_AVG NL80211_STA_INFO_ACK_SIGNAL_AVG + + /** * enum nl80211_tid_stats - per TID statistics attributes * @__NL80211_TID_STATS_INVALID: attribute number 0 is reserved @@ -2633,6 +3112,7 @@ enum nl80211_sta_info { * @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted * MSDUs (u64) * @NL80211_TID_STATS_PAD: attribute used for padding for 64-bit alignment + * @NL80211_TID_STATS_TXQ_STATS: TXQ stats (nested attribute) * @NUM_NL80211_TID_STATS: number of attributes here * @NL80211_TID_STATS_MAX: highest numbered attribute here */ @@ -2643,6 +3123,7 @@ enum nl80211_tid_stats { NL80211_TID_STATS_TX_MSDU_RETRIES, NL80211_TID_STATS_TX_MSDU_FAILED, NL80211_TID_STATS_PAD, + NL80211_TID_STATS_TXQ_STATS, /* keep last */ NUM_NL80211_TID_STATS, @@ -2650,6 +3131,44 @@ enum nl80211_tid_stats { }; /** + * enum nl80211_txq_stats - per TXQ statistics attributes + * @__NL80211_TXQ_STATS_INVALID: attribute number 0 is reserved + * @NUM_NL80211_TXQ_STATS: number of attributes here + * @NL80211_TXQ_STATS_BACKLOG_BYTES: number of bytes currently backlogged + * @NL80211_TXQ_STATS_BACKLOG_PACKETS: number of packets currently + * backlogged + * @NL80211_TXQ_STATS_FLOWS: total number of new flows seen + * @NL80211_TXQ_STATS_DROPS: total number of packet drops + * @NL80211_TXQ_STATS_ECN_MARKS: total number of packet ECN marks + * @NL80211_TXQ_STATS_OVERLIMIT: number of drops due to queue space overflow + * @NL80211_TXQ_STATS_OVERMEMORY: number of drops due to memory limit overflow + * (only for per-phy stats) + * @NL80211_TXQ_STATS_COLLISIONS: number of hash collisions + * @NL80211_TXQ_STATS_TX_BYTES: total number of bytes dequeued from TXQ + * @NL80211_TXQ_STATS_TX_PACKETS: total number of packets dequeued from TXQ + * @NL80211_TXQ_STATS_MAX_FLOWS: number of flow buckets for PHY + * @NL80211_TXQ_STATS_MAX: highest numbered attribute here + */ +enum nl80211_txq_stats { + __NL80211_TXQ_STATS_INVALID, + NL80211_TXQ_STATS_BACKLOG_BYTES, + NL80211_TXQ_STATS_BACKLOG_PACKETS, + NL80211_TXQ_STATS_FLOWS, + NL80211_TXQ_STATS_DROPS, + NL80211_TXQ_STATS_ECN_MARKS, + NL80211_TXQ_STATS_OVERLIMIT, + NL80211_TXQ_STATS_OVERMEMORY, + NL80211_TXQ_STATS_COLLISIONS, + NL80211_TXQ_STATS_TX_BYTES, + NL80211_TXQ_STATS_TX_PACKETS, + NL80211_TXQ_STATS_MAX_FLOWS, + + /* keep last */ + NUM_NL80211_TXQ_STATS, + NL80211_TXQ_STATS_MAX = NUM_NL80211_TXQ_STATS - 1 +}; + +/** * enum nl80211_mpath_flags - nl80211 mesh path flags * * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active @@ -2701,6 +3220,38 @@ enum nl80211_mpath_info { }; /** + * enum nl80211_band_iftype_attr - Interface type data attributes + * + * @__NL80211_BAND_IFTYPE_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_BAND_IFTYPE_ATTR_IFTYPES: nested attribute containing a flag attribute + * for each interface type that supports the band data + * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC: HE MAC capabilities as in HE + * capabilities IE + * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY: HE PHY capabilities as in HE + * capabilities IE + * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET: HE supported NSS/MCS as in HE + * capabilities IE + * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE: HE PPE thresholds information as + * defined in HE capabilities IE + * @NL80211_BAND_IFTYPE_ATTR_MAX: highest band HE capability attribute currently + * defined + * @__NL80211_BAND_IFTYPE_ATTR_AFTER_LAST: internal use + */ +enum nl80211_band_iftype_attr { + __NL80211_BAND_IFTYPE_ATTR_INVALID, + + NL80211_BAND_IFTYPE_ATTR_IFTYPES, + NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC, + NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY, + NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET, + NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE, + + /* keep last */ + __NL80211_BAND_IFTYPE_ATTR_AFTER_LAST, + NL80211_BAND_IFTYPE_ATTR_MAX = __NL80211_BAND_IFTYPE_ATTR_AFTER_LAST - 1 +}; + +/** * enum nl80211_band_attr - band attributes * @__NL80211_BAND_ATTR_INVALID: attribute number 0 is reserved * @NL80211_BAND_ATTR_FREQS: supported frequencies in this band, @@ -2715,6 +3266,8 @@ enum nl80211_mpath_info { * @NL80211_BAND_ATTR_VHT_MCS_SET: 32-byte attribute containing the MCS set as * defined in 802.11ac * @NL80211_BAND_ATTR_VHT_CAPA: VHT capabilities, as in the HT information IE + * @NL80211_BAND_ATTR_IFTYPE_DATA: nested array attribute, with each entry using + * attributes from &enum nl80211_band_iftype_attr * @NL80211_BAND_ATTR_MAX: highest band attribute currently defined * @__NL80211_BAND_ATTR_AFTER_LAST: internal use */ @@ -2730,6 +3283,7 @@ enum nl80211_band_attr { NL80211_BAND_ATTR_VHT_MCS_SET, NL80211_BAND_ATTR_VHT_CAPA, + NL80211_BAND_ATTR_IFTYPE_DATA, /* keep last */ __NL80211_BAND_ATTR_AFTER_LAST, @@ -2739,6 +3293,29 @@ enum nl80211_band_attr { #define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA /** + * enum nl80211_wmm_rule - regulatory wmm rule + * + * @__NL80211_WMMR_INVALID: attribute number 0 is reserved + * @NL80211_WMMR_CW_MIN: Minimum contention window slot. + * @NL80211_WMMR_CW_MAX: Maximum contention window slot. + * @NL80211_WMMR_AIFSN: Arbitration Inter Frame Space. + * @NL80211_WMMR_TXOP: Maximum allowed tx operation time. + * @nl80211_WMMR_MAX: highest possible wmm rule. + * @__NL80211_WMMR_LAST: Internal use. + */ +enum nl80211_wmm_rule { + __NL80211_WMMR_INVALID, + NL80211_WMMR_CW_MIN, + NL80211_WMMR_CW_MAX, + NL80211_WMMR_AIFSN, + NL80211_WMMR_TXOP, + + /* keep last */ + __NL80211_WMMR_LAST, + NL80211_WMMR_MAX = __NL80211_WMMR_LAST - 1 +}; + +/** * enum nl80211_frequency_attr - frequency attributes * @__NL80211_FREQUENCY_ATTR_INVALID: attribute number 0 is reserved * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz @@ -2787,6 +3364,9 @@ enum nl80211_band_attr { * on this channel in current regulatory domain. * @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_WMM: this channel has wmm limitations. + * This is a nested attribute that contains the wmm limitation per AC. + * (see &enum nl80211_wmm_rule) * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use @@ -2815,6 +3395,7 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_IR_CONCURRENT, NL80211_FREQUENCY_ATTR_NO_20MHZ, NL80211_FREQUENCY_ATTR_NO_10MHZ, + NL80211_FREQUENCY_ATTR_WMM, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, @@ -2942,6 +3523,7 @@ enum nl80211_reg_rule_attr { * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching, * only report BSS with matching SSID. + * (This cannot be used together with BSSID.) * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a * BSS in scan results. Filtering is turned off if not specified. Note that * if this attribute is in a match set of its own, then it is treated as @@ -2950,6 +3532,15 @@ enum nl80211_reg_rule_attr { * how this API was implemented in the past. Also, due to the same problem, * the only way to create a matchset with only an RSSI filter (with this * attribute) is if there's only a single matchset with the RSSI attribute. + * @NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI: Flag indicating whether + * %NL80211_SCHED_SCAN_MATCH_ATTR_RSSI to be used as absolute RSSI or + * relative to current bss's RSSI. + * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST: When present the RSSI level for + * BSS-es in the specified band is to be adjusted before doing + * RSSI-based BSS selection. The attribute value is a packed structure + * value as specified by &struct nl80211_bss_select_rssi_adjust. + * @NL80211_SCHED_SCAN_MATCH_ATTR_BSSID: BSSID to be used for matching + * (this cannot be used together with SSID). * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter * attribute number currently defined * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use @@ -2959,6 +3550,9 @@ enum nl80211_sched_scan_match_attr { NL80211_SCHED_SCAN_MATCH_ATTR_SSID, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI, + NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI, + NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST, + NL80211_SCHED_SCAN_MATCH_ATTR_BSSID, /* keep last */ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST, @@ -2985,7 +3579,7 @@ enum nl80211_sched_scan_match_attr { * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated * base on contiguous rules and wider channels will be allowed to cross * multiple contiguous/overlapping frequency ranges. - * @NL80211_RRF_IR_CONCURRENT: See &NL80211_FREQUENCY_ATTR_IR_CONCURRENT + * @NL80211_RRF_IR_CONCURRENT: See %NL80211_FREQUENCY_ATTR_IR_CONCURRENT * @NL80211_RRF_NO_HT40MINUS: channels can't be used in HT40- operation * @NL80211_RRF_NO_HT40PLUS: channels can't be used in HT40+ operation * @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed @@ -3528,6 +4122,9 @@ enum nl80211_bss_scan_width { * @NL80211_BSS_PARENT_BSSID. (u64). * @NL80211_BSS_PARENT_BSSID: the BSS according to which @NL80211_BSS_PARENT_TSF * is set. + * @NL80211_BSS_CHAIN_SIGNAL: per-chain signal strength of last BSS update. + * Contains a nested array of signal strength attributes (u8, dBm), + * using the nesting index as the antenna number. * @__NL80211_BSS_AFTER_LAST: internal * @NL80211_BSS_MAX: highest BSS attribute */ @@ -3551,6 +4148,7 @@ enum nl80211_bss { NL80211_BSS_PAD, NL80211_BSS_PARENT_TSF, NL80211_BSS_PARENT_BSSID, + NL80211_BSS_CHAIN_SIGNAL, /* keep last */ __NL80211_BSS_AFTER_LAST, @@ -3583,6 +4181,9 @@ enum nl80211_bss_status { * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r) * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP) * @NL80211_AUTHTYPE_SAE: Simultaneous authentication of equals + * @NL80211_AUTHTYPE_FILS_SK: Fast Initial Link Setup shared key + * @NL80211_AUTHTYPE_FILS_SK_PFS: Fast Initial Link Setup shared key with PFS + * @NL80211_AUTHTYPE_FILS_PK: Fast Initial Link Setup public key * @__NL80211_AUTHTYPE_NUM: internal * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by @@ -3595,6 +4196,9 @@ enum nl80211_auth_type { NL80211_AUTHTYPE_FT, NL80211_AUTHTYPE_NETWORK_EAP, NL80211_AUTHTYPE_SAE, + NL80211_AUTHTYPE_FILS_SK, + NL80211_AUTHTYPE_FILS_SK_PFS, + NL80211_AUTHTYPE_FILS_PK, /* keep last */ __NL80211_AUTHTYPE_NUM, @@ -3621,10 +4225,12 @@ enum nl80211_key_type { * enum nl80211_mfp - Management frame protection state * @NL80211_MFP_NO: Management frame protection not used * @NL80211_MFP_REQUIRED: Management frame protection required + * @NL80211_MFP_OPTIONAL: Management frame protection is optional */ enum nl80211_mfp { NL80211_MFP_NO, NL80211_MFP_REQUIRED, + NL80211_MFP_OPTIONAL, }; enum nl80211_wpa_versions { @@ -3735,7 +4341,7 @@ enum nl80211_txrate_gi { * enum nl80211_band - Frequency band * @NL80211_BAND_2GHZ: 2.4 GHz ISM band * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz) - * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz) + * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 69.12 GHz) * @NUM_NL80211_BANDS: number of bands, avoid using this in userspace * since newer kernel versions may support more bands */ @@ -3762,7 +4368,10 @@ enum nl80211_ps_state { * @__NL80211_ATTR_CQM_INVALID: invalid * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies * the threshold for the RSSI level at which an event will be sent. Zero - * to disable. + * to disable. Alternatively, if %NL80211_EXT_FEATURE_CQM_RSSI_LIST is + * set, multiple values can be supplied as a low-to-high sorted array of + * threshold values in dBm. Events will be sent when the RSSI value + * crosses any of the thresholds. * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies * the minimum amount the RSSI level must change after an event before a * new event may be issued (to reduce effects of RSSI oscillation). @@ -3782,6 +4391,8 @@ enum nl80211_ps_state { * %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting. * @NL80211_ATTR_CQM_BEACON_LOSS_EVENT: flag attribute that's set in a beacon * loss event + * @NL80211_ATTR_CQM_RSSI_LEVEL: the RSSI value in dBm that triggered the + * RSSI threshold event. * @__NL80211_ATTR_CQM_AFTER_LAST: internal * @NL80211_ATTR_CQM_MAX: highest key attribute */ @@ -3795,6 +4406,7 @@ enum nl80211_attr_cqm { NL80211_ATTR_CQM_TXE_PKTS, NL80211_ATTR_CQM_TXE_INTVL, NL80211_ATTR_CQM_BEACON_LOSS_EVENT, + NL80211_ATTR_CQM_RSSI_LEVEL, /* keep last */ __NL80211_ATTR_CQM_AFTER_LAST, @@ -4203,6 +4815,9 @@ enum nl80211_iface_limit_attrs { * of supported channel widths for radar detection. * @NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u32 attribute containing the bitmap * of supported regulatory regions for radar detection. + * @NL80211_IFACE_COMB_BI_MIN_GCD: u32 attribute specifying the minimum GCD of + * different beacon intervals supported by all the interface combinations + * in this group (if not present, all beacon intervals be identical). * @NUM_NL80211_IFACE_COMB: number of attributes * @MAX_NL80211_IFACE_COMB: highest attribute number * @@ -4210,8 +4825,8 @@ enum nl80211_iface_limit_attrs { * limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2 * => allows an AP and a STA that must match BIs * - * numbers = [ #{AP, P2P-GO} <= 8 ], channels = 1, max = 8 - * => allows 8 of AP/GO + * numbers = [ #{AP, P2P-GO} <= 8 ], BI min gcd, channels = 1, max = 8, + * => allows 8 of AP/GO that can have BI gcd >= min gcd * * numbers = [ #{STA} <= 2 ], channels = 2, max = 2 * => allows two STAs on different channels @@ -4237,6 +4852,7 @@ enum nl80211_if_combination_attrs { NL80211_IFACE_COMB_NUM_CHANNELS, NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, NL80211_IFACE_COMB_RADAR_DETECT_REGIONS, + NL80211_IFACE_COMB_BI_MIN_GCD, /* keep last */ NUM_NL80211_IFACE_COMB, @@ -4551,6 +5167,64 @@ enum nl80211_feature_flags { * (if available). * @NL80211_EXT_FEATURE_SET_SCAN_DWELL: This driver supports configuration of * channel dwell time. + * @NL80211_EXT_FEATURE_BEACON_RATE_LEGACY: Driver supports beacon rate + * configuration (AP/mesh), supporting a legacy (non HT/VHT) rate. + * @NL80211_EXT_FEATURE_BEACON_RATE_HT: Driver supports beacon rate + * configuration (AP/mesh) with HT rates. + * @NL80211_EXT_FEATURE_BEACON_RATE_VHT: Driver supports beacon rate + * configuration (AP/mesh) with VHT rates. + * @NL80211_EXT_FEATURE_FILS_STA: This driver supports Fast Initial Link Setup + * with user space SME (NL80211_CMD_AUTHENTICATE) in station mode. + * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA: This driver supports randomized TA + * in @NL80211_CMD_FRAME while not associated. + * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED: This driver supports + * randomized TA in @NL80211_CMD_FRAME while associated. + * @NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI: The driver supports sched_scan + * for reporting BSSs with better RSSI than the current connected BSS + * (%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI). + * @NL80211_EXT_FEATURE_CQM_RSSI_LIST: With this driver the + * %NL80211_ATTR_CQM_RSSI_THOLD attribute accepts a list of zero or more + * RSSI threshold values to monitor rather than exactly one threshold. + * @NL80211_EXT_FEATURE_FILS_SK_OFFLOAD: Driver SME supports FILS shared key + * authentication with %NL80211_CMD_CONNECT. + * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK: Device wants to do 4-way + * handshake with PSK in station mode (PSK is passed as part of the connect + * and associate commands), doing it in the host might not be supported. + * @NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X: Device wants to do doing 4-way + * handshake with 802.1X in station mode (will pass EAP frames to the host + * and accept the set_pmk/del_pmk commands), doing it in the host might not + * be supported. + * @NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME: Driver is capable of overriding + * the max channel attribute in the FILS request params IE with the + * actual dwell time. + * @NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP: Driver accepts broadcast probe + * response + * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE: Driver supports sending + * the first probe request in each channel at rate of at least 5.5Mbps. + * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: Driver supports + * probe request tx deferral and suppression + * @NL80211_EXT_FEATURE_MFP_OPTIONAL: Driver supports the %NL80211_MFP_OPTIONAL + * value in %NL80211_ATTR_USE_MFP. + * @NL80211_EXT_FEATURE_LOW_SPAN_SCAN: Driver supports low span scan. + * @NL80211_EXT_FEATURE_LOW_POWER_SCAN: Driver supports low power scan. + * @NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN: Driver supports high accuracy scan. + * @NL80211_EXT_FEATURE_DFS_OFFLOAD: HW/driver will offload DFS actions. + * Device or driver will do all DFS-related actions by itself, + * informing user-space about CAC progress, radar detection event, + * channel change triggered by radar detection event. + * No need to start CAC from user-space, no need to react to + * "radar detected" event. + * @NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211: Driver supports sending and + * receiving control port frames over nl80211 instead of the netdevice. + * @NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT: This driver/device supports + * (average) ACK signal strength reporting. + * @NL80211_EXT_FEATURE_TXQS: Driver supports FQ-CoDel-enabled intermediate + * TXQs. + * @NL80211_EXT_FEATURE_SCAN_RANDOM_SN: Driver/device supports randomizing the + * SN in probe request frames if requested by %NL80211_SCAN_FLAG_RANDOM_SN. + * @NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT: Driver/device can omit all data + * except for supported rates from the probe request content if requested + * by the %NL80211_SCAN_FLAG_MIN_PREQ_CONTENT flag. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -4562,6 +5236,33 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_SCAN_START_TIME, NL80211_EXT_FEATURE_BSS_PARENT_TSF, NL80211_EXT_FEATURE_SET_SCAN_DWELL, + NL80211_EXT_FEATURE_BEACON_RATE_LEGACY, + NL80211_EXT_FEATURE_BEACON_RATE_HT, + NL80211_EXT_FEATURE_BEACON_RATE_VHT, + NL80211_EXT_FEATURE_FILS_STA, + NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA, + NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED, + NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI, + NL80211_EXT_FEATURE_CQM_RSSI_LIST, + NL80211_EXT_FEATURE_FILS_SK_OFFLOAD, + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK, + NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X, + NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME, + NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP, + NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE, + NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION, + NL80211_EXT_FEATURE_MFP_OPTIONAL, + NL80211_EXT_FEATURE_LOW_SPAN_SCAN, + NL80211_EXT_FEATURE_LOW_POWER_SCAN, + NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN, + NL80211_EXT_FEATURE_DFS_OFFLOAD, + NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211, + NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT, + /* we renamed this - stay compatible */ + NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT = NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT, + NL80211_EXT_FEATURE_TXQS, + NL80211_EXT_FEATURE_SCAN_RANDOM_SN, + NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, @@ -4601,12 +5302,31 @@ enum nl80211_connect_failed_reason { }; /** + * enum nl80211_timeout_reason - timeout reasons + * + * @NL80211_TIMEOUT_UNSPECIFIED: Timeout reason unspecified. + * @NL80211_TIMEOUT_SCAN: Scan (AP discovery) timed out. + * @NL80211_TIMEOUT_AUTH: Authentication timed out. + * @NL80211_TIMEOUT_ASSOC: Association timed out. + */ +enum nl80211_timeout_reason { + NL80211_TIMEOUT_UNSPECIFIED, + NL80211_TIMEOUT_SCAN, + NL80211_TIMEOUT_AUTH, + NL80211_TIMEOUT_ASSOC, +}; + +/** * enum nl80211_scan_flags - scan request control flags * * Scan request control flags are used to control the handling * of NL80211_CMD_TRIGGER_SCAN and NL80211_CMD_START_SCHED_SCAN * requests. * + * NL80211_SCAN_FLAG_LOW_SPAN, NL80211_SCAN_FLAG_LOW_POWER, and + * NL80211_SCAN_FLAG_HIGH_ACCURACY flags are exclusive of each other, i.e., only + * one of them can be used in the request. + * * @NL80211_SCAN_FLAG_LOW_PRIORITY: scan request has low priority * @NL80211_SCAN_FLAG_FLUSH: flush cache before scanning * @NL80211_SCAN_FLAG_AP: force a scan even if the interface is configured @@ -4623,12 +5343,52 @@ enum nl80211_connect_failed_reason { * locally administered 1, multicast 0) is assumed. * This flag must not be requested when the feature isn't supported, check * the nl80211 feature flags for the device. + * @NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME: fill the dwell time in the FILS + * request parameters IE in the probe request + * @NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe responses + * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE: send probe request frames at + * rate of at least 5.5M. In case non OCE AP is dicovered in the channel, + * only the first probe req in the channel will be sent in high rate. + * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: allow probe request + * tx deferral (dot11FILSProbeDelay shall be set to 15ms) + * and suppression (if it has received a broadcast Probe Response frame, + * Beacon frame or FILS Discovery frame from an AP that the STA considers + * a suitable candidate for (re-)association - suitable in terms of + * SSID and/or RSSI. + * @NL80211_SCAN_FLAG_LOW_SPAN: Span corresponds to the total time taken to + * accomplish the scan. Thus, this flag intends the driver to perform the + * scan request with lesser span/duration. It is specific to the driver + * implementations on how this is accomplished. Scan accuracy may get + * impacted with this flag. + * @NL80211_SCAN_FLAG_LOW_POWER: This flag intends the scan attempts to consume + * optimal possible power. Drivers can resort to their specific means to + * optimize the power. Scan accuracy may get impacted with this flag. + * @NL80211_SCAN_FLAG_HIGH_ACCURACY: Accuracy here intends to the extent of scan + * results obtained. Thus HIGH_ACCURACY scan flag aims to get maximum + * possible scan results. This flag hints the driver to use the best + * possible scan configuration to improve the accuracy in scanning. + * Latency and power use may get impacted with this flag. + * @NL80211_SCAN_FLAG_RANDOM_SN: randomize the sequence number in probe + * request frames from this scan to avoid correlation/tracking being + * possible. + * @NL80211_SCAN_FLAG_MIN_PREQ_CONTENT: minimize probe request content to + * only have supported rates and no additional capabilities (unless + * added by userspace explicitly.) */ enum nl80211_scan_flags { - NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0, - NL80211_SCAN_FLAG_FLUSH = 1<<1, - NL80211_SCAN_FLAG_AP = 1<<2, - NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3, + NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0, + NL80211_SCAN_FLAG_FLUSH = 1<<1, + NL80211_SCAN_FLAG_AP = 1<<2, + NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3, + NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME = 1<<4, + NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP = 1<<5, + NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE = 1<<6, + NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION = 1<<7, + NL80211_SCAN_FLAG_LOW_SPAN = 1<<8, + NL80211_SCAN_FLAG_LOW_POWER = 1<<9, + NL80211_SCAN_FLAG_HIGH_ACCURACY = 1<<10, + NL80211_SCAN_FLAG_RANDOM_SN = 1<<11, + NL80211_SCAN_FLAG_MIN_PREQ_CONTENT = 1<<12, }; /** @@ -4682,12 +5442,20 @@ enum nl80211_smps_mode { * change to the channel status. * @NL80211_RADAR_NOP_FINISHED: The Non-Occupancy Period for this channel is * over, channel becomes usable. + * @NL80211_RADAR_PRE_CAC_EXPIRED: Channel Availability Check done on this + * non-operating channel is expired and no longer valid. New CAC must + * be done on this channel before starting the operation. This is not + * applicable for ETSI dfs domain where pre-CAC is valid for ever. + * @NL80211_RADAR_CAC_STARTED: Channel Availability Check has been started, + * should be generated by HW if NL80211_EXT_FEATURE_DFS_OFFLOAD is enabled. */ enum nl80211_radar_event { NL80211_RADAR_DETECTED, NL80211_RADAR_CAC_FINISHED, NL80211_RADAR_CAC_ABORTED, NL80211_RADAR_NOP_FINISHED, + NL80211_RADAR_PRE_CAC_EXPIRED, + NL80211_RADAR_CAC_STARTED, }; /** @@ -4814,8 +5582,9 @@ enum nl80211_sched_scan_plan { /** * struct nl80211_bss_select_rssi_adjust - RSSI adjustment parameters. * - * @band: band of BSS that must match for RSSI value adjustment. - * @delta: value used to adjust the RSSI value of matching BSS. + * @band: band of BSS that must match for RSSI value adjustment. The value + * of this field is according to &enum nl80211_band. + * @delta: value used to adjust the RSSI value of matching BSS in dB. */ struct nl80211_bss_select_rssi_adjust { __u8 band; @@ -4855,4 +5624,182 @@ enum nl80211_bss_select_attr { NL80211_BSS_SELECT_ATTR_MAX = __NL80211_BSS_SELECT_ATTR_AFTER_LAST - 1 }; +/** + * enum nl80211_nan_function_type - NAN function type + * + * Defines the function type of a NAN function + * + * @NL80211_NAN_FUNC_PUBLISH: function is publish + * @NL80211_NAN_FUNC_SUBSCRIBE: function is subscribe + * @NL80211_NAN_FUNC_FOLLOW_UP: function is follow-up + */ +enum nl80211_nan_function_type { + NL80211_NAN_FUNC_PUBLISH, + NL80211_NAN_FUNC_SUBSCRIBE, + NL80211_NAN_FUNC_FOLLOW_UP, + + /* keep last */ + __NL80211_NAN_FUNC_TYPE_AFTER_LAST, + NL80211_NAN_FUNC_MAX_TYPE = __NL80211_NAN_FUNC_TYPE_AFTER_LAST - 1, +}; + +/** + * enum nl80211_nan_publish_type - NAN publish tx type + * + * Defines how to send publish Service Discovery Frames + * + * @NL80211_NAN_SOLICITED_PUBLISH: publish function is solicited + * @NL80211_NAN_UNSOLICITED_PUBLISH: publish function is unsolicited + */ +enum nl80211_nan_publish_type { + NL80211_NAN_SOLICITED_PUBLISH = 1 << 0, + NL80211_NAN_UNSOLICITED_PUBLISH = 1 << 1, +}; + +/** + * enum nl80211_nan_func_term_reason - NAN functions termination reason + * + * Defines termination reasons of a NAN function + * + * @NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST: requested by user + * @NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED: timeout + * @NL80211_NAN_FUNC_TERM_REASON_ERROR: errored + */ +enum nl80211_nan_func_term_reason { + NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST, + NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED, + NL80211_NAN_FUNC_TERM_REASON_ERROR, +}; + +#define NL80211_NAN_FUNC_SERVICE_ID_LEN 6 +#define NL80211_NAN_FUNC_SERVICE_SPEC_INFO_MAX_LEN 0xff +#define NL80211_NAN_FUNC_SRF_MAX_LEN 0xff + +/** + * enum nl80211_nan_func_attributes - NAN function attributes + * @__NL80211_NAN_FUNC_INVALID: invalid + * @NL80211_NAN_FUNC_TYPE: &enum nl80211_nan_function_type (u8). + * @NL80211_NAN_FUNC_SERVICE_ID: 6 bytes of the service ID hash as + * specified in NAN spec. This is a binary attribute. + * @NL80211_NAN_FUNC_PUBLISH_TYPE: relevant if the function's type is + * publish. Defines the transmission type for the publish Service Discovery + * Frame, see &enum nl80211_nan_publish_type. Its type is u8. + * @NL80211_NAN_FUNC_PUBLISH_BCAST: relevant if the function is a solicited + * publish. Should the solicited publish Service Discovery Frame be sent to + * the NAN Broadcast address. This is a flag. + * @NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE: relevant if the function's type is + * subscribe. Is the subscribe active. This is a flag. + * @NL80211_NAN_FUNC_FOLLOW_UP_ID: relevant if the function's type is follow up. + * The instance ID for the follow up Service Discovery Frame. This is u8. + * @NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID: relevant if the function's type + * is follow up. This is a u8. + * The requestor instance ID for the follow up Service Discovery Frame. + * @NL80211_NAN_FUNC_FOLLOW_UP_DEST: the MAC address of the recipient of the + * follow up Service Discovery Frame. This is a binary attribute. + * @NL80211_NAN_FUNC_CLOSE_RANGE: is this function limited for devices in a + * close range. The range itself (RSSI) is defined by the device. + * This is a flag. + * @NL80211_NAN_FUNC_TTL: strictly positive number of DWs this function should + * stay active. If not present infinite TTL is assumed. This is a u32. + * @NL80211_NAN_FUNC_SERVICE_INFO: array of bytes describing the service + * specific info. This is a binary attribute. + * @NL80211_NAN_FUNC_SRF: Service Receive Filter. This is a nested attribute. + * See &enum nl80211_nan_srf_attributes. + * @NL80211_NAN_FUNC_RX_MATCH_FILTER: Receive Matching filter. This is a nested + * attribute. It is a list of binary values. + * @NL80211_NAN_FUNC_TX_MATCH_FILTER: Transmit Matching filter. This is a + * nested attribute. It is a list of binary values. + * @NL80211_NAN_FUNC_INSTANCE_ID: The instance ID of the function. + * Its type is u8 and it cannot be 0. + * @NL80211_NAN_FUNC_TERM_REASON: NAN function termination reason. + * See &enum nl80211_nan_func_term_reason. + * + * @NUM_NL80211_NAN_FUNC_ATTR: internal + * @NL80211_NAN_FUNC_ATTR_MAX: highest NAN function attribute + */ +enum nl80211_nan_func_attributes { + __NL80211_NAN_FUNC_INVALID, + NL80211_NAN_FUNC_TYPE, + NL80211_NAN_FUNC_SERVICE_ID, + NL80211_NAN_FUNC_PUBLISH_TYPE, + NL80211_NAN_FUNC_PUBLISH_BCAST, + NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE, + NL80211_NAN_FUNC_FOLLOW_UP_ID, + NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID, + NL80211_NAN_FUNC_FOLLOW_UP_DEST, + NL80211_NAN_FUNC_CLOSE_RANGE, + NL80211_NAN_FUNC_TTL, + NL80211_NAN_FUNC_SERVICE_INFO, + NL80211_NAN_FUNC_SRF, + NL80211_NAN_FUNC_RX_MATCH_FILTER, + NL80211_NAN_FUNC_TX_MATCH_FILTER, + NL80211_NAN_FUNC_INSTANCE_ID, + NL80211_NAN_FUNC_TERM_REASON, + + /* keep last */ + NUM_NL80211_NAN_FUNC_ATTR, + NL80211_NAN_FUNC_ATTR_MAX = NUM_NL80211_NAN_FUNC_ATTR - 1 +}; + +/** + * enum nl80211_nan_srf_attributes - NAN Service Response filter attributes + * @__NL80211_NAN_SRF_INVALID: invalid + * @NL80211_NAN_SRF_INCLUDE: present if the include bit of the SRF set. + * This is a flag. + * @NL80211_NAN_SRF_BF: Bloom Filter. Present if and only if + * %NL80211_NAN_SRF_MAC_ADDRS isn't present. This attribute is binary. + * @NL80211_NAN_SRF_BF_IDX: index of the Bloom Filter. Mandatory if + * %NL80211_NAN_SRF_BF is present. This is a u8. + * @NL80211_NAN_SRF_MAC_ADDRS: list of MAC addresses for the SRF. Present if + * and only if %NL80211_NAN_SRF_BF isn't present. This is a nested + * attribute. Each nested attribute is a MAC address. + * @NUM_NL80211_NAN_SRF_ATTR: internal + * @NL80211_NAN_SRF_ATTR_MAX: highest NAN SRF attribute + */ +enum nl80211_nan_srf_attributes { + __NL80211_NAN_SRF_INVALID, + NL80211_NAN_SRF_INCLUDE, + NL80211_NAN_SRF_BF, + NL80211_NAN_SRF_BF_IDX, + NL80211_NAN_SRF_MAC_ADDRS, + + /* keep last */ + NUM_NL80211_NAN_SRF_ATTR, + NL80211_NAN_SRF_ATTR_MAX = NUM_NL80211_NAN_SRF_ATTR - 1, +}; + +/** + * enum nl80211_nan_match_attributes - NAN match attributes + * @__NL80211_NAN_MATCH_INVALID: invalid + * @NL80211_NAN_MATCH_FUNC_LOCAL: the local function that had the + * match. This is a nested attribute. + * See &enum nl80211_nan_func_attributes. + * @NL80211_NAN_MATCH_FUNC_PEER: the peer function + * that caused the match. This is a nested attribute. + * See &enum nl80211_nan_func_attributes. + * + * @NUM_NL80211_NAN_MATCH_ATTR: internal + * @NL80211_NAN_MATCH_ATTR_MAX: highest NAN match attribute + */ +enum nl80211_nan_match_attributes { + __NL80211_NAN_MATCH_INVALID, + NL80211_NAN_MATCH_FUNC_LOCAL, + NL80211_NAN_MATCH_FUNC_PEER, + + /* keep last */ + NUM_NL80211_NAN_MATCH_ATTR, + NL80211_NAN_MATCH_ATTR_MAX = NUM_NL80211_NAN_MATCH_ATTR - 1 +}; + +/** + * nl80211_external_auth_action - Action to perform with external + * authentication request. Used by NL80211_ATTR_EXTERNAL_AUTH_ACTION. + * @NL80211_EXTERNAL_AUTH_START: Start the authentication. + * @NL80211_EXTERNAL_AUTH_ABORT: Abort the ongoing authentication. + */ +enum nl80211_external_auth_action { + NL80211_EXTERNAL_AUTH_START, + NL80211_EXTERNAL_AUTH_ABORT, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/src/eap_common/eap_eke_common.c b/src/eap_common/eap_eke_common.c index 621746821538b..bfe8811e33f27 100644 --- a/src/eap_common/eap_eke_common.c +++ b/src/eap_common/eap_eke_common.c @@ -161,7 +161,6 @@ int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub) int generator; u8 gen; const struct dh_group *dh; - size_t pub_len, i; generator = eap_eke_dh_generator(group); dh = eap_eke_dh_group(group); @@ -169,33 +168,11 @@ int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub) return -1; gen = generator; - /* x = random number 2 .. p-1 */ - if (random_get_bytes(ret_priv, dh->prime_len)) - return -1; - if (os_memcmp(ret_priv, dh->prime, dh->prime_len) > 0) { - /* Make sure private value is smaller than prime */ - ret_priv[0] = 0; - } - for (i = 0; i < dh->prime_len - 1; i++) { - if (ret_priv[i]) - break; - } - if (i == dh->prime_len - 1 && (ret_priv[i] == 0 || ret_priv[i] == 1)) + if (crypto_dh_init(gen, dh->prime, dh->prime_len, ret_priv, + ret_pub) < 0) return -1; wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: DH private value", ret_priv, dh->prime_len); - - /* y = g ^ x (mod p) */ - pub_len = dh->prime_len; - if (crypto_mod_exp(&gen, 1, ret_priv, dh->prime_len, - dh->prime, dh->prime_len, ret_pub, &pub_len) < 0) - return -1; - if (pub_len < dh->prime_len) { - size_t pad = dh->prime_len - pub_len; - os_memmove(ret_pub + pad, ret_pub, pub_len); - os_memset(ret_pub, 0, pad); - } - wpa_hexdump(MSG_DEBUG, "EAP-EKE: DH public value", ret_pub, dh->prime_len); @@ -421,8 +398,9 @@ int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key, /* SharedSecret = prf(0+, g ^ (x_s * x_p) (mod p)) */ len = dh->prime_len; - if (crypto_mod_exp(peer_pub, dh->prime_len, dhpriv, dh->prime_len, - dh->prime, dh->prime_len, modexp, &len) < 0) + if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len, + dhpriv, dh->prime_len, peer_pub, + dh->prime_len, modexp, &len) < 0) return -1; if (len < dh->prime_len) { size_t pad = dh->prime_len - len; diff --git a/src/eap_common/eap_fast_common.c b/src/eap_common/eap_fast_common.c index 9ef671c41c7d7..57990d254a6af 100644 --- a/src/eap_common/eap_fast_common.c +++ b/src/eap_common/eap_fast_common.c @@ -79,7 +79,7 @@ void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random, /* * RFC 4851, Section 5.1: - * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash", + * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash", * server_random + client_random, 48) */ os_memcpy(seed, server_random, TLS_RANDOM_LEN); diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c index 67f8f7098c4b6..88c6595887ab9 100644 --- a/src/eap_common/eap_pwd_common.c +++ b/src/eap_common/eap_pwd_common.c @@ -81,6 +81,27 @@ static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label, } +EAP_PWD_group * get_eap_pwd_group(u16 num) +{ + EAP_PWD_group *grp; + + grp = os_zalloc(sizeof(EAP_PWD_group)); + if (!grp) + return NULL; + grp->group = crypto_ec_init(num); + if (!grp->group) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC group"); + os_free(grp); + return NULL; + } + + grp->group_num = num; + wpa_printf(MSG_INFO, "EAP-pwd: provisioned group %d", num); + + return grp; +} + + /* * compute a "random" secret point on an elliptic curve based * on the password and identities. @@ -91,105 +112,71 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, const u8 *id_peer, size_t id_peer_len, const u8 *token) { - BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; + struct crypto_bignum *qr = NULL, *qnr = NULL, *one = NULL; + struct crypto_bignum *tmp1 = NULL, *tmp2 = NULL, *pm1 = NULL; struct crypto_hash *hash; unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr; - int nid, is_odd, ret = 0; + int is_odd, ret = 0, check, found = 0; size_t primebytelen, primebitlen; + struct crypto_bignum *x_candidate = NULL, *rnd = NULL, *cofactor = NULL; + const struct crypto_bignum *prime; - switch (num) { /* from IANA registry for IKE D-H groups */ - case 19: - nid = NID_X9_62_prime256v1; - break; - case 20: - nid = NID_secp384r1; - break; - case 21: - nid = NID_secp521r1; - break; -#ifndef OPENSSL_IS_BORINGSSL - case 25: - nid = NID_X9_62_prime192v1; - break; -#endif /* OPENSSL_IS_BORINGSSL */ - case 26: - nid = NID_secp224r1; - break; -#ifdef NID_brainpoolP224r1 - case 27: - nid = NID_brainpoolP224r1; - break; -#endif /* NID_brainpoolP224r1 */ -#ifdef NID_brainpoolP256r1 - case 28: - nid = NID_brainpoolP256r1; - break; -#endif /* NID_brainpoolP256r1 */ -#ifdef NID_brainpoolP384r1 - case 29: - nid = NID_brainpoolP384r1; - break; -#endif /* NID_brainpoolP384r1 */ -#ifdef NID_brainpoolP512r1 - case 30: - nid = NID_brainpoolP512r1; - break; -#endif /* NID_brainpoolP512r1 */ - default: - wpa_printf(MSG_INFO, "EAP-pwd: unsupported group %d", num); + if (grp->pwe) return -1; - } - grp->pwe = NULL; - grp->order = NULL; - grp->prime = NULL; - - if ((grp->group = EC_GROUP_new_by_curve_name(nid)) == NULL) { - wpa_printf(MSG_INFO, "EAP-pwd: unable to create EC_GROUP"); - goto fail; - } - - if (((rnd = BN_new()) == NULL) || - ((cofactor = BN_new()) == NULL) || - ((grp->pwe = EC_POINT_new(grp->group)) == NULL) || - ((grp->order = BN_new()) == NULL) || - ((grp->prime = BN_new()) == NULL) || - ((x_candidate = BN_new()) == NULL)) { + prime = crypto_ec_get_prime(grp->group); + cofactor = crypto_bignum_init(); + grp->pwe = crypto_ec_point_init(grp->group); + tmp1 = crypto_bignum_init(); + pm1 = crypto_bignum_init(); + one = crypto_bignum_init_set((const u8 *) "\x01", 1); + if (!cofactor || !grp->pwe || !tmp1 || !pm1 || !one) { wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums"); goto fail; } - if (!EC_GROUP_get_curve_GFp(grp->group, grp->prime, NULL, NULL, NULL)) - { - wpa_printf(MSG_INFO, "EAP-pwd: unable to get prime for GFp " - "curve"); - goto fail; - } - if (!EC_GROUP_get_order(grp->group, grp->order, NULL)) { - wpa_printf(MSG_INFO, "EAP-pwd: unable to get order for curve"); - goto fail; - } - if (!EC_GROUP_get_cofactor(grp->group, cofactor, NULL)) { + if (crypto_ec_cofactor(grp->group, cofactor) < 0) { wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for " "curve"); goto fail; } - primebitlen = BN_num_bits(grp->prime); - primebytelen = BN_num_bytes(grp->prime); + primebitlen = crypto_ec_prime_len_bits(grp->group); + primebytelen = crypto_ec_prime_len(grp->group); if ((prfbuf = os_malloc(primebytelen)) == NULL) { wpa_printf(MSG_INFO, "EAP-pwd: unable to malloc space for prf " "buffer"); goto fail; } - os_memset(prfbuf, 0, primebytelen); - ctr = 0; - while (1) { - if (ctr > 30) { - wpa_printf(MSG_INFO, "EAP-pwd: unable to find random " - "point on curve for group %d, something's " - "fishy", num); + if (crypto_bignum_sub(prime, one, pm1) < 0) + goto fail; + + /* get a random quadratic residue and nonresidue */ + while (!qr || !qnr) { + int res; + + if (crypto_bignum_rand(tmp1, prime) < 0) goto fail; + res = crypto_bignum_legendre(tmp1, prime); + if (!qr && res == 1) { + qr = tmp1; + tmp1 = crypto_bignum_init(); + } else if (!qnr && res == -1) { + qnr = tmp1; + tmp1 = crypto_bignum_init(); } + if (!tmp1) + goto fail; + } + + os_memset(prfbuf, 0, primebytelen); + ctr = 0; + + /* + * Run through the hunting-and-pecking loop 40 times to mask the time + * necessary to find PWE. The odds of PWE not being found in 40 loops is + * roughly 1 in 1 trillion. + */ + while (ctr < 40) { ctr++; /* @@ -207,15 +194,25 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, eap_pwd_h_update(hash, &ctr, sizeof(ctr)); eap_pwd_h_final(hash, pwe_digest); - BN_bin2bn(pwe_digest, SHA256_MAC_LEN, rnd); - + crypto_bignum_deinit(rnd, 1); + rnd = crypto_bignum_init_set(pwe_digest, SHA256_MAC_LEN); + if (!rnd) { + wpa_printf(MSG_INFO, "EAP-pwd: unable to create rnd"); + goto fail; + } if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN, (u8 *) "EAP-pwd Hunting And Pecking", os_strlen("EAP-pwd Hunting And Pecking"), prfbuf, primebitlen) < 0) goto fail; - BN_bin2bn(prfbuf, primebytelen, x_candidate); + crypto_bignum_deinit(x_candidate, 1); + x_candidate = crypto_bignum_init_set(prfbuf, primebytelen); + if (!x_candidate) { + wpa_printf(MSG_INFO, + "EAP-pwd: unable to create x_candidate"); + goto fail; + } /* * eap_pwd_kdf() returns a string of bits 0..primebitlen but @@ -224,97 +221,157 @@ int compute_password_element(EAP_PWD_group *grp, u16 num, * then excessive bits-- those _after_ primebitlen-- so now * we have to shift right the amount we masked off. */ - if (primebitlen % 8) - BN_rshift(x_candidate, x_candidate, - (8 - (primebitlen % 8))); + if ((primebitlen % 8) && + crypto_bignum_rshift(x_candidate, + (8 - (primebitlen % 8)), + x_candidate) < 0) + goto fail; - if (BN_ucmp(x_candidate, grp->prime) >= 0) + if (crypto_bignum_cmp(x_candidate, prime) >= 0) continue; wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate", prfbuf, primebytelen); /* - * need to unambiguously identify the solution, if there is - * one... + * compute y^2 using the equation of the curve + * + * y^2 = x^3 + ax + b */ - if (BN_is_odd(rnd)) - is_odd = 1; - else - is_odd = 0; + tmp2 = crypto_ec_point_compute_y_sqr(grp->group, x_candidate); + if (!tmp2) + goto fail; /* - * solve the quadratic equation, if it's not solvable then we - * don't have a point + * mask tmp2 so doing legendre won't leak timing info + * + * tmp1 is a random number between 1 and p-1 */ - if (!EC_POINT_set_compressed_coordinates_GFp(grp->group, - grp->pwe, - x_candidate, - is_odd, NULL)) - continue; + if (crypto_bignum_rand(tmp1, pm1) < 0 || + crypto_bignum_mulmod(tmp2, tmp1, prime, tmp2) < 0 || + crypto_bignum_mulmod(tmp2, tmp1, prime, tmp2) < 0) + goto fail; + /* - * If there's a solution to the equation then the point must be - * on the curve so why check again explicitly? OpenSSL code - * says this is required by X9.62. We're not X9.62 but it can't - * hurt just to be sure. + * Now tmp2 (y^2) is masked, all values between 1 and p-1 + * are equally probable. Multiplying by r^2 does not change + * whether or not tmp2 is a quadratic residue, just masks it. + * + * Flip a coin, multiply by the random quadratic residue or the + * random quadratic nonresidue and record heads or tails. */ - if (!EC_POINT_is_on_curve(grp->group, grp->pwe, NULL)) { - wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve"); - continue; + if (crypto_bignum_is_odd(tmp1)) { + crypto_bignum_mulmod(tmp2, qr, prime, tmp2); + check = 1; + } else { + crypto_bignum_mulmod(tmp2, qnr, prime, tmp2); + check = -1; } - if (BN_cmp(cofactor, BN_value_one())) { - /* make sure the point is not in a small sub-group */ - if (!EC_POINT_mul(grp->group, grp->pwe, NULL, grp->pwe, - cofactor, NULL)) { - wpa_printf(MSG_INFO, "EAP-pwd: cannot " - "multiply generator by order"); + /* + * Now it's safe to do legendre, if check is 1 then it's + * a straightforward test (multiplying by qr does not + * change result), if check is -1 then it's the opposite test + * (multiplying a qr by qnr would make a qnr). + */ + if (crypto_bignum_legendre(tmp2, prime) == check) { + if (found == 1) + continue; + + /* need to unambiguously identify the solution */ + is_odd = crypto_bignum_is_odd(rnd); + + /* + * We know x_candidate is a quadratic residue so set + * it here. + */ + if (crypto_ec_point_solve_y_coord(grp->group, grp->pwe, + x_candidate, + is_odd) != 0) { + wpa_printf(MSG_INFO, + "EAP-pwd: Could not solve for y"); continue; } - if (EC_POINT_is_at_infinity(grp->group, grp->pwe)) { - wpa_printf(MSG_INFO, "EAP-pwd: point is at " - "infinity"); + + /* + * If there's a solution to the equation then the point + * must be on the curve so why check again explicitly? + * OpenSSL code says this is required by X9.62. We're + * not X9.62 but it can't hurt just to be sure. + */ + if (!crypto_ec_point_is_on_curve(grp->group, + grp->pwe)) { + wpa_printf(MSG_INFO, + "EAP-pwd: point is not on curve"); continue; } + + if (!crypto_bignum_is_one(cofactor)) { + /* make sure the point is not in a small + * sub-group */ + if (crypto_ec_point_mul(grp->group, grp->pwe, + cofactor, + grp->pwe) != 0) { + wpa_printf(MSG_INFO, + "EAP-pwd: cannot multiply generator by order"); + continue; + } + if (crypto_ec_point_is_at_infinity(grp->group, + grp->pwe)) { + wpa_printf(MSG_INFO, + "EAP-pwd: point is at infinity"); + continue; + } + } + wpa_printf(MSG_DEBUG, + "EAP-pwd: found a PWE in %d tries", ctr); + found = 1; } - /* if we got here then we have a new generator. */ - break; } - wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %d tries", ctr); - grp->group_num = num; + if (found == 0) { + wpa_printf(MSG_INFO, + "EAP-pwd: unable to find random point on curve for group %d, something's fishy", + num); + goto fail; + } if (0) { fail: - EC_GROUP_free(grp->group); - grp->group = NULL; - EC_POINT_clear_free(grp->pwe); + crypto_ec_point_deinit(grp->pwe, 1); grp->pwe = NULL; - BN_clear_free(grp->order); - grp->order = NULL; - BN_clear_free(grp->prime); - grp->prime = NULL; ret = 1; } /* cleanliness and order.... */ - BN_clear_free(cofactor); - BN_clear_free(x_candidate); - BN_clear_free(rnd); + crypto_bignum_deinit(cofactor, 1); + crypto_bignum_deinit(x_candidate, 1); + crypto_bignum_deinit(rnd, 1); + crypto_bignum_deinit(pm1, 0); + crypto_bignum_deinit(tmp1, 1); + crypto_bignum_deinit(tmp2, 1); + crypto_bignum_deinit(qr, 1); + crypto_bignum_deinit(qnr, 1); + crypto_bignum_deinit(one, 0); os_free(prfbuf); return ret; } -int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k, - const BIGNUM *peer_scalar, const BIGNUM *server_scalar, +int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k, + const struct crypto_bignum *peer_scalar, + const struct crypto_bignum *server_scalar, const u8 *confirm_peer, const u8 *confirm_server, const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id) { struct crypto_hash *hash; u8 mk[SHA256_MAC_LEN], *cruft; u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN]; - int offset; + size_t prime_len, order_len; + + prime_len = crypto_ec_prime_len(grp->group); + order_len = crypto_ec_order_len(grp->group); - if ((cruft = os_malloc(BN_num_bytes(grp->prime))) == NULL) + cruft = os_malloc(prime_len); + if (!cruft) return -1; /* @@ -328,14 +385,10 @@ int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k, return -1; } eap_pwd_h_update(hash, (const u8 *) ciphersuite, sizeof(u32)); - offset = BN_num_bytes(grp->order) - BN_num_bytes(peer_scalar); - os_memset(cruft, 0, BN_num_bytes(grp->prime)); - BN_bn2bin(peer_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order)); - offset = BN_num_bytes(grp->order) - BN_num_bytes(server_scalar); - os_memset(cruft, 0, BN_num_bytes(grp->prime)); - BN_bn2bin(server_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->order)); + crypto_bignum_to_bin(peer_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); + crypto_bignum_to_bin(server_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); eap_pwd_h_final(hash, &session_id[1]); /* then compute MK = H(k | confirm-peer | confirm-server) */ @@ -344,10 +397,8 @@ int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k, os_free(cruft); return -1; } - offset = BN_num_bytes(grp->prime) - BN_num_bytes(k); - os_memset(cruft, 0, BN_num_bytes(grp->prime)); - BN_bn2bin(k, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(grp->prime)); + crypto_bignum_to_bin(k, cruft, prime_len, prime_len); + eap_pwd_h_update(hash, cruft, prime_len); os_free(cruft); eap_pwd_h_update(hash, confirm_peer, SHA256_MAC_LEN); eap_pwd_h_update(hash, confirm_server, SHA256_MAC_LEN); diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h index a0d717edfe7a8..6b07cf8f797c8 100644 --- a/src/eap_common/eap_pwd_common.h +++ b/src/eap_common/eap_pwd_common.h @@ -9,20 +9,14 @@ #ifndef EAP_PWD_COMMON_H #define EAP_PWD_COMMON_H -#include <openssl/bn.h> -#include <openssl/ec.h> -#include <openssl/evp.h> - /* * definition of a finite cyclic group * TODO: support one based on a prime field */ typedef struct group_definition_ { u16 group_num; - EC_GROUP *group; - EC_POINT *pwe; - BIGNUM *order; - BIGNUM *prime; + struct crypto_ec *group; + struct crypto_ec_point *pwe; } EAP_PWD_group; /* @@ -52,17 +46,22 @@ struct eap_pwd_id { u8 prep; #define EAP_PWD_PREP_NONE 0 #define EAP_PWD_PREP_MS 1 +#define EAP_PWD_PREP_SSHA1 3 +#define EAP_PWD_PREP_SSHA256 4 +#define EAP_PWD_PREP_SSHA512 5 u8 identity[0]; /* length inferred from payload */ } STRUCT_PACKED; /* common routines */ +EAP_PWD_group * get_eap_pwd_group(u16 num); int compute_password_element(EAP_PWD_group *grp, u16 num, const u8 *password, size_t password_len, const u8 *id_server, size_t id_server_len, const u8 *id_peer, size_t id_peer_len, const u8 *token); -int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, const BIGNUM *k, - const BIGNUM *peer_scalar, const BIGNUM *server_scalar, +int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k, + const struct crypto_bignum *peer_scalar, + const struct crypto_bignum *server_scalar, const u8 *confirm_peer, const u8 *confirm_server, const u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id); struct crypto_hash * eap_pwd_h_init(void); diff --git a/src/eap_common/eap_sim_common.c b/src/eap_common/eap_sim_common.c index 2adc3b376a8ef..6290c35f1a6b8 100644 --- a/src/eap_common/eap_sim_common.c +++ b/src/eap_common/eap_sim_common.c @@ -175,7 +175,7 @@ int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req, mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN) return -1; - tmp = os_malloc(wpabuf_len(req)); + tmp = os_memdup(wpabuf_head(req), wpabuf_len(req)); if (tmp == NULL) return -1; @@ -185,7 +185,6 @@ int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req, len[1] = extra_len; /* HMAC-SHA1-128 */ - os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req)); os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - msg", tmp, wpabuf_len(req)); @@ -370,7 +369,7 @@ int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req, mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN) return -1; - tmp = os_malloc(wpabuf_len(req)); + tmp = os_memdup(wpabuf_head(req), wpabuf_len(req)); if (tmp == NULL) return -1; @@ -380,7 +379,6 @@ int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req, len[1] = extra_len; /* HMAC-SHA-256-128 */ - os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req)); os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - msg", tmp, wpabuf_len(req)); @@ -943,10 +941,9 @@ u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, return NULL; } - decrypted = os_malloc(encr_data_len); + decrypted = os_memdup(encr_data, encr_data_len); if (decrypted == NULL) return NULL; - os_memcpy(decrypted, encr_data, encr_data_len); if (aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len)) { os_free(decrypted); diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c index 9110ca5b9cfd6..974c475ff2d4e 100644 --- a/src/eap_peer/eap.c +++ b/src/eap_peer/eap.c @@ -95,6 +95,14 @@ static void eap_notify_status(struct eap_sm *sm, const char *status, } +static void eap_report_error(struct eap_sm *sm, int error_code) +{ + wpa_printf(MSG_DEBUG, "EAP: Error notification: %d", error_code); + if (sm->eapol_cb->notify_eap_error) + sm->eapol_cb->notify_eap_error(sm->eapol_ctx, error_code); +} + + static void eap_sm_free_key(struct eap_sm *sm) { if (sm->eapKeyData) { @@ -121,15 +129,17 @@ static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt) /** - * eap_allowed_method - Check whether EAP method is allowed + * eap_config_allowed_method - Check whether EAP method is allowed * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @config: EAP configuration * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types * @method: EAP type * Returns: 1 = allowed EAP method, 0 = not allowed */ -int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method) +static int eap_config_allowed_method(struct eap_sm *sm, + struct eap_peer_config *config, + int vendor, u32 method) { - struct eap_peer_config *config = eap_get_config(sm); int i; struct eap_method_type *m; @@ -146,6 +156,57 @@ int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method) } +/** + * eap_allowed_method - Check whether EAP method is allowed + * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init() + * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types + * @method: EAP type + * Returns: 1 = allowed EAP method, 0 = not allowed + */ +int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method) +{ + return eap_config_allowed_method(sm, eap_get_config(sm), vendor, + method); +} + + +#if defined(PCSC_FUNCS) || defined(CONFIG_EAP_PROXY) +static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi, + size_t max_len, size_t *imsi_len, + int mnc_len) +{ + char *pos, mnc[4]; + + if (*imsi_len + 36 > max_len) { + wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer"); + return -1; + } + + if (mnc_len != 2 && mnc_len != 3) + mnc_len = 3; + + if (mnc_len == 2) { + mnc[0] = '0'; + mnc[1] = imsi[3]; + mnc[2] = imsi[4]; + } else if (mnc_len == 3) { + mnc[0] = imsi[3]; + mnc[1] = imsi[4]; + mnc[2] = imsi[5]; + } + mnc[3] = '\0'; + + pos = imsi + *imsi_len; + pos += os_snprintf(pos, imsi + max_len - pos, + "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org", + mnc, imsi[0], imsi[1], imsi[2]); + *imsi_len = pos - imsi; + + return 0; +} +#endif /* PCSC_FUNCS || CONFIG_EAP_PROXY */ + + /* * This state initializes state machine variables when the machine is * activated (portEnabled = TRUE). This is also used when re-starting @@ -371,9 +432,8 @@ nak: #ifdef CONFIG_ERP -static char * eap_home_realm(struct eap_sm *sm) +static char * eap_get_realm(struct eap_sm *sm, struct eap_peer_config *config) { - struct eap_peer_config *config = eap_get_config(sm); char *realm; size_t i, realm_len; @@ -413,7 +473,51 @@ static char * eap_home_realm(struct eap_sm *sm) } } - return os_strdup(""); +#ifdef CONFIG_EAP_PROXY + /* When identity is not provided in the config, build the realm from + * IMSI for eap_proxy based methods. + */ + if (!config->identity && !config->anonymous_identity && + sm->eapol_cb->get_imsi && + (eap_config_allowed_method(sm, config, EAP_VENDOR_IETF, + EAP_TYPE_SIM) || + eap_config_allowed_method(sm, config, EAP_VENDOR_IETF, + EAP_TYPE_AKA) || + eap_config_allowed_method(sm, config, EAP_VENDOR_IETF, + EAP_TYPE_AKA_PRIME))) { + char imsi[100]; + size_t imsi_len; + int mnc_len, pos; + + wpa_printf(MSG_DEBUG, "EAP: Build realm from IMSI (eap_proxy)"); + mnc_len = sm->eapol_cb->get_imsi(sm->eapol_ctx, config->sim_num, + imsi, &imsi_len); + if (mnc_len < 0) + return NULL; + + pos = imsi_len + 1; /* points to the beginning of the realm */ + if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len, + mnc_len) < 0) { + wpa_printf(MSG_WARNING, "Could not append realm"); + return NULL; + } + + realm = os_strdup(&imsi[pos]); + if (!realm) + return NULL; + + wpa_printf(MSG_DEBUG, "EAP: Generated realm '%s'", realm); + return realm; + } +#endif /* CONFIG_EAP_PROXY */ + + return NULL; +} + + +static char * eap_home_realm(struct eap_sm *sm) +{ + return eap_get_realm(sm, eap_get_config(sm)); } @@ -469,6 +573,89 @@ static void eap_erp_remove_keys_realm(struct eap_sm *sm, const char *realm) } } + +int eap_peer_update_erp_next_seq_num(struct eap_sm *sm, u16 next_seq_num) +{ + struct eap_erp_key *erp; + char *home_realm; + + home_realm = eap_home_realm(sm); + if (!home_realm || os_strlen(home_realm) == 0) { + os_free(home_realm); + return -1; + } + + erp = eap_erp_get_key(sm, home_realm); + if (!erp) { + wpa_printf(MSG_DEBUG, + "EAP: Failed to find ERP key for realm: %s", + home_realm); + os_free(home_realm); + return -1; + } + + if ((u32) next_seq_num < erp->next_seq) { + /* Sequence number has wrapped around, clear this ERP + * info and do a full auth next time. + */ + eap_peer_erp_free_key(erp); + } else { + erp->next_seq = (u32) next_seq_num; + } + + os_free(home_realm); + return 0; +} + + +int eap_peer_get_erp_info(struct eap_sm *sm, struct eap_peer_config *config, + const u8 **username, size_t *username_len, + const u8 **realm, size_t *realm_len, + u16 *erp_next_seq_num, const u8 **rrk, + size_t *rrk_len) +{ + struct eap_erp_key *erp; + char *home_realm; + char *pos; + + if (config) + home_realm = eap_get_realm(sm, config); + else + home_realm = eap_home_realm(sm); + if (!home_realm || os_strlen(home_realm) == 0) { + os_free(home_realm); + return -1; + } + + erp = eap_erp_get_key(sm, home_realm); + os_free(home_realm); + if (!erp) + return -1; + + if (erp->next_seq >= 65536) + return -1; /* SEQ has range of 0..65535 */ + + pos = os_strchr(erp->keyname_nai, '@'); + if (!pos) + return -1; /* this cannot really happen */ + *username_len = pos - erp->keyname_nai; + *username = (u8 *) erp->keyname_nai; + + pos++; + *realm_len = os_strlen(pos); + *realm = (u8 *) pos; + + *erp_next_seq_num = (u16) erp->next_seq; + + *rrk_len = erp->rRK_len; + *rrk = erp->rRK; + + if (*username_len == 0 || *realm_len == 0 || *rrk_len == 0) + return -1; + + return 0; +} + #endif /* CONFIG_ERP */ @@ -483,13 +670,20 @@ void eap_peer_erp_free_keys(struct eap_sm *sm) } -static void eap_peer_erp_init(struct eap_sm *sm) +/* Note: If ext_session and/or ext_emsk are passed to this function, they are + * expected to point to allocated memory and those allocations will be freed + * unconditionally. */ +void eap_peer_erp_init(struct eap_sm *sm, u8 *ext_session_id, + size_t ext_session_id_len, u8 *ext_emsk, + size_t ext_emsk_len) { #ifdef CONFIG_ERP u8 *emsk = NULL; size_t emsk_len = 0; + u8 *session_id = NULL; + size_t session_id_len = 0; u8 EMSKname[EAP_EMSK_NAME_LEN]; - u8 len[2]; + u8 len[2], ctx[3]; char *realm; size_t realm_len, nai_buf_len; struct eap_erp_key *erp = NULL; @@ -497,7 +691,7 @@ static void eap_peer_erp_init(struct eap_sm *sm) realm = eap_home_realm(sm); if (!realm) - return; + goto fail; realm_len = os_strlen(realm); wpa_printf(MSG_DEBUG, "EAP: Realm for ERP keyName-NAI: %s", realm); eap_erp_remove_keys_realm(sm, realm); @@ -517,7 +711,13 @@ static void eap_peer_erp_init(struct eap_sm *sm) if (erp == NULL) goto fail; - emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len); + if (ext_emsk) { + emsk = ext_emsk; + emsk_len = ext_emsk_len; + } else { + emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len); + } + if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) { wpa_printf(MSG_DEBUG, "EAP: No suitable EMSK available for ERP"); @@ -526,10 +726,23 @@ static void eap_peer_erp_init(struct eap_sm *sm) wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len); - WPA_PUT_BE16(len, 8); - if (hmac_sha256_kdf(sm->eapSessionId, sm->eapSessionIdLen, "EMSK", - len, sizeof(len), - EMSKname, EAP_EMSK_NAME_LEN) < 0) { + if (ext_session_id) { + session_id = ext_session_id; + session_id_len = ext_session_id_len; + } else { + session_id = sm->eapSessionId; + session_id_len = sm->eapSessionIdLen; + } + + if (!session_id || session_id_len == 0) { + wpa_printf(MSG_DEBUG, + "EAP: No suitable session id available for ERP"); + goto fail; + } + + WPA_PUT_BE16(len, EAP_EMSK_NAME_LEN); + if (hmac_sha256_kdf(session_id, session_id_len, "EMSK", len, + sizeof(len), EMSKname, EAP_EMSK_NAME_LEN) < 0) { wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname"); goto fail; } @@ -550,9 +763,11 @@ static void eap_peer_erp_init(struct eap_sm *sm) erp->rRK_len = emsk_len; wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len); + ctx[0] = EAP_ERP_CS_HMAC_SHA256_128; + WPA_PUT_BE16(&ctx[1], erp->rRK_len); if (hmac_sha256_kdf(erp->rRK, erp->rRK_len, - "EAP Re-authentication Integrity Key@ietf.org", - len, sizeof(len), erp->rIK, erp->rRK_len) < 0) { + "Re-authentication Integrity Key@ietf.org", + ctx, sizeof(ctx), erp->rIK, erp->rRK_len) < 0) { wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP"); goto fail; } @@ -563,7 +778,11 @@ static void eap_peer_erp_init(struct eap_sm *sm) dl_list_add(&sm->erp_keys, &erp->list); erp = NULL; fail: - bin_clear_free(emsk, emsk_len); + if (ext_emsk) + bin_clear_free(ext_emsk, ext_emsk_len); + else + bin_clear_free(emsk, emsk_len); + bin_clear_free(ext_session_id, ext_session_id_len); bin_clear_free(erp, sizeof(*erp)); os_free(realm); #endif /* CONFIG_ERP */ @@ -571,8 +790,7 @@ fail: #ifdef CONFIG_ERP -static int eap_peer_erp_reauth_start(struct eap_sm *sm, - const struct eap_hdr *hdr, size_t len) +struct wpabuf * eap_peer_build_erp_reauth_start(struct eap_sm *sm, u8 eap_id) { char *realm; struct eap_erp_key *erp; @@ -581,16 +799,16 @@ static int eap_peer_erp_reauth_start(struct eap_sm *sm, realm = eap_home_realm(sm); if (!realm) - return -1; + return NULL; erp = eap_erp_get_key(sm, realm); os_free(realm); realm = NULL; if (!erp) - return -1; + return NULL; if (erp->next_seq >= 65536) - return -1; /* SEQ has range of 0..65535 */ + return NULL; /* SEQ has range of 0..65535 */ /* TODO: check rRK lifetime expiration */ @@ -599,9 +817,9 @@ static int eap_peer_erp_reauth_start(struct eap_sm *sm, msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH, 1 + 2 + 2 + os_strlen(erp->keyname_nai) + 1 + 16, - EAP_CODE_INITIATE, hdr->identifier); + EAP_CODE_INITIATE, eap_id); if (msg == NULL) - return -1; + return NULL; wpabuf_put_u8(msg, 0x20); /* Flags: R=0 B=0 L=1 */ wpabuf_put_be16(msg, erp->next_seq); @@ -615,13 +833,28 @@ static int eap_peer_erp_reauth_start(struct eap_sm *sm, if (hmac_sha256(erp->rIK, erp->rIK_len, wpabuf_head(msg), wpabuf_len(msg), hash) < 0) { wpabuf_free(msg); - return -1; + return NULL; } wpabuf_put_data(msg, hash, 16); - wpa_printf(MSG_DEBUG, "EAP: Sending EAP-Initiate/Re-auth"); sm->erp_seq = erp->next_seq; erp->next_seq++; + + wpa_hexdump_buf(MSG_DEBUG, "ERP: EAP-Initiate/Re-auth", msg); + + return msg; +} + + +static int eap_peer_erp_reauth_start(struct eap_sm *sm, u8 eap_id) +{ + struct wpabuf *msg; + + msg = eap_peer_build_erp_reauth_start(sm, eap_id); + if (!msg) + return -1; + + wpa_printf(MSG_DEBUG, "EAP: Sending EAP-Initiate/Re-auth"); wpabuf_free(sm->eapRespData); sm->eapRespData = msg; sm->reauthInit = TRUE; @@ -691,8 +924,6 @@ SM_STATE(EAP, METHOD) if (sm->m->isKeyAvailable && sm->m->getKey && sm->m->isKeyAvailable(sm, sm->eap_method_priv)) { - struct eap_peer_config *config = eap_get_config(sm); - eap_sm_free_key(sm); sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv, &sm->eapKeyDataLen); @@ -705,8 +936,6 @@ SM_STATE(EAP, METHOD) wpa_hexdump(MSG_DEBUG, "EAP: Session-Id", sm->eapSessionId, sm->eapSessionIdLen); } - if (config->erp && sm->m->get_emsk && sm->eapSessionId) - eap_peer_erp_init(sm); } } @@ -804,6 +1033,8 @@ SM_STATE(EAP, RETRANSMIT) */ SM_STATE(EAP, SUCCESS) { + struct eap_peer_config *config = eap_get_config(sm); + SM_ENTRY(EAP, SUCCESS); if (sm->eapKeyData != NULL) sm->eapKeyAvailable = TRUE; @@ -826,6 +1057,11 @@ SM_STATE(EAP, SUCCESS) wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS "EAP authentication completed successfully"); + + if (config->erp && sm->m->get_emsk && sm->eapSessionId && + sm->m->isKeyAvailable && + sm->m->isKeyAvailable(sm, sm->eap_method_priv)) + eap_peer_erp_init(sm, NULL, 0, NULL, 0); } @@ -1276,48 +1512,6 @@ static int mnc_len_from_imsi(const char *imsi) } -static int eap_sm_append_3gpp_realm(struct eap_sm *sm, char *imsi, - size_t max_len, size_t *imsi_len) -{ - int mnc_len; - char *pos, mnc[4]; - - if (*imsi_len + 36 > max_len) { - wpa_printf(MSG_WARNING, "No room for realm in IMSI buffer"); - return -1; - } - - /* MNC (2 or 3 digits) */ - mnc_len = scard_get_mnc_len(sm->scard_ctx); - if (mnc_len < 0) - mnc_len = mnc_len_from_imsi(imsi); - if (mnc_len < 0) { - wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM " - "assuming 3"); - mnc_len = 3; - } - - if (mnc_len == 2) { - mnc[0] = '0'; - mnc[1] = imsi[3]; - mnc[2] = imsi[4]; - } else if (mnc_len == 3) { - mnc[0] = imsi[3]; - mnc[1] = imsi[4]; - mnc[2] = imsi[5]; - } - mnc[3] = '\0'; - - pos = imsi + *imsi_len; - pos += os_snprintf(pos, imsi + max_len - pos, - "@wlan.mnc%s.mcc%c%c%c.3gppnetwork.org", - mnc, imsi[0], imsi[1], imsi[2]); - *imsi_len = pos - imsi; - - return 0; -} - - static int eap_sm_imsi_identity(struct eap_sm *sm, struct eap_peer_config *conf) { @@ -1325,7 +1519,7 @@ static int eap_sm_imsi_identity(struct eap_sm *sm, char imsi[100]; size_t imsi_len; struct eap_method_type *m = conf->eap_methods; - int i; + int i, mnc_len; imsi_len = sizeof(imsi); if (scard_get_imsi(sm->scard_ctx, imsi, &imsi_len)) { @@ -1340,7 +1534,18 @@ static int eap_sm_imsi_identity(struct eap_sm *sm, return -1; } - if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len) < 0) { + /* MNC (2 or 3 digits) */ + mnc_len = scard_get_mnc_len(sm->scard_ctx); + if (mnc_len < 0) + mnc_len = mnc_len_from_imsi(imsi); + if (mnc_len < 0) { + wpa_printf(MSG_INFO, "Failed to get MNC length from (U)SIM " + "assuming 3"); + mnc_len = 3; + } + + if (eap_sm_append_3gpp_realm(sm, imsi, sizeof(imsi), &imsi_len, + mnc_len) < 0) { wpa_printf(MSG_WARNING, "Could not add realm to SIM identity"); return -1; } @@ -1566,7 +1771,7 @@ static void eap_peer_initiate(struct eap_sm *sm, const struct eap_hdr *hdr, /* TODO: Derivation of domain specific keys for local ER */ } - if (eap_peer_erp_reauth_start(sm, hdr, len) == 0) + if (eap_peer_erp_reauth_start(sm, hdr->identifier) == 0) return; invalid: @@ -1577,8 +1782,7 @@ invalid: } -static void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr, - size_t len) +void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr, size_t len) { #ifdef CONFIG_ERP const u8 *pos = (const u8 *) (hdr + 1); @@ -1828,6 +2032,15 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) case EAP_CODE_FAILURE: wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure"); eap_notify_status(sm, "completion", "failure"); + + /* Get the error code from method */ + if (sm->m && sm->m->get_error_code) { + int error_code; + + error_code = sm->m->get_error_code(sm->eap_method_priv); + if (error_code != NO_EAP_METHOD_ERROR) + eap_report_error(sm, error_code); + } sm->rxFailure = TRUE; break; case EAP_CODE_INITIATE: @@ -2231,6 +2444,7 @@ static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field, config->pending_req_passphrase++; break; case WPA_CTRL_REQ_SIM: + config->pending_req_sim++; txt = msg; break; case WPA_CTRL_REQ_EXT_CERT_CHECK: diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h index 1a645af8b2003..d0837e37a0e08 100644 --- a/src/eap_peer/eap.h +++ b/src/eap_peer/eap.h @@ -246,12 +246,37 @@ struct eapol_callbacks { void (*notify_status)(void *ctx, const char *status, const char *parameter); + /** + * notify_eap_error - Report EAP method error code + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @error_code: Error code from the used EAP method + */ + void (*notify_eap_error)(void *ctx, int error_code); + #ifdef CONFIG_EAP_PROXY /** * eap_proxy_cb - Callback signifying any updates from eap_proxy * @ctx: eapol_ctx from eap_peer_sm_init() call */ void (*eap_proxy_cb)(void *ctx); + + /** + * eap_proxy_notify_sim_status - Notification of SIM status change + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @sim_state: One of enum value from sim_state + */ + void (*eap_proxy_notify_sim_status)(void *ctx, + enum eap_proxy_sim_state sim_state); + + /** + * get_imsi - Get the IMSI value from eap_proxy + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @sim_num: SIM/USIM number to get the IMSI value for + * @imsi: Buffer for IMSI value + * @len: Buffer for returning IMSI length in octets + * Returns: MNC length (2 or 3) or -1 on error + */ + int (*get_imsi)(void *ctx, int sim_num, char *imsi, size_t *len); #endif /* CONFIG_EAP_PROXY */ /** @@ -348,6 +373,16 @@ void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext); void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len); int eap_peer_was_failure_expected(struct eap_sm *sm); void eap_peer_erp_free_keys(struct eap_sm *sm); +struct wpabuf * eap_peer_build_erp_reauth_start(struct eap_sm *sm, u8 eap_id); +void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr, size_t len); +int eap_peer_get_erp_info(struct eap_sm *sm, struct eap_peer_config *config, + const u8 **username, size_t *username_len, + const u8 **realm, size_t *realm_len, u16 *erp_seq_num, + const u8 **rrk, size_t *rrk_len); +int eap_peer_update_erp_next_seq_num(struct eap_sm *sm, u16 seq_num); +void eap_peer_erp_init(struct eap_sm *sm, u8 *ext_session_id, + size_t ext_session_id_len, u8 *ext_emsk, + size_t ext_emsk_len); #endif /* IEEE8021X_EAPOL */ diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c index 0bac62dee523a..a4441413f0ddd 100644 --- a/src/eap_peer/eap_aka.c +++ b/src/eap_peer/eap_aka.c @@ -48,11 +48,15 @@ struct eap_aka_data { struct wpabuf *id_msgs; int prev_id; int result_ind, use_result_ind; + int use_pseudonym; u8 eap_method; u8 *network_name; size_t network_name_len; u16 kdf; int kdf_negotiation; + u16 last_kdf_attrs[EAP_AKA_PRIME_KDF_MAX]; + size_t last_kdf_count; + int error_code; }; @@ -96,12 +100,16 @@ static void * eap_aka_init(struct eap_sm *sm) data->eap_method = EAP_TYPE_AKA; + /* Zero is a valid error code, so we need to initialize */ + data->error_code = NO_EAP_METHOD_ERROR; + eap_aka_state(data, CONTINUE); data->prev_id = -1; data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL; - if (config && config->anonymous_identity) { + data->use_pseudonym = !sm->init_phase2; + if (config && config->anonymous_identity && data->use_pseudonym) { data->pseudonym = os_malloc(config->anonymous_identity_len); if (data->pseudonym) { os_memcpy(data->pseudonym, config->anonymous_identity, @@ -350,7 +358,8 @@ static void eap_aka_clear_identities(struct eap_sm *sm, os_free(data->pseudonym); data->pseudonym = NULL; data->pseudonym_len = 0; - eap_set_anon_id(sm, NULL, 0); + if (data->use_pseudonym) + eap_set_anon_id(sm, NULL, 0); } if ((id & CLEAR_REAUTH_ID) && data->reauth_id) { wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id"); @@ -405,20 +414,21 @@ static int eap_aka_learn_ids(struct eap_sm *sm, struct eap_aka_data *data, realm, realm_len); } data->pseudonym_len = attr->next_pseudonym_len + realm_len; - eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len); + if (data->use_pseudonym) + eap_set_anon_id(sm, data->pseudonym, + data->pseudonym_len); } if (attr->next_reauth_id) { os_free(data->reauth_id); - data->reauth_id = os_malloc(attr->next_reauth_id_len); + data->reauth_id = os_memdup(attr->next_reauth_id, + attr->next_reauth_id_len); if (data->reauth_id == NULL) { wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for " "next reauth_id"); data->reauth_id_len = 0; return -1; } - os_memcpy(data->reauth_id, attr->next_reauth_id, - attr->next_reauth_id_len); data->reauth_id_len = attr->next_reauth_id_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: (encr) AT_NEXT_REAUTH_ID", @@ -570,7 +580,7 @@ static struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data, static struct wpabuf * eap_aka_synchronization_failure( - struct eap_aka_data *data, u8 id) + struct eap_aka_data *data, u8 id, struct eap_sim_attrs *attr) { struct eap_sim_msg *msg; @@ -584,6 +594,15 @@ static struct wpabuf * eap_aka_synchronization_failure( wpa_printf(MSG_DEBUG, " AT_AUTS"); eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts, EAP_AKA_AUTS_LEN); + if (data->eap_method == EAP_TYPE_AKA_PRIME) { + size_t i; + + for (i = 0; i < attr->kdf_count; i++) { + wpa_printf(MSG_DEBUG, " AT_KDF"); + eap_sim_msg_add(msg, EAP_SIM_AT_KDF, attr->kdf[i], + NULL, 0); + } + } return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0); } @@ -817,9 +836,13 @@ static struct wpabuf * eap_aka_prime_kdf_neg(struct eap_aka_data *data, size_t i; for (i = 0; i < attr->kdf_count; i++) { - if (attr->kdf[i] == EAP_AKA_PRIME_KDF) + if (attr->kdf[i] == EAP_AKA_PRIME_KDF) { + os_memcpy(data->last_kdf_attrs, attr->kdf, + sizeof(u16) * attr->kdf_count); + data->last_kdf_count = attr->kdf_count; return eap_aka_prime_kdf_select(data, id, EAP_AKA_PRIME_KDF); + } } /* No matching KDF found - fail authentication as if AUTN had been @@ -840,26 +863,32 @@ static int eap_aka_prime_kdf_valid(struct eap_aka_data *data, * of the selected KDF into the beginning of the list. */ if (data->kdf_negotiation) { + /* When the peer receives the new EAP-Request/AKA'-Challenge + * message, must check only requested change occurred in the + * list of AT_KDF attributes. If there are any other changes, + * the peer must behave like the case that AT_MAC had been + * incorrect and authentication is failed. These are defined in + * EAP-AKA' specification RFC 5448, Section 3.2. */ if (attr->kdf[0] != data->kdf) { wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " "accept the selected KDF"); - return 0; + return -1; } - for (i = 1; i < attr->kdf_count; i++) { - if (attr->kdf[i] == data->kdf) - break; - } - if (i == attr->kdf_count && - attr->kdf_count < EAP_AKA_PRIME_KDF_MAX) { - wpa_printf(MSG_WARNING, "EAP-AKA': The server did not " - "duplicate the selected KDF"); - return 0; + if (attr->kdf_count > EAP_AKA_PRIME_KDF_MAX || + attr->kdf_count != data->last_kdf_count + 1) { + wpa_printf(MSG_WARNING, + "EAP-AKA': The length of KDF attributes is wrong"); + return -1; } - /* TODO: should check that the list is identical to the one - * used in the previous Challenge message apart from the added - * entry in the beginning. */ + for (i = 1; i < attr->kdf_count; i++) { + if (attr->kdf[i] != data->last_kdf_attrs[i - 1]) { + wpa_printf(MSG_WARNING, + "EAP-AKA': The KDF attributes except selected KDF are not same as original one"); + return -1; + } + } } for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) { @@ -908,22 +937,25 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, return eap_aka_authentication_reject(data, id); } os_free(data->network_name); - data->network_name = os_malloc(attr->kdf_input_len); + data->network_name = os_memdup(attr->kdf_input, + attr->kdf_input_len); if (data->network_name == NULL) { wpa_printf(MSG_WARNING, "EAP-AKA': No memory for " "storing Network Name"); return eap_aka_authentication_reject(data, id); } - os_memcpy(data->network_name, attr->kdf_input, - attr->kdf_input_len); data->network_name_len = attr->kdf_input_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name " "(AT_KDF_INPUT)", data->network_name, data->network_name_len); /* TODO: check Network Name per 3GPP.33.402 */ - if (!eap_aka_prime_kdf_valid(data, attr)) + res = eap_aka_prime_kdf_valid(data, attr); + if (res == 0) return eap_aka_authentication_reject(data, id); + else if (res == -1) + return eap_aka_client_error( + data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET); if (attr->kdf[0] != EAP_AKA_PRIME_KDF) return eap_aka_prime_kdf_neg(data, id, attr); @@ -966,7 +998,7 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, } else if (res == -2) { wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication " "failed (AUTN seq# -> AUTS)"); - return eap_aka_synchronization_failure(data, id); + return eap_aka_synchronization_failure(data, id, attr); } else if (res > 0) { wpa_printf(MSG_DEBUG, "EAP-AKA: Wait for external USIM processing"); return NULL; @@ -997,8 +1029,17 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm, } else if (data->pseudonym) { identity = data->pseudonym; identity_len = data->pseudonym_len; - } else - identity = eap_get_config_identity(sm, &identity_len); + } else { + struct eap_peer_config *config; + + config = eap_get_config(sm); + if (config && config->imsi_identity) { + identity = config->imsi_identity; + identity_len = config->imsi_identity_len; + } else { + identity = eap_get_config_identity(sm, &identity_len); + } + } wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK " "derivation", identity, identity_len); if (data->eap_method == EAP_TYPE_AKA_PRIME) { @@ -1143,6 +1184,7 @@ static struct wpabuf * eap_aka_process_notification( eap_sim_report_notification(sm->msg_ctx, attr->notification, 1); if (attr->notification >= 0 && attr->notification < 32768) { + data->error_code = attr->notification; eap_aka_state(data, FAILURE); } else if (attr->notification == EAP_SIM_SUCCESS && data->state == RESULT_SUCCESS) @@ -1437,12 +1479,11 @@ static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_SIM_KEYING_DATA_LEN); + key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN); if (key == NULL) return NULL; *len = EAP_SIM_KEYING_DATA_LEN; - os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); return key; } @@ -1478,17 +1519,33 @@ static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); return key; } +static int eap_aka_get_error_code(void *priv) +{ + struct eap_aka_data *data = priv; + int current_data_error; + + if (!data) + return NO_EAP_METHOD_ERROR; + + current_data_error = data->error_code; + + /* Now reset for next transaction */ + data->error_code = NO_EAP_METHOD_ERROR; + + return current_data_error; +} + + int eap_peer_aka_register(void) { struct eap_method *eap; @@ -1509,6 +1566,7 @@ int eap_peer_aka_register(void) eap->init_for_reauth = eap_aka_init_for_reauth; eap->get_identity = eap_aka_get_identity; eap->get_emsk = eap_aka_get_emsk; + eap->get_error_code = eap_aka_get_error_code; return eap_peer_method_register(eap); } @@ -1536,6 +1594,7 @@ int eap_peer_aka_prime_register(void) eap->init_for_reauth = eap_aka_init_for_reauth; eap->get_identity = eap_aka_get_identity; eap->get_emsk = eap_aka_get_emsk; + eap->get_error_code = eap_aka_get_error_code; return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h index f98007263b338..d416afd56d59f 100644 --- a/src/eap_peer/eap_config.h +++ b/src/eap_peer/eap_config.h @@ -46,6 +46,9 @@ struct eap_peer_config { */ size_t anonymous_identity_len; + u8 *imsi_identity; + size_t imsi_identity_len; + /** * password - Password string for EAP * @@ -628,6 +631,15 @@ struct eap_peer_config { int pending_req_passphrase; /** + * pending_req_sim - Pending SIM request + * + * This field should not be set in configuration step. It is only used + * internally when control interface is used to request needed + * information. + */ + int pending_req_sim; + + /** * pending_req_otp - Whether there is a pending OTP request * * This field should not be set in configuration step. It is only used diff --git a/src/eap_peer/eap_eke.c b/src/eap_peer/eap_eke.c index f899f653fdca4..0de7d6cbf49bd 100644 --- a/src/eap_peer/eap_eke.c +++ b/src/eap_peer/eap_eke.c @@ -85,12 +85,11 @@ static void * eap_eke_init(struct eap_sm *sm) identity = eap_get_config_identity(sm, &identity_len); if (identity) { - data->peerid = os_malloc(identity_len); + data->peerid = os_memdup(identity, identity_len); if (data->peerid == NULL) { eap_eke_deinit(sm, data); return NULL; } - os_memcpy(data->peerid, identity, identity_len); data->peerid_len = identity_len; } @@ -310,12 +309,11 @@ static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data, wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Server Identity", pos, end - pos); os_free(data->serverid); - data->serverid = os_malloc(end - pos); + data->serverid = os_memdup(pos, end - pos); if (data->serverid == NULL) { return eap_eke_build_fail(data, ret, id, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); } - os_memcpy(data->serverid, pos, end - pos); data->serverid_len = end - pos; wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-ID/Response"); @@ -717,10 +715,9 @@ static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -735,10 +732,9 @@ static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c index 964ebe74fede8..74cec7dd583f8 100644 --- a/src/eap_peer/eap_fast.c +++ b/src/eap_peer/eap_fast.c @@ -484,7 +484,8 @@ static int eap_fast_phase2_request(struct eap_sm *sm, if (*resp == NULL && config && (config->pending_req_identity || config->pending_req_password || - config->pending_req_otp || config->pending_req_new_password)) { + config->pending_req_otp || config->pending_req_new_password || + config->pending_req_sim)) { wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); } else if (*resp == NULL) @@ -1677,6 +1678,10 @@ static Boolean eap_fast_has_reauth_data(struct eap_sm *sm, void *priv) static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv) { struct eap_fast_data *data = priv; + + if (data->phase2_priv && data->phase2_method && + data->phase2_method->deinit_for_reauth) + data->phase2_method->deinit_for_reauth(sm, data->phase2_priv); os_free(data->key_block_p); data->key_block_p = NULL; wpabuf_free(data->pending_phase2_req); @@ -1744,12 +1749,11 @@ static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len) if (!data->success) return NULL; - key = os_malloc(EAP_FAST_KEY_LEN); + key = os_memdup(data->key_data, EAP_FAST_KEY_LEN); if (key == NULL) return NULL; *len = EAP_FAST_KEY_LEN; - os_memcpy(key, data->key_data, EAP_FAST_KEY_LEN); return key; } @@ -1763,12 +1767,11 @@ static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (!data->success || !data->session_id) return NULL; - id = os_malloc(data->id_len); + id = os_memdup(data->session_id, data->id_len); if (id == NULL) return NULL; *len = data->id_len; - os_memcpy(id, data->session_id, data->id_len); return id; } @@ -1782,12 +1785,11 @@ static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (!data->success) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); return key; } diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c index c815860355137..7d674c8c0a701 100644 --- a/src/eap_peer/eap_fast_pac.c +++ b/src/eap_peer/eap_fast_pac.c @@ -114,10 +114,9 @@ static int eap_fast_copy_buf(u8 **dst, size_t *dst_len, const u8 *src, size_t src_len) { if (src) { - *dst = os_malloc(src_len); + *dst = os_memdup(src, src_len); if (*dst == NULL) return -1; - os_memcpy(*dst, src, src_len); *dst_len = src_len; } return 0; @@ -720,19 +719,17 @@ static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac) if (type == PAC_TYPE_A_ID) { os_free(pac->a_id); - pac->a_id = os_malloc(len); + pac->a_id = os_memdup(pos, len); if (pac->a_id == NULL) break; - os_memcpy(pac->a_id, pos, len); pac->a_id_len = len; } if (type == PAC_TYPE_A_ID_INFO) { os_free(pac->a_id_info); - pac->a_id_info = os_malloc(len); + pac->a_id_info = os_memdup(pos, len); if (pac->a_id_info == NULL) break; - os_memcpy(pac->a_id_info, pos, len); pac->a_id_info_len = len; } @@ -820,10 +817,9 @@ int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root, if (val > end - pos) goto parse_fail; pac->pac_opaque_len = val; - pac->pac_opaque = os_malloc(pac->pac_opaque_len); + pac->pac_opaque = os_memdup(pos, pac->pac_opaque_len); if (pac->pac_opaque == NULL) goto parse_fail; - os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len); pos += pac->pac_opaque_len; if (2 > end - pos) goto parse_fail; @@ -832,10 +828,9 @@ int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root, if (val > end - pos) goto parse_fail; pac->pac_info_len = val; - pac->pac_info = os_malloc(pac->pac_info_len); + pac->pac_info = os_memdup(pos, pac->pac_info_len); if (pac->pac_info == NULL) goto parse_fail; - os_memcpy(pac->pac_info, pos, pac->pac_info_len); pos += pac->pac_info_len; eap_fast_pac_get_a_id(pac); diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c index 177cbccf58508..f9c4d3773bf7a 100644 --- a/src/eap_peer/eap_gpsk.c +++ b/src/eap_peer/eap_gpsk.c @@ -96,12 +96,11 @@ static void * eap_gpsk_init(struct eap_sm *sm) identity = eap_get_config_identity(sm, &identity_len); if (identity) { - data->id_peer = os_malloc(identity_len); + data->id_peer = os_memdup(identity, identity_len); if (data->id_peer == NULL) { eap_gpsk_deinit(sm, data); return NULL; } - os_memcpy(data->id_peer, identity, identity_len); data->id_peer_len = identity_len; } @@ -117,12 +116,11 @@ static void * eap_gpsk_init(struct eap_sm *sm) } } - data->psk = os_malloc(password_len); + data->psk = os_memdup(password, password_len); if (data->psk == NULL) { eap_gpsk_deinit(sm, data); return NULL; } - os_memcpy(data->psk, password, password_len); data->psk_len = password_len; return data; @@ -158,12 +156,11 @@ static const u8 * eap_gpsk_process_id_server(struct eap_gpsk_data *data, return NULL; } os_free(data->id_server); - data->id_server = os_malloc(alen); + data->id_server = os_memdup(pos, alen); if (data->id_server == NULL) { wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server"); return NULL; } - os_memcpy(data->id_server, pos, alen); data->id_server_len = alen; wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server", data->id_server, data->id_server_len); @@ -722,10 +719,9 @@ static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -740,10 +736,9 @@ static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; @@ -758,10 +753,9 @@ static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - sid = os_malloc(data->id_len); + sid = os_memdup(data->session_id, data->id_len); if (sid == NULL) return NULL; - os_memcpy(sid, data->session_id, data->id_len); *len = data->id_len; return sid; diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h index 6ab24834d654a..096f0f28efca0 100644 --- a/src/eap_peer/eap_i.h +++ b/src/eap_peer/eap_i.h @@ -14,6 +14,8 @@ #include "eap_peer/eap.h" #include "eap_common/eap_common.h" +#define NO_EAP_METHOD_ERROR (-1) + /* RFC 4137 - EAP Peer state machine */ typedef enum { @@ -206,6 +208,17 @@ struct eap_method { const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len); /** + * get_error_code - Get the latest EAP method error code + * @priv: Pointer to private EAP method data from eap_method::init() + * Returns: An int for the EAP method specific error code if exists or + * NO_EAP_METHOD_ERROR otherwise. + * + * This method is an optional handler that only EAP methods that need to + * report their error code need to implement. + */ + int (*get_error_code)(void *priv); + + /** * free - Free EAP method data * @method: Pointer to the method data registered with * eap_peer_method_register(). diff --git a/src/eap_peer/eap_ikev2.c b/src/eap_peer/eap_ikev2.c index 390f0ec8cf4df..6ddf50835ade7 100644 --- a/src/eap_peer/eap_ikev2.c +++ b/src/eap_peer/eap_ikev2.c @@ -83,18 +83,16 @@ static void * eap_ikev2_init(struct eap_sm *sm) if (data->ikev2.key_pad == NULL) goto failed; data->ikev2.key_pad_len = 21; - data->ikev2.IDr = os_malloc(identity_len); + data->ikev2.IDr = os_memdup(identity, identity_len); if (data->ikev2.IDr == NULL) goto failed; - os_memcpy(data->ikev2.IDr, identity, identity_len); data->ikev2.IDr_len = identity_len; password = eap_get_config_password(sm, &password_len); if (password) { - data->ikev2.shared_secret = os_malloc(password_len); + data->ikev2.shared_secret = os_memdup(password, password_len); if (data->ikev2.shared_secret == NULL) goto failed; - os_memcpy(data->ikev2.shared_secret, password, password_len); data->ikev2.shared_secret_len = password_len; } diff --git a/src/eap_peer/eap_leap.c b/src/eap_peer/eap_leap.c index ff6fa4afd2f72..233b9eeb1f83f 100644 --- a/src/eap_peer/eap_leap.c +++ b/src/eap_peer/eap_leap.c @@ -115,10 +115,14 @@ static struct wpabuf * eap_leap_process_request(struct eap_sm *sm, void *priv, wpabuf_put_u8(resp, 0); /* unused */ wpabuf_put_u8(resp, LEAP_RESPONSE_LEN); rpos = wpabuf_put(resp, LEAP_RESPONSE_LEN); - if (pwhash) - challenge_response(challenge, password, rpos); - else - nt_challenge_response(challenge, password, password_len, rpos); + if ((pwhash && challenge_response(challenge, password, rpos)) || + (!pwhash && + nt_challenge_response(challenge, password, password_len, rpos))) { + wpa_printf(MSG_DEBUG, "EAP-LEAP: Failed to derive response"); + ret->ignore = TRUE; + wpabuf_free(resp); + return NULL; + } os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response", rpos, LEAP_RESPONSE_LEN); @@ -239,7 +243,10 @@ static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv, return NULL; } } - challenge_response(data->ap_challenge, pw_hash_hash, expected); + if (challenge_response(data->ap_challenge, pw_hash_hash, expected)) { + ret->ignore = TRUE; + return NULL; + } ret->methodState = METHOD_DONE; ret->allowNotifications = FALSE; diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c index ce2227d388ef4..877495cf3ac72 100644 --- a/src/eap_peer/eap_mschapv2.c +++ b/src/eap_peer/eap_mschapv2.c @@ -109,23 +109,21 @@ static void * eap_mschapv2_init(struct eap_sm *sm) return NULL; if (sm->peer_challenge) { - data->peer_challenge = os_malloc(MSCHAPV2_CHAL_LEN); + data->peer_challenge = os_memdup(sm->peer_challenge, + MSCHAPV2_CHAL_LEN); if (data->peer_challenge == NULL) { eap_mschapv2_deinit(sm, data); return NULL; } - os_memcpy(data->peer_challenge, sm->peer_challenge, - MSCHAPV2_CHAL_LEN); } if (sm->auth_challenge) { - data->auth_challenge = os_malloc(MSCHAPV2_CHAL_LEN); + data->auth_challenge = os_memdup(sm->auth_challenge, + MSCHAPV2_CHAL_LEN); if (data->auth_challenge == NULL) { eap_mschapv2_deinit(sm, data); return NULL; } - os_memcpy(data->auth_challenge, sm->auth_challenge, - MSCHAPV2_CHAL_LEN); } data->phase2 = sm->init_phase2; @@ -567,11 +565,11 @@ static struct wpabuf * eap_mschapv2_change_password( if (pwhash) { u8 new_password_hash[16]; if (nt_password_hash(new_password, new_password_len, - new_password_hash)) + new_password_hash) || + nt_password_hash_encrypted_with_block(password, + new_password_hash, + cp->encr_hash)) goto fail; - nt_password_hash_encrypted_with_block(password, - new_password_hash, - cp->encr_hash); } else { if (old_nt_password_hash_encrypted_with_new_nt_password_hash( new_password, new_password_len, diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c index a7012d2870cf3..3cef1c8800a22 100644 --- a/src/eap_peer/eap_pax.c +++ b/src/eap_peer/eap_pax.c @@ -69,12 +69,11 @@ static void * eap_pax_init(struct eap_sm *sm) return NULL; data->state = PAX_INIT; - data->cid = os_malloc(identity_len); + data->cid = os_memdup(identity, identity_len); if (data->cid == NULL) { eap_pax_deinit(sm, data); return NULL; } - os_memcpy(data->cid, identity, identity_len); data->cid_len = identity_len; os_memcpy(data->ak, password, EAP_PAX_AK_LEN); diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c index 45ba38168d4fc..34075b1d9af65 100644 --- a/src/eap_peer/eap_peap.c +++ b/src/eap_peer/eap_peap.c @@ -726,7 +726,8 @@ static int eap_peap_phase2_request(struct eap_sm *sm, if (*resp == NULL && (config->pending_req_identity || config->pending_req_password || - config->pending_req_otp || config->pending_req_new_password)) { + config->pending_req_otp || config->pending_req_new_password || + config->pending_req_sim)) { wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_alloc_copy(hdr, len); } @@ -1082,7 +1083,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, eap_peer_tls_derive_key(sm, &data->ssl, label, EAP_TLS_KEY_LEN); if (data->key_data) { - wpa_hexdump_key(MSG_DEBUG, + wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Derived key", data->key_data, EAP_TLS_KEY_LEN); @@ -1163,6 +1164,10 @@ static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv) static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv) { struct eap_peap_data *data = priv; + + if (data->phase2_priv && data->phase2_method && + data->phase2_method->deinit_for_reauth) + data->phase2_method->deinit_for_reauth(sm, data->phase2_priv); wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = NULL; wpabuf_free(data->pending_resp); @@ -1267,12 +1272,11 @@ static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (data->session_id == NULL || !data->phase2_success) return NULL; - id = os_malloc(data->id_len); + id = os_memdup(data->session_id, data->id_len); if (id == NULL) return NULL; *len = data->id_len; - os_memcpy(id, data->session_id, data->id_len); return id; } diff --git a/src/eap_peer/eap_proxy.h b/src/eap_peer/eap_proxy.h index 23cdbe698b3cc..9d8e570267223 100644 --- a/src/eap_peer/eap_proxy.h +++ b/src/eap_peer/eap_proxy.h @@ -20,7 +20,7 @@ enum eap_proxy_status { }; struct eap_proxy_sm * -eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, +eap_proxy_init(void *eapol_ctx, const struct eapol_callbacks *eapol_cb, void *msg_ctx); void eap_proxy_deinit(struct eap_proxy_sm *eap_proxy); @@ -40,10 +40,16 @@ eap_proxy_packet_update(struct eap_proxy_sm *eap_proxy, u8 *eapReqData, int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen, int verbose); -int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf, - size_t *imsi_len); +int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, int sim_num, + char *imsi_buf, size_t *imsi_len); int eap_proxy_notify_config(struct eap_proxy_sm *sm, struct eap_peer_config *config); +u8 * eap_proxy_get_eap_session_id(struct eap_proxy_sm *sm, size_t *len); + +u8 * eap_proxy_get_emsk(struct eap_proxy_sm *sm, size_t *len); + +void eap_proxy_sm_abort(struct eap_proxy_sm *sm); + #endif /* EAP_PROXY_H */ diff --git a/src/eap_peer/eap_proxy_dummy.c b/src/eap_peer/eap_proxy_dummy.c index d84f01234ed5b..2cc05c92cdfc6 100644 --- a/src/eap_peer/eap_proxy_dummy.c +++ b/src/eap_peer/eap_proxy_dummy.c @@ -12,7 +12,7 @@ #include "eap_proxy.h" struct eap_proxy_sm * -eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, +eap_proxy_init(void *eapol_ctx, const struct eapol_callbacks *eapol_cb, void *msg_ctx) { return NULL; @@ -63,8 +63,8 @@ int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen, } -int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf, - size_t *imsi_len) +int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, int sim_num, + char *imsi_buf, size_t *imsi_len) { return -1; } @@ -75,3 +75,20 @@ int eap_proxy_notify_config(struct eap_proxy_sm *sm, { return -1; } + + +u8 * eap_proxy_get_eap_session_id(struct eap_proxy_sm *sm, size_t *len) +{ + return NULL; +} + + +u8 * eap_proxy_get_emsk(struct eap_proxy_sm *sm, size_t *len) +{ + return NULL; +} + + +void eap_proxy_sm_abort(struct eap_proxy_sm *sm) +{ +} diff --git a/src/eap_peer/eap_psk.c b/src/eap_peer/eap_psk.c index ac18c158ad8bb..eea9430d24062 100644 --- a/src/eap_peer/eap_psk.c +++ b/src/eap_peer/eap_psk.c @@ -116,14 +116,13 @@ static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data, os_memcpy(data->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN); os_free(data->id_s); data->id_s_len = len - sizeof(*hdr1); - data->id_s = os_malloc(data->id_s_len); + data->id_s = os_memdup(hdr1 + 1, data->id_s_len); if (data->id_s == NULL) { wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for " "ID_S (len=%lu)", (unsigned long) data->id_s_len); ret->ignore = TRUE; return NULL; } - os_memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len); wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S", data->id_s, data->id_s_len); @@ -273,13 +272,12 @@ static struct wpabuf * eap_psk_process_3(struct eap_psk_data *data, wpabuf_head(reqData), 5); wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left); - decrypted = os_malloc(left); + decrypted = os_memdup(msg, left); if (decrypted == NULL) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; return NULL; } - os_memcpy(decrypted, msg, left); if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), wpabuf_head(reqData), @@ -425,12 +423,11 @@ static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != PSK_DONE) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; *len = EAP_MSK_LEN; - os_memcpy(key, data->msk, EAP_MSK_LEN); return key; } @@ -466,12 +463,11 @@ static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != PSK_DONE) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); return key; } diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c index d2bc981cd06b8..761c16af996a3 100644 --- a/src/eap_peer/eap_pwd.c +++ b/src/eap_peer/eap_pwd.c @@ -9,8 +9,11 @@ #include "includes.h" #include "common.h" +#include "crypto/sha1.h" #include "crypto/sha256.h" +#include "crypto/sha512.h" #include "crypto/ms_funcs.h" +#include "crypto/crypto.h" #include "eap_peer/eap_i.h" #include "eap_common/eap_pwd_common.h" @@ -28,6 +31,8 @@ struct eap_pwd_data { size_t password_len; int password_hash; u16 group_num; + u8 prep; + u8 token[4]; EAP_PWD_group *grp; struct wpabuf *inbuf; @@ -36,18 +41,16 @@ struct eap_pwd_data { size_t out_frag_pos; size_t mtu; - BIGNUM *k; - BIGNUM *private_value; - BIGNUM *server_scalar; - BIGNUM *my_scalar; - EC_POINT *my_element; - EC_POINT *server_element; + struct crypto_bignum *k; + struct crypto_bignum *private_value; + struct crypto_bignum *server_scalar; + struct crypto_bignum *my_scalar; + struct crypto_ec_point *my_element; + struct crypto_ec_point *server_element; u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; u8 session_id[1 + SHA256_MAC_LEN]; - - BN_CTX *bnctx; }; @@ -107,15 +110,8 @@ static void * eap_pwd_init(struct eap_sm *sm) return NULL; } - if ((data->bnctx = BN_CTX_new()) == NULL) { - wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail"); - os_free(data); - return NULL; - } - if ((data->id_peer = os_malloc(identity_len)) == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); - BN_CTX_free(data->bnctx); os_free(data); return NULL; } @@ -125,7 +121,6 @@ static void * eap_pwd_init(struct eap_sm *sm) if ((data->password = os_malloc(password_len)) == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail"); - BN_CTX_free(data->bnctx); bin_clear_free(data->id_peer, data->id_peer_len); os_free(data); return NULL; @@ -152,21 +147,18 @@ static void eap_pwd_deinit(struct eap_sm *sm, void *priv) { struct eap_pwd_data *data = priv; - BN_clear_free(data->private_value); - BN_clear_free(data->server_scalar); - BN_clear_free(data->my_scalar); - BN_clear_free(data->k); - BN_CTX_free(data->bnctx); - EC_POINT_clear_free(data->my_element); - EC_POINT_clear_free(data->server_element); + crypto_bignum_deinit(data->private_value, 1); + crypto_bignum_deinit(data->server_scalar, 1); + crypto_bignum_deinit(data->my_scalar, 1); + crypto_bignum_deinit(data->k, 1); + crypto_ec_point_deinit(data->my_element, 1); + crypto_ec_point_deinit(data->server_element, 1); bin_clear_free(data->id_peer, data->id_peer_len); bin_clear_free(data->id_server, data->id_server_len); bin_clear_free(data->password, data->password_len); if (data->grp) { - EC_GROUP_free(data->grp->group); - EC_POINT_clear_free(data->grp->pwe); - BN_clear_free(data->grp->order); - BN_clear_free(data->grp->prime); + crypto_ec_deinit(data->grp->group); + crypto_ec_point_deinit(data->grp->pwe, 1); os_free(data->grp); } wpabuf_free(data->inbuf); @@ -183,11 +175,10 @@ static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -202,11 +193,10 @@ static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - id = os_malloc(1 + SHA256_MAC_LEN); + id = os_memdup(data->session_id, 1 + SHA256_MAC_LEN); if (id == NULL) return NULL; - os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN); *len = 1 + SHA256_MAC_LEN; return id; @@ -220,10 +210,6 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, const u8 *payload, size_t payload_len) { struct eap_pwd_id *id; - const u8 *password; - size_t password_len; - u8 pwhashhash[16]; - int res; if (data->state != PWD_ID_Req) { ret->ignore = TRUE; @@ -250,7 +236,10 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, } if (id->prep != EAP_PWD_PREP_NONE && - id->prep != EAP_PWD_PREP_MS) { + id->prep != EAP_PWD_PREP_MS && + id->prep != EAP_PWD_PREP_SSHA1 && + id->prep != EAP_PWD_PREP_SSHA256 && + id->prep != EAP_PWD_PREP_SSHA512) { wpa_printf(MSG_DEBUG, "EAP-PWD: Unsupported password pre-processing technique (Prep=%u)", id->prep); @@ -268,6 +257,15 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, wpa_printf(MSG_DEBUG, "EAP-PWD (peer): using group %d", data->group_num); + data->prep = id->prep; + os_memcpy(data->token, id->token, sizeof(id->token)); + + if (data->id_server || data->grp) { + wpa_printf(MSG_INFO, "EAP-pwd: data was already allocated"); + eap_pwd_state(data, FAILURE); + return; + } + data->id_server = os_malloc(payload_len - sizeof(struct eap_pwd_id)); if (data->id_server == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); @@ -279,7 +277,7 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of", data->id_server, data->id_server_len); - data->grp = os_zalloc(sizeof(EAP_PWD_group)); + data->grp = get_eap_pwd_group(data->group_num); if (data->grp == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for " "group"); @@ -287,13 +285,74 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, return; } - if (id->prep == EAP_PWD_PREP_MS) { + data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) + + data->id_peer_len); + if (data->outbuf == NULL) { + eap_pwd_state(data, FAILURE); + return; + } + wpabuf_put_be16(data->outbuf, data->group_num); + wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); + wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); + wpabuf_put_data(data->outbuf, id->token, sizeof(id->token)); + wpabuf_put_u8(data->outbuf, id->prep); + wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len); + + eap_pwd_state(data, PWD_Commit_Req); +} + + +static void +eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, + struct eap_method_ret *ret, + const struct wpabuf *reqData, + const u8 *payload, size_t payload_len) +{ + struct crypto_ec_point *K = NULL, *point = NULL; + struct crypto_bignum *mask = NULL, *cofactor = NULL; + const u8 *ptr = payload; + u8 *scalar = NULL, *element = NULL; + size_t prime_len, order_len; + const u8 *password; + size_t password_len; + u8 pwhashhash[16]; + const u8 *salt_pwd[2]; + size_t salt_pwd_len[2], exp_len; + u8 salt_len, salthashpwd[64]; /* 64 = SHA512_DIGEST_LENGTH */ + int res; + + if (data->state != PWD_Commit_Req) { + ret->ignore = TRUE; + goto fin; + } + + if (!data->grp) { + wpa_printf(MSG_DEBUG, + "EAP-PWD (client): uninitialized EAP-pwd group"); + ret->ignore = TRUE; + goto fin; + } + + prime_len = crypto_ec_prime_len(data->grp->group); + order_len = crypto_ec_order_len(data->grp->group); + + switch (data->prep) { + case EAP_PWD_PREP_MS: + wpa_printf(MSG_DEBUG, + "EAP-pwd commit request, password prep is MS"); #ifdef CONFIG_FIPS wpa_printf(MSG_ERROR, "EAP-PWD (peer): MS password hash not supported in FIPS mode"); eap_pwd_state(data, FAILURE); return; #else /* CONFIG_FIPS */ + if (payload_len != 2 * prime_len + order_len) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Commit payload length %u (expected %u)", + (unsigned int) payload_len, + (unsigned int) (2 * prime_len + order_len)); + goto fin; + } if (data->password_hash) { res = hash_nt_password_hash(data->password, pwhashhash); } else { @@ -314,9 +373,139 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, password = pwhashhash; password_len = sizeof(pwhashhash); #endif /* CONFIG_FIPS */ - } else { + break; + case EAP_PWD_PREP_SSHA1: + wpa_printf(MSG_DEBUG, + "EAP-pwd commit request, password prep is salted sha1"); + if (payload_len < 1 || *ptr == 0) { + wpa_printf(MSG_DEBUG, "EAP-pwd: Invalid Salt-len"); + goto fin; + } + salt_len = *ptr++; + exp_len = 1 + salt_len + 2 * prime_len + order_len; + if (payload_len != exp_len) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Commit payload length %u (expected %u)", + (unsigned int) payload_len, + (unsigned int) exp_len); + goto fin; + } + + /* salted-password = Hash(password | salt) */ + wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Unsalted password", + data->password, data->password_len); + wpa_hexdump(MSG_DEBUG, "EAP-pwd: Salt", ptr, salt_len); + salt_pwd[0] = data->password; + salt_pwd[1] = ptr; + salt_pwd_len[0] = data->password_len; + salt_pwd_len[1] = salt_len; + if (sha1_vector(2, salt_pwd, salt_pwd_len, salthashpwd) < 0) + goto fin; + + wpa_printf(MSG_DEBUG, + "EAP-pwd: sha1 hashed %d byte salt with password", + (int) salt_len); + ptr += salt_len; + password = salthashpwd; + password_len = SHA1_MAC_LEN; + wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Salted password", + password, password_len); + break; + case EAP_PWD_PREP_SSHA256: + wpa_printf(MSG_DEBUG, + "EAP-pwd commit request, password prep is salted sha256"); + if (payload_len < 1 || *ptr == 0) { + wpa_printf(MSG_DEBUG, "EAP-pwd: Invalid Salt-len"); + goto fin; + } + salt_len = *ptr++; + exp_len = 1 + salt_len + 2 * prime_len + order_len; + if (payload_len != exp_len) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Commit payload length %u (expected %u)", + (unsigned int) payload_len, + (unsigned int) exp_len); + goto fin; + } + + /* salted-password = Hash(password | salt) */ + wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Unsalted password", + data->password, data->password_len); + wpa_hexdump(MSG_DEBUG, "EAP-pwd: Salt", ptr, salt_len); + salt_pwd[0] = data->password; + salt_pwd[1] = ptr; + salt_pwd_len[0] = data->password_len; + salt_pwd_len[1] = salt_len; + if (sha256_vector(2, salt_pwd, salt_pwd_len, salthashpwd) < 0) + goto fin; + + ptr += salt_len; + password = salthashpwd; + password_len = SHA256_MAC_LEN; + wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Salted password", + password, password_len); + break; +#ifdef CONFIG_SHA512 + case EAP_PWD_PREP_SSHA512: + wpa_printf(MSG_DEBUG, + "EAP-pwd commit request, password prep is salted sha512"); + if (payload_len < 1 || *ptr == 0) { + wpa_printf(MSG_DEBUG, "EAP-pwd: Invalid Salt-len"); + goto fin; + } + salt_len = *ptr++; + exp_len = 1 + salt_len + 2 * prime_len + order_len; + if (payload_len != exp_len) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Commit payload length %u (expected %u)", + (unsigned int) payload_len, + (unsigned int) exp_len); + goto fin; + } + + /* salted-password = Hash(password | salt) */ + wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Unsalted password", + data->password, data->password_len); + wpa_hexdump(MSG_DEBUG, "EAP-pwd: Salt", ptr, salt_len); + salt_pwd[0] = data->password; + salt_pwd[1] = ptr; + salt_pwd_len[0] = data->password_len; + salt_pwd_len[1] = salt_len; + if (sha512_vector(2, salt_pwd, salt_pwd_len, salthashpwd) < 0) + goto fin; + + ptr += salt_len; + password = salthashpwd; + password_len = SHA512_MAC_LEN; + wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: Salted password", + password, password_len); + break; +#endif /* CONFIG_SHA512 */ + case EAP_PWD_PREP_NONE: + wpa_printf(MSG_DEBUG, + "EAP-pwd commit request, password prep is NONE"); + if (data->password_hash) { + wpa_printf(MSG_DEBUG, + "EAP-PWD: Unhashed password not available"); + eap_pwd_state(data, FAILURE); + return; + } + if (payload_len != 2 * prime_len + order_len) { + wpa_printf(MSG_INFO, + "EAP-pwd: Unexpected Commit payload length %u (expected %u)", + (unsigned int) payload_len, + (unsigned int) (2 * prime_len + order_len)); + goto fin; + } password = data->password; password_len = data->password_len; + break; + default: + wpa_printf(MSG_DEBUG, + "EAP-pwd: Unsupported password pre-processing technique (Prep=%u)", + data->prep); + eap_pwd_state(data, FAILURE); + return; } /* compute PWE */ @@ -324,8 +513,9 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, password, password_len, data->id_server, data->id_server_len, data->id_peer, data->id_peer_len, - id->token); + data->token); os_memset(pwhashhash, 0, sizeof(pwhashhash)); + os_memset(salthashpwd, 0, sizeof(salthashpwd)); if (res) { wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute PWE"); eap_pwd_state(data, FAILURE); @@ -333,134 +523,86 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data, } wpa_printf(MSG_DEBUG, "EAP-PWD (peer): computed %d bit PWE...", - BN_num_bits(data->grp->prime)); - - data->outbuf = wpabuf_alloc(sizeof(struct eap_pwd_id) + - data->id_peer_len); - if (data->outbuf == NULL) { - eap_pwd_state(data, FAILURE); - return; - } - wpabuf_put_be16(data->outbuf, data->group_num); - wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); - wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); - wpabuf_put_data(data->outbuf, id->token, sizeof(id->token)); - wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE); - wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len); - - eap_pwd_state(data, PWD_Commit_Req); -} - - -static void -eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, - struct eap_method_ret *ret, - const struct wpabuf *reqData, - const u8 *payload, size_t payload_len) -{ - EC_POINT *K = NULL, *point = NULL; - BIGNUM *mask = NULL, *x = NULL, *y = NULL, *cofactor = NULL; - u16 offset; - u8 *ptr, *scalar = NULL, *element = NULL; - size_t prime_len, order_len; - - if (data->state != PWD_Commit_Req) { - ret->ignore = TRUE; - goto fin; - } - - prime_len = BN_num_bytes(data->grp->prime); - order_len = BN_num_bytes(data->grp->order); - - if (payload_len != 2 * prime_len + order_len) { - wpa_printf(MSG_INFO, - "EAP-pwd: Unexpected Commit payload length %u (expected %u)", - (unsigned int) payload_len, - (unsigned int) (2 * prime_len + order_len)); - goto fin; - } - - if (((data->private_value = BN_new()) == NULL) || - ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) || - ((cofactor = BN_new()) == NULL) || - ((data->my_scalar = BN_new()) == NULL) || - ((mask = BN_new()) == NULL)) { + (int) crypto_ec_prime_len_bits(data->grp->group)); + + data->private_value = crypto_bignum_init(); + data->my_element = crypto_ec_point_init(data->grp->group); + cofactor = crypto_bignum_init(); + data->my_scalar = crypto_bignum_init(); + mask = crypto_bignum_init(); + if (!data->private_value || !data->my_element || !cofactor || + !data->my_scalar || !mask) { wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail"); goto fin; } - if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) { + if (crypto_ec_cofactor(data->grp->group, cofactor) < 0) { wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor " "for curve"); goto fin; } - if (BN_rand_range(data->private_value, data->grp->order) != 1 || - BN_rand_range(mask, data->grp->order) != 1 || - BN_add(data->my_scalar, data->private_value, mask) != 1 || - BN_mod(data->my_scalar, data->my_scalar, data->grp->order, - data->bnctx) != 1) { + if (crypto_bignum_rand(data->private_value, + crypto_ec_get_order(data->grp->group)) < 0 || + crypto_bignum_rand(mask, + crypto_ec_get_order(data->grp->group)) < 0 || + crypto_bignum_add(data->private_value, mask, + data->my_scalar) < 0 || + crypto_bignum_mod(data->my_scalar, + crypto_ec_get_order(data->grp->group), + data->my_scalar) < 0) { wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get randomness"); goto fin; } - if (!EC_POINT_mul(data->grp->group, data->my_element, NULL, - data->grp->pwe, mask, data->bnctx)) { + if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask, + data->my_element) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): element allocation " "fail"); eap_pwd_state(data, FAILURE); goto fin; } - if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx)) - { + if (crypto_ec_point_invert(data->grp->group, data->my_element) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail"); goto fin; } - if (((x = BN_new()) == NULL) || - ((y = BN_new()) == NULL)) { - wpa_printf(MSG_INFO, "EAP-PWD (peer): point allocation fail"); - goto fin; - } - /* process the request */ - if (((data->server_scalar = BN_new()) == NULL) || - ((data->k = BN_new()) == NULL) || - ((K = EC_POINT_new(data->grp->group)) == NULL) || - ((point = EC_POINT_new(data->grp->group)) == NULL) || - ((data->server_element = EC_POINT_new(data->grp->group)) == NULL)) - { + data->k = crypto_bignum_init(); + K = crypto_ec_point_init(data->grp->group); + point = crypto_ec_point_init(data->grp->group); + if (!data->k || !K || !point) { wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation " "fail"); goto fin; } /* element, x then y, followed by scalar */ - ptr = (u8 *) payload; - BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x); - ptr += BN_num_bytes(data->grp->prime); - BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y); - ptr += BN_num_bytes(data->grp->prime); - BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->server_scalar); - if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group, - data->server_element, x, y, - data->bnctx)) { + data->server_element = crypto_ec_point_from_bin(data->grp->group, ptr); + if (!data->server_element) { wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element " "fail"); goto fin; } + ptr += prime_len * 2; + data->server_scalar = crypto_bignum_init_set(ptr, order_len); + if (!data->server_scalar) { + wpa_printf(MSG_INFO, + "EAP-PWD (peer): setting peer scalar fail"); + goto fin; + } /* check to ensure server's element is not in a small sub-group */ - if (BN_cmp(cofactor, BN_value_one())) { - if (!EC_POINT_mul(data->grp->group, point, NULL, - data->server_element, cofactor, NULL)) { + if (!crypto_bignum_is_one(cofactor)) { + if (crypto_ec_point_mul(data->grp->group, data->server_element, + cofactor, point) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " "server element by order!\n"); goto fin; } - if (EC_POINT_is_at_infinity(data->grp->group, point)) { + if (crypto_ec_point_is_at_infinity(data->grp->group, point)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): server element " "is at infinity!\n"); goto fin; @@ -468,21 +610,20 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, } /* compute the shared key, k */ - if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe, - data->server_scalar, data->bnctx)) || - (!EC_POINT_add(data->grp->group, K, K, data->server_element, - data->bnctx)) || - (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value, - data->bnctx))) { + if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, + data->server_scalar, K) < 0 || + crypto_ec_point_add(data->grp->group, K, data->server_element, + K) < 0 || + crypto_ec_point_mul(data->grp->group, K, data->private_value, + K) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): computing shared key " "fail"); goto fin; } /* ensure that the shared key isn't in a small sub-group */ - if (BN_cmp(cofactor, BN_value_one())) { - if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor, - NULL)) { + if (!crypto_bignum_is_one(cofactor)) { + if (crypto_ec_point_mul(data->grp->group, K, cofactor, K) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply " "shared key point by order"); goto fin; @@ -495,30 +636,22 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, * never going to happen it is a simple and safe check "just to be * sure" so let's be safe. */ - if (EC_POINT_is_at_infinity(data->grp->group, K)) { + if (crypto_ec_point_is_at_infinity(data->grp->group, K)) { wpa_printf(MSG_INFO, "EAP-PWD (peer): shared key point is at " "infinity!\n"); goto fin; } - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k, - NULL, data->bnctx)) { + if (crypto_ec_point_x(data->grp->group, K, data->k) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to extract " "shared secret from point"); goto fin; } /* now do the response */ - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->my_element, x, y, - data->bnctx)) { - wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail"); - goto fin; - } - - if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) || - ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) == - NULL)) { + scalar = os_zalloc(order_len); + element = os_zalloc(prime_len * 2); + if (!scalar || !element) { wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail"); goto fin; } @@ -528,36 +661,28 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data, * sufficiently smaller than the prime or order might need pre-pending * with zeros. */ - os_memset(scalar, 0, BN_num_bytes(data->grp->order)); - os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->my_scalar); - BN_bn2bin(data->my_scalar, scalar + offset); - - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, element + offset); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset); - - data->outbuf = wpabuf_alloc(BN_num_bytes(data->grp->order) + - 2 * BN_num_bytes(data->grp->prime)); + crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len); + if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element, + element + prime_len) != 0) { + wpa_printf(MSG_INFO, "EAP-PWD (peer): point assignment fail"); + goto fin; + } + + data->outbuf = wpabuf_alloc(order_len + 2 * prime_len); if (data->outbuf == NULL) goto fin; /* we send the element as (x,y) follwed by the scalar */ - wpabuf_put_data(data->outbuf, element, - 2 * BN_num_bytes(data->grp->prime)); - wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order)); + wpabuf_put_data(data->outbuf, element, 2 * prime_len); + wpabuf_put_data(data->outbuf, scalar, order_len); fin: os_free(scalar); os_free(element); - BN_clear_free(x); - BN_clear_free(y); - BN_clear_free(mask); - BN_clear_free(cofactor); - EC_POINT_clear_free(K); - EC_POINT_clear_free(point); + crypto_bignum_deinit(mask, 1); + crypto_bignum_deinit(cofactor, 1); + crypto_ec_point_deinit(K, 1); + crypto_ec_point_deinit(point, 1); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); else @@ -571,12 +696,11 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, const struct wpabuf *reqData, const u8 *payload, size_t payload_len) { - BIGNUM *x = NULL, *y = NULL; - struct crypto_hash *hash; + struct crypto_hash *hash = NULL; u32 cs; u16 grp; u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; - int offset; + size_t prime_len = 0, order_len = 0; if (data->state != PWD_Confirm_Req) { ret->ignore = TRUE; @@ -590,6 +714,9 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, goto fin; } + prime_len = crypto_ec_prime_len(data->grp->group); + order_len = crypto_ec_order_len(data->grp->group); + /* * first build up the ciphersuite which is group | random_function | * prf @@ -602,9 +729,9 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, ptr += sizeof(u8); *ptr = EAP_PWD_DEFAULT_PRF; - /* each component of the cruft will be at most as big as the prime */ - if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || - ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { + /* each component of the point will be at most as big as the prime */ + cruft = os_malloc(prime_len * 2); + if (!cruft) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm allocation " "fail"); goto fin; @@ -622,65 +749,41 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, * zero the memory each time because this is mod prime math and some * value may start with a few zeros and the previous one did not. */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); - BN_bn2bin(data->k, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len); + eap_pwd_h_update(hash, cruft, prime_len); /* server element: x, y */ - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->server_element, x, y, - data->bnctx)) { + if (crypto_ec_point_to_bin(data->grp->group, data->server_element, + cruft, cruft + prime_len) != 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " "assignment fail"); goto fin; } - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, prime_len * 2); /* server scalar */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->server_scalar); - BN_bn2bin(data->server_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + crypto_bignum_to_bin(data->server_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); /* my element: x, y */ - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->my_element, x, y, - data->bnctx)) { + if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft, + cruft + prime_len) != 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " "assignment fail"); goto fin; } - - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, prime_len * 2); /* my scalar */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->my_scalar); - BN_bn2bin(data->my_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); /* the ciphersuite */ eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); /* random function fin */ eap_pwd_h_final(hash, conf); + hash = NULL; ptr = (u8 *) payload; if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) { @@ -700,66 +803,43 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, goto fin; /* k */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); - BN_bn2bin(data->k, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len); + eap_pwd_h_update(hash, cruft, prime_len); /* my element */ - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->my_element, x, y, - data->bnctx)) { + if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft, + cruft + prime_len) != 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point " "assignment fail"); goto fin; } - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, prime_len * 2); /* my scalar */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->my_scalar); - BN_bn2bin(data->my_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); /* server element: x, y */ - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->server_element, x, y, - data->bnctx)) { + if (crypto_ec_point_to_bin(data->grp->group, data->server_element, + cruft, cruft + prime_len) != 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm point " "assignment fail"); goto fin; } - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, prime_len * 2); /* server scalar */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->server_scalar); - BN_bn2bin(data->server_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + crypto_bignum_to_bin(data->server_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); /* the ciphersuite */ eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); /* all done */ eap_pwd_h_final(hash, conf); + hash = NULL; - if (compute_keys(data->grp, data->bnctx, data->k, + if (compute_keys(data->grp, data->k, data->my_scalar, data->server_scalar, conf, ptr, &cs, data->msk, data->emsk, data->session_id) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | " @@ -774,10 +854,7 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data, wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); fin: - if (data->grp) - bin_clear_free(cruft, BN_num_bytes(data->grp->prime)); - BN_clear_free(x); - BN_clear_free(y); + bin_clear_free(cruft, prime_len * 2); if (data->outbuf == NULL) { ret->methodState = METHOD_DONE; ret->decision = DECISION_FAIL; @@ -785,6 +862,10 @@ fin: } else { eap_pwd_state(data, SUCCESS_ON_FRAG_COMPLETION); } + + /* clean allocated memory */ + if (hash) + eap_pwd_h_final(hash, conf); } diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c index 330febbefd78c..0a6ce255af4de 100644 --- a/src/eap_peer/eap_sake.c +++ b/src/eap_peer/eap_sake.c @@ -85,12 +85,11 @@ static void * eap_sake_init(struct eap_sm *sm) identity = eap_get_config_identity(sm, &identity_len); if (identity) { - data->peerid = os_malloc(identity_len); + data->peerid = os_memdup(identity, identity_len); if (data->peerid == NULL) { eap_sake_deinit(sm, data); return NULL; } - os_memcpy(data->peerid, identity, identity_len); data->peerid_len = identity_len; } @@ -230,10 +229,9 @@ static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm, if (attr.serverid) { wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-SAKE: SERVERID", attr.serverid, attr.serverid_len); - data->serverid = os_malloc(attr.serverid_len); + data->serverid = os_memdup(attr.serverid, attr.serverid_len); if (data->serverid == NULL) return NULL; - os_memcpy(data->serverid, attr.serverid, attr.serverid_len); data->serverid_len = attr.serverid_len; } @@ -450,10 +448,9 @@ static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -490,10 +487,9 @@ static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c index b97c95db196f0..ba5eea9ddfa2a 100644 --- a/src/eap_peer/eap_sim.c +++ b/src/eap_peer/eap_sim.c @@ -46,6 +46,8 @@ struct eap_sim_data { CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE } state; int result_ind, use_result_ind; + int use_pseudonym; + int error_code; }; @@ -93,6 +95,9 @@ static void * eap_sim_init(struct eap_sm *sm) return NULL; } + /* Zero is a valid error code, so we need to initialize */ + data->error_code = NO_EAP_METHOD_ERROR; + data->min_num_chal = 2; if (config && config->phase1) { char *pos = os_strstr(config->phase1, "sim_min_num_chal="); @@ -115,7 +120,8 @@ static void * eap_sim_init(struct eap_sm *sm) NULL; } - if (config && config->anonymous_identity) { + data->use_pseudonym = !sm->init_phase2; + if (config && config->anonymous_identity && data->use_pseudonym) { data->pseudonym = os_malloc(config->anonymous_identity_len); if (data->pseudonym) { os_memcpy(data->pseudonym, config->anonymous_identity, @@ -372,7 +378,8 @@ static void eap_sim_clear_identities(struct eap_sm *sm, os_free(data->pseudonym); data->pseudonym = NULL; data->pseudonym_len = 0; - eap_set_anon_id(sm, NULL, 0); + if (data->use_pseudonym) + eap_set_anon_id(sm, NULL, 0); } if ((id & CLEAR_REAUTH_ID) && data->reauth_id) { wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old reauth_id"); @@ -427,20 +434,21 @@ static int eap_sim_learn_ids(struct eap_sm *sm, struct eap_sim_data *data, realm, realm_len); } data->pseudonym_len = attr->next_pseudonym_len + realm_len; - eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len); + if (data->use_pseudonym) + eap_set_anon_id(sm, data->pseudonym, + data->pseudonym_len); } if (attr->next_reauth_id) { os_free(data->reauth_id); - data->reauth_id = os_malloc(attr->next_reauth_id_len); + data->reauth_id = os_memdup(attr->next_reauth_id, + attr->next_reauth_id_len); if (data->reauth_id == NULL) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for " "next reauth_id"); data->reauth_id_len = 0; return -1; } - os_memcpy(data->reauth_id, attr->next_reauth_id, - attr->next_reauth_id_len); data->reauth_id_len = attr->next_reauth_id_len; wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: (encr) AT_NEXT_REAUTH_ID", @@ -635,14 +643,13 @@ static struct wpabuf * eap_sim_process_start(struct eap_sm *sm, } os_free(data->ver_list); - data->ver_list = os_malloc(attr->version_list_len); + data->ver_list = os_memdup(attr->version_list, attr->version_list_len); if (data->ver_list == NULL) { wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to allocate " "memory for version list"); return eap_sim_client_error(data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET); } - os_memcpy(data->ver_list, attr->version_list, attr->version_list_len); data->ver_list_len = attr->version_list_len; pos = data->ver_list; for (i = 0; i < data->ver_list_len / 2; i++) { @@ -764,8 +771,17 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm, } else if (data->pseudonym) { identity = data->pseudonym; identity_len = data->pseudonym_len; - } else - identity = eap_get_config_identity(sm, &identity_len); + } else { + struct eap_peer_config *config; + + config = eap_get_config(sm); + if (config && config->imsi_identity) { + identity = config->imsi_identity; + identity_len = config->imsi_identity_len; + } else { + identity = eap_get_config_identity(sm, &identity_len); + } + } wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Selected identity for MK " "derivation", identity, identity_len); eap_sim_derive_mk(identity, identity_len, data->nonce_mt, @@ -908,6 +924,7 @@ static struct wpabuf * eap_sim_process_notification( eap_sim_report_notification(sm->msg_ctx, attr->notification, 0); if (attr->notification >= 0 && attr->notification < 32768) { + data->error_code = attr->notification; eap_sim_state(data, FAILURE); } else if (attr->notification == EAP_SIM_SUCCESS && data->state == RESULT_SUCCESS) @@ -1181,12 +1198,11 @@ static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_SIM_KEYING_DATA_LEN); + key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN); if (key == NULL) return NULL; *len = EAP_SIM_KEYING_DATA_LEN; - os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); return key; } @@ -1223,17 +1239,33 @@ static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); return key; } +static int eap_sim_get_error_code(void *priv) +{ + struct eap_sim_data *data = priv; + int current_data_error; + + if (!data) + return NO_EAP_METHOD_ERROR; + + current_data_error = data->error_code; + + /* Now reset for next transaction */ + data->error_code = NO_EAP_METHOD_ERROR; + + return current_data_error; +} + + int eap_peer_sim_register(void) { struct eap_method *eap; @@ -1254,6 +1286,7 @@ int eap_peer_sim_register(void) eap->init_for_reauth = eap_sim_init_for_reauth; eap->get_identity = eap_sim_get_identity; eap->get_emsk = eap_sim_get_emsk; + eap->get_error_code = eap_sim_get_error_code; return eap_peer_method_register(eap); } diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c index ca2354f8a7850..cb747026cb8a5 100644 --- a/src/eap_peer/eap_tls.c +++ b/src/eap_peer/eap_tls.c @@ -173,14 +173,31 @@ static struct wpabuf * eap_tls_failure(struct eap_sm *sm, static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data, struct eap_method_ret *ret) { + const char *label; + wpa_printf(MSG_DEBUG, "EAP-TLS: Done"); - ret->methodState = METHOD_DONE; - ret->decision = DECISION_UNCOND_SUCC; + if (data->ssl.tls_out) { + wpa_printf(MSG_DEBUG, "EAP-TLS: Fragment(s) remaining"); + return; + } + + if (data->ssl.tls_v13) { + label = "EXPORTER_EAP_TLS_Key_Material"; + + /* A possible NewSessionTicket may be received before + * EAP-Success, so need to allow it to be received. */ + ret->methodState = METHOD_MAY_CONT; + ret->decision = DECISION_COND_SUCC; + } else { + label = "client EAP encryption"; + + ret->methodState = METHOD_DONE; + ret->decision = DECISION_UNCOND_SUCC; + } eap_tls_free_key(data); - data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, - "client EAP encryption", + data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, label, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (data->key_data) { @@ -338,12 +355,11 @@ static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->key_data == NULL) return NULL; - key = os_malloc(EAP_TLS_KEY_LEN); + key = os_memdup(data->key_data, EAP_TLS_KEY_LEN); if (key == NULL) return NULL; *len = EAP_TLS_KEY_LEN; - os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); return key; } @@ -357,12 +373,11 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->key_data == NULL) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; - os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); return key; } @@ -376,12 +391,11 @@ static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (data->session_id == NULL) return NULL; - id = os_malloc(data->id_len); + id = os_memdup(data->session_id, data->id_len); if (id == NULL) return NULL; *len = data->id_len; - os_memcpy(id, data->session_id, data->id_len); return id; } diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c index 0dcb9c138f81d..0de131526a519 100644 --- a/src/eap_peer/eap_tls_common.c +++ b/src/eap_peer/eap_tls_common.c @@ -80,10 +80,22 @@ static void eap_tls_params_flags(struct tls_connection_params *params, params->flags |= TLS_CONN_DISABLE_TLSv1_2; if (os_strstr(txt, "tls_disable_tlsv1_2=0")) params->flags &= ~TLS_CONN_DISABLE_TLSv1_2; + if (os_strstr(txt, "tls_disable_tlsv1_3=1")) + params->flags |= TLS_CONN_DISABLE_TLSv1_3; + if (os_strstr(txt, "tls_disable_tlsv1_3=0")) + params->flags &= ~TLS_CONN_DISABLE_TLSv1_3; if (os_strstr(txt, "tls_ext_cert_check=1")) params->flags |= TLS_CONN_EXT_CERT_CHECK; if (os_strstr(txt, "tls_ext_cert_check=0")) params->flags &= ~TLS_CONN_EXT_CERT_CHECK; + if (os_strstr(txt, "tls_suiteb=1")) + params->flags |= TLS_CONN_SUITEB; + if (os_strstr(txt, "tls_suiteb=0")) + params->flags &= ~TLS_CONN_SUITEB; + if (os_strstr(txt, "tls_suiteb_no_ecdh=1")) + params->flags |= TLS_CONN_SUITEB_NO_ECDH; + if (os_strstr(txt, "tls_suiteb_no_ecdh=0")) + params->flags &= ~TLS_CONN_SUITEB_NO_ECDH; } @@ -151,6 +163,23 @@ static int eap_tls_params_from_conf(struct eap_sm *sm, */ params->flags |= TLS_CONN_DISABLE_SESSION_TICKET; } + if (data->eap_type == EAP_TYPE_FAST || + data->eap_type == EAP_TYPE_TTLS || + data->eap_type == EAP_TYPE_PEAP) { + /* The current EAP peer implementation is not yet ready for the + * TLS v1.3 changes, so disable this by default for now. */ + params->flags |= TLS_CONN_DISABLE_TLSv1_3; + } + if (data->eap_type == EAP_TYPE_TLS) { + /* While the current EAP-TLS implementation is more or less + * complete for TLS v1.3, there has been no interoperability + * testing with other implementations, so disable for by default + * for now until there has been chance to confirm that no + * significant interoperability issues show up with TLS version + * update. + */ + params->flags |= TLS_CONN_DISABLE_TLSv1_3; + } if (phase2) { wpa_printf(MSG_DEBUG, "TLS: using phase2 config options"); eap_tls_params_from_conf2(params, config); @@ -358,6 +387,13 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm, struct tls_random keys; u8 *out; + if (eap_type == EAP_TYPE_TLS && data->tls_v13) { + *len = 64; + return eap_peer_tls_derive_key(sm, data, + "EXPORTER_EAP_TLS_Session-Id", + 64); + } + if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys) || keys.client_random == NULL || keys.server_random == NULL) return NULL; @@ -661,6 +697,8 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, * the AS. */ int res = eap_tls_process_input(sm, data, in_data, out_data); + char buf[20]; + if (res) { /* * Input processing failed (res = -1) or more data is @@ -673,6 +711,12 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, * The incoming message has been reassembled and processed. The * response was allocated into data->tls_out buffer. */ + + if (tls_get_version(data->ssl_ctx, data->conn, + buf, sizeof(buf)) == 0) { + wpa_printf(MSG_DEBUG, "SSL: Using TLS version %s", buf); + data->tls_v13 = os_strcmp(buf, "TLSv1.3") == 0; + } } if (data->tls_out == NULL) { diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h index acd2b783617fe..306e6a98bc3f7 100644 --- a/src/eap_peer/eap_tls_common.h +++ b/src/eap_peer/eap_tls_common.h @@ -73,6 +73,11 @@ struct eap_ssl_data { * eap_type - EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST) */ u8 eap_type; + + /** + * tls_v13 - Whether TLS v1.3 or newer is used + */ + int tls_v13; }; diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c index 92f94dcd60190..f18788ce8cb5c 100644 --- a/src/eap_peer/eap_ttls.c +++ b/src/eap_peer/eap_ttls.c @@ -458,7 +458,7 @@ static int eap_ttls_phase2_request_eap(struct eap_sm *sm, if (*resp == NULL && (config->pending_req_identity || config->pending_req_password || - config->pending_req_otp)) { + config->pending_req_otp || config->pending_req_sim)) { return 0; } @@ -624,12 +624,28 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm, os_memset(pos, 0, 24); /* LM-Response */ pos += 24; if (pwhash) { - challenge_response(challenge, password, pos); /* NT-Response */ + /* NT-Response */ + if (challenge_response(challenge, password, pos)) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/MSCHAP: Failed derive password hash"); + wpabuf_free(msg); + os_free(challenge); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash", password, 16); } else { - nt_challenge_response(challenge, password, password_len, - pos); /* NT-Response */ + /* NT-Response */ + if (nt_challenge_response(challenge, password, password_len, + pos)) { + wpa_printf(MSG_ERROR, + "EAP-TTLS/MSCHAP: Failed derive password"); + wpabuf_free(msg); + os_free(challenge); + return -1; + } + wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password", password, password_len); } @@ -870,13 +886,12 @@ static int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen, { wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message"); if (parse->eapdata == NULL) { - parse->eapdata = os_malloc(dlen); + parse->eapdata = os_memdup(dpos, dlen); if (parse->eapdata == NULL) { wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate " "memory for Phase 2 EAP data"); return -1; } - os_memcpy(parse->eapdata, dpos, dlen); parse->eap_len = dlen; } else { u8 *neweap = os_realloc(parse->eapdata, parse->eap_len + dlen); @@ -1280,7 +1295,8 @@ static int eap_ttls_process_decrypted(struct eap_sm *sm, } else if (config->pending_req_identity || config->pending_req_password || config->pending_req_otp || - config->pending_req_new_password) { + config->pending_req_new_password || + config->pending_req_sim) { wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = wpabuf_dup(in_decrypted); } @@ -1317,7 +1333,8 @@ static int eap_ttls_implicit_identity_request(struct eap_sm *sm, (config->pending_req_identity || config->pending_req_password || config->pending_req_otp || - config->pending_req_new_password)) { + config->pending_req_new_password || + config->pending_req_sim)) { /* * Use empty buffer to force implicit request * processing when EAP request is re-processed after @@ -1537,7 +1554,7 @@ static int eap_ttls_process_handshake(struct eap_sm *sm, } -static void eap_ttls_check_auth_status(struct eap_sm *sm, +static void eap_ttls_check_auth_status(struct eap_sm *sm, struct eap_ttls_data *data, struct eap_method_ret *ret) { @@ -1648,6 +1665,10 @@ static Boolean eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv) static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv) { struct eap_ttls_data *data = priv; + + if (data->phase2_priv && data->phase2_method && + data->phase2_method->deinit_for_reauth) + data->phase2_method->deinit_for_reauth(sm, data->phase2_priv); wpabuf_free(data->pending_phase2_req); data->pending_phase2_req = NULL; wpabuf_free(data->pending_resp); @@ -1739,12 +1760,11 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->key_data == NULL || !data->phase2_success) return NULL; - key = os_malloc(EAP_TLS_KEY_LEN); + key = os_memdup(data->key_data, EAP_TLS_KEY_LEN); if (key == NULL) return NULL; *len = EAP_TLS_KEY_LEN; - os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN); return key; } @@ -1758,12 +1778,11 @@ static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (data->session_id == NULL || !data->phase2_success) return NULL; - id = os_malloc(data->id_len); + id = os_memdup(data->session_id, data->id_len); if (id == NULL) return NULL; *len = data->id_len; - os_memcpy(id, data->session_id, data->id_len); return id; } @@ -1777,12 +1796,11 @@ static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->key_data == NULL) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); if (key == NULL) return NULL; *len = EAP_EMSK_LEN; - os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN); return key; } diff --git a/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c index ca6502ea02e73..7bd97b1b997ef 100644 --- a/src/eap_peer/ikev2.c +++ b/src/eap_peer/ikev2.c @@ -476,10 +476,9 @@ static int ikev2_process_idi(struct ikev2_responder_data *data, wpa_printf(MSG_DEBUG, "IKEV2: IDi ID Type %d", id_type); wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDi", idi, idi_len); os_free(data->IDi); - data->IDi = os_malloc(idi_len); + data->IDi = os_memdup(idi, idi_len); if (data->IDi == NULL) return -1; - os_memcpy(data->IDi, idi, idi_len); data->IDi_len = idi_len; data->IDi_type = id_type; diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c index 0c5caa7dd522e..a9bafe2886c09 100644 --- a/src/eap_peer/tncc.c +++ b/src/eap_peer/tncc.c @@ -126,12 +126,10 @@ static TNC_Result TNC_TNCC_ReportMessageTypes( imc = tnc_imc[imcID]; os_free(imc->supported_types); - imc->supported_types = - os_malloc(typeCount * sizeof(TNC_MessageType)); + imc->supported_types = os_memdup(supportedTypes, + typeCount * sizeof(TNC_MessageType)); if (imc->supported_types == NULL) return TNC_RESULT_FATAL; - os_memcpy(imc->supported_types, supportedTypes, - typeCount * sizeof(TNC_MessageType)); imc->num_supported_types = typeCount; return TNC_RESULT_SUCCESS; diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h index 69eaab8de946b..4fbc661c22fe2 100644 --- a/src/eap_server/eap.h +++ b/src/eap_server/eap.h @@ -31,6 +31,8 @@ struct eap_user { size_t password_len; int password_hash; /* whether password is hashed with * nt_password_hash() */ + u8 *salt; + size_t salt_len; int phase2; int force_version; unsigned int remediation:1; @@ -38,6 +40,7 @@ struct eap_user { int ttls_auth; /* bitfield of * EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */ struct hostapd_radius_attr *accept_attr; + u32 t_c_timestamp; }; struct eap_eapol_interface { @@ -132,6 +135,7 @@ struct eap_config { size_t server_id_len; int erp; unsigned int tls_session_lifetime; + unsigned int tls_flags; #ifdef CONFIG_TESTING_OPTIONS u32 tls_test_flags; @@ -148,10 +152,12 @@ void eap_sm_notify_cached(struct eap_sm *sm); void eap_sm_pending_cb(struct eap_sm *sm); int eap_sm_method_pending(struct eap_sm *sm); const u8 * eap_get_identity(struct eap_sm *sm, size_t *len); +const char * eap_get_serial_num(struct eap_sm *sm); struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm); void eap_server_clear_identity(struct eap_sm *sm); void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source, const u8 *username, size_t username_len, const u8 *challenge, const u8 *response); +void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len); #endif /* EAP_H */ diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h index c90443d19cb93..cf8a9f0d98e1f 100644 --- a/src/eap_server/eap_i.h +++ b/src/eap_server/eap_i.h @@ -159,6 +159,7 @@ struct eap_sm { void *eap_method_priv; u8 *identity; size_t identity_len; + char *serial_num; /* Whether Phase 2 method should validate identity match */ int require_identity_match; int lastId; /* Identifier used in the last EAP-Packet */ @@ -211,6 +212,7 @@ struct eap_sm { Boolean try_initiate_reauth; int erp; unsigned int tls_session_lifetime; + unsigned int tls_flags; #ifdef CONFIG_TESTING_OPTIONS u32 tls_test_flags; diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c index 84ecafc7ca3e5..38a1b5c9ee220 100644 --- a/src/eap_server/eap_server.c +++ b/src/eap_server/eap_server.c @@ -326,6 +326,9 @@ SM_STATE(EAP, RETRANSMIT) if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0) sm->eap_if.eapReq = TRUE; } + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_RETRANSMIT MACSTR, + MAC2STR(sm->peer_addr)); } @@ -415,7 +418,7 @@ static void eap_server_erp_init(struct eap_sm *sm) u8 *emsk = NULL; size_t emsk_len = 0; u8 EMSKname[EAP_EMSK_NAME_LEN]; - u8 len[2]; + u8 len[2], ctx[3]; const char *domain; size_t domain_len, nai_buf_len; struct eap_server_erp_key *erp = NULL; @@ -452,7 +455,7 @@ static void eap_server_erp_init(struct eap_sm *sm) wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len); - WPA_PUT_BE16(len, 8); + WPA_PUT_BE16(len, EAP_EMSK_NAME_LEN); if (hmac_sha256_kdf(sm->eap_if.eapSessionId, sm->eap_if.eapSessionIdLen, "EMSK", len, sizeof(len), EMSKname, EAP_EMSK_NAME_LEN) < 0) { @@ -476,9 +479,11 @@ static void eap_server_erp_init(struct eap_sm *sm) erp->rRK_len = emsk_len; wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len); + ctx[0] = EAP_ERP_CS_HMAC_SHA256_128; + WPA_PUT_BE16(&ctx[1], erp->rRK_len); if (hmac_sha256_kdf(erp->rRK, erp->rRK_len, - "EAP Re-authentication Integrity Key@ietf.org", - len, sizeof(len), erp->rIK, erp->rRK_len) < 0) { + "Re-authentication Integrity Key@ietf.org", + ctx, sizeof(ctx), erp->rIK, erp->rRK_len) < 0) { wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP"); goto fail; } @@ -632,6 +637,9 @@ SM_STATE(EAP, TIMEOUT_FAILURE) SM_ENTRY(EAP, TIMEOUT_FAILURE); sm->eap_if.eapTimeout = TRUE; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TIMEOUT_FAILURE MACSTR, + MAC2STR(sm->peer_addr)); } @@ -1009,6 +1017,9 @@ SM_STATE(EAP, RETRANSMIT2) if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0) sm->eap_if.eapReq = TRUE; } + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_RETRANSMIT2 MACSTR, + MAC2STR(sm->peer_addr)); } @@ -1099,6 +1110,9 @@ SM_STATE(EAP, TIMEOUT_FAILURE2) SM_ENTRY(EAP, TIMEOUT_FAILURE2); sm->eap_if.eapTimeout = TRUE; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TIMEOUT_FAILURE2 MACSTR, + MAC2STR(sm->peer_addr)); } @@ -1108,6 +1122,9 @@ SM_STATE(EAP, FAILURE2) eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData); sm->eap_if.eapFail = TRUE; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE2 MACSTR, + MAC2STR(sm->peer_addr)); } @@ -1134,6 +1151,9 @@ SM_STATE(EAP, SUCCESS2) * started properly. */ sm->start_reauth = TRUE; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS2 MACSTR, + MAC2STR(sm->peer_addr)); } @@ -1800,6 +1820,8 @@ static void eap_user_free(struct eap_user *user) return; bin_clear_free(user->password, user->password_len); user->password = NULL; + bin_clear_free(user->salt, user->salt_len); + user->salt = NULL; os_free(user); } @@ -1866,6 +1888,7 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx, sm->server_id_len = conf->server_id_len; sm->erp = conf->erp; sm->tls_session_lifetime = conf->tls_session_lifetime; + sm->tls_flags = conf->tls_flags; #ifdef CONFIG_TESTING_OPTIONS sm->tls_test_flags = conf->tls_test_flags; @@ -1897,6 +1920,7 @@ void eap_server_sm_deinit(struct eap_sm *sm) wpabuf_free(sm->lastReqData); wpabuf_free(sm->eap_if.eapRespData); os_free(sm->identity); + os_free(sm->serial_num); os_free(sm->pac_opaque_encr_key); os_free(sm->eap_fast_a_id); os_free(sm->eap_fast_a_id_info); @@ -1969,6 +1993,55 @@ const u8 * eap_get_identity(struct eap_sm *sm, size_t *len) /** + * eap_get_serial_num - Get the serial number of user certificate + * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() + * Returns: Pointer to the serial number or %NULL if not available + */ +const char * eap_get_serial_num(struct eap_sm *sm) +{ + return sm->serial_num; +} + + +void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len) +{ +#ifdef CONFIG_ERP + const struct eap_hdr *hdr; + const u8 *pos, *end; + struct erp_tlvs parse; + + if (len < sizeof(*hdr) + 1) + return; + hdr = (const struct eap_hdr *) eap; + end = eap + len; + pos = (const u8 *) (hdr + 1); + if (hdr->code != EAP_CODE_INITIATE || *pos != EAP_ERP_TYPE_REAUTH) + return; + pos++; + if (pos + 3 > end) + return; + + /* Skip Flags and SEQ */ + pos += 3; + + if (erp_parse_tlvs(pos, end, &parse, 1) < 0 || !parse.keyname) + return; + wpa_hexdump_ascii(MSG_DEBUG, + "EAP: Update identity based on EAP-Initiate/Re-auth keyName-NAI", + parse.keyname, parse.keyname_len); + os_free(sm->identity); + sm->identity = os_malloc(parse.keyname_len); + if (sm->identity) { + os_memcpy(sm->identity, parse.keyname, parse.keyname_len); + sm->identity_len = parse.keyname_len; + } else { + sm->identity_len = 0; + } +#endif /* CONFIG_ERP */ +} + + +/** * eap_get_interface - Get pointer to EAP-EAPOL interface data * @sm: Pointer to EAP state machine allocated with eap_server_sm_init() * Returns: Pointer to the EAP-EAPOL interface data diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c index a8bb5eae6b56a..175021163c1df 100644 --- a/src/eap_server/eap_server_aka.c +++ b/src/eap_server/eap_server_aka.c @@ -1261,10 +1261,9 @@ static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_SIM_KEYING_DATA_LEN); + key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); *len = EAP_SIM_KEYING_DATA_LEN; return key; } @@ -1278,10 +1277,9 @@ static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; } diff --git a/src/eap_server/eap_server_eke.c b/src/eap_server/eap_server_eke.c index 1eba8f515648a..71580bf7bf527 100644 --- a/src/eap_server/eap_server_eke.c +++ b/src/eap_server/eap_server_eke.c @@ -467,13 +467,12 @@ static void eap_eke_process_identity(struct eap_sm *sm, data->peerid_type = *pos++; os_free(data->peerid); - data->peerid = os_malloc(end - pos); + data->peerid = os_memdup(pos, end - pos); if (data->peerid == NULL) { wpa_printf(MSG_INFO, "EAP-EKE: Failed to allocate memory for peerid"); eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR); return; } - os_memcpy(data->peerid, pos, end - pos); data->peerid_len = end - pos; wpa_printf(MSG_DEBUG, "EAP-EKE: Peer IDType %u", data->peerid_type); wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Peer Identity", @@ -731,10 +730,9 @@ static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -749,10 +747,9 @@ static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c index 20491726880e9..a63f820465c8b 100644 --- a/src/eap_server/eap_server_fast.c +++ b/src/eap_server/eap_server_fast.c @@ -471,12 +471,11 @@ static void * eap_fast_init(struct eap_sm *sm) eap_fast_reset(sm, data); return NULL; } - data->srv_id = os_malloc(sm->eap_fast_a_id_len); + data->srv_id = os_memdup(sm->eap_fast_a_id, sm->eap_fast_a_id_len); if (data->srv_id == NULL) { eap_fast_reset(sm, data); return NULL; } - os_memcpy(data->srv_id, sm->eap_fast_a_id, sm->eap_fast_a_id_len); data->srv_id_len = sm->eap_fast_a_id_len; if (sm->eap_fast_a_id_info == NULL) { @@ -561,7 +560,7 @@ static int eap_fast_phase1_done(struct eap_sm *sm, struct eap_fast_data *data) return -1; } data->anon_provisioning = os_strstr(cipher, "ADH") != NULL; - + if (data->anon_provisioning) { wpa_printf(MSG_DEBUG, "EAP-FAST: Anonymous provisioning"); eap_fast_derive_key_provisioning(sm, data); @@ -789,7 +788,7 @@ static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm, /* A-ID (inside PAC-Info) */ eap_fast_put_tlv(buf, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len); - + /* Note: headers may be misaligned after A-ID */ if (sm->identity) { @@ -1517,7 +1516,7 @@ static void eap_fast_process_msg(struct eap_sm *sm, void *priv, if (eap_fast_process_phase1(sm, data)) break; - /* fall through to PHASE2_START */ + /* fall through */ case PHASE2_START: eap_fast_process_phase2_start(sm, data); break; diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c index 94e74ec9b2f7d..fb3d11748c8c2 100644 --- a/src/eap_server/eap_server_gpsk.c +++ b/src/eap_server/eap_server_gpsk.c @@ -269,13 +269,12 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, return; } os_free(data->id_peer); - data->id_peer = os_malloc(alen); + data->id_peer = os_memdup(pos, alen); if (data->id_peer == NULL) { wpa_printf(MSG_DEBUG, "EAP-GPSK: Not enough memory to store " "%d-octet ID_Peer", alen); return; } - os_memcpy(data->id_peer, pos, alen); data->id_peer_len = alen; wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer", data->id_peer, data->id_peer_len); @@ -575,10 +574,9 @@ static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -593,10 +591,9 @@ static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; @@ -618,10 +615,9 @@ static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - sid = os_malloc(data->id_len); + sid = os_memdup(data->session_id, data->id_len); if (sid == NULL) return NULL; - os_memcpy(sid, data->session_id, data->id_len); *len = data->id_len; return sid; diff --git a/src/eap_server/eap_server_gtc.c b/src/eap_server/eap_server_gtc.c index 193a8517ac086..fcccbcbd5efa7 100644 --- a/src/eap_server/eap_server_gtc.c +++ b/src/eap_server/eap_server_gtc.c @@ -141,12 +141,11 @@ static void eap_gtc_process(struct eap_sm *sm, void *priv, } else { os_free(sm->identity); sm->identity_len = pos2 - pos; - sm->identity = os_malloc(sm->identity_len); + sm->identity = os_memdup(pos, sm->identity_len); if (sm->identity == NULL) { data->state = FAILURE; return; } - os_memcpy(sm->identity, pos, sm->identity_len); } if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) { diff --git a/src/eap_server/eap_server_ikev2.c b/src/eap_server/eap_server_ikev2.c index 3a249d141e0cf..32e6872045c54 100644 --- a/src/eap_server/eap_server_ikev2.c +++ b/src/eap_server/eap_server_ikev2.c @@ -103,10 +103,9 @@ static void * eap_ikev2_init(struct eap_sm *sm) data->ikev2.proposal.encr = ENCR_AES_CBC; data->ikev2.proposal.dh = DH_GROUP2_1024BIT_MODP; - data->ikev2.IDi = os_malloc(sm->server_id_len); + data->ikev2.IDi = os_memdup(sm->server_id, sm->server_id_len); if (data->ikev2.IDi == NULL) goto failed; - os_memcpy(data->ikev2.IDi, sm->server_id, sm->server_id_len); data->ikev2.IDi_len = sm->server_id_len; data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret; @@ -224,7 +223,7 @@ static struct wpabuf * eap_ikev2_buildReq(struct eap_sm *sm, void *priv, u8 id) } data->out_used = 0; } - /* pass through */ + /* fall through */ case WAIT_FRAG_ACK: return eap_ikev2_build_msg(data, id); case FRAG_ACK: diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c index 460cd9c82ff56..6c47bb636aab9 100644 --- a/src/eap_server/eap_server_mschapv2.c +++ b/src/eap_server/eap_server_mschapv2.c @@ -71,13 +71,12 @@ static void * eap_mschapv2_init(struct eap_sm *sm) } if (sm->peer_challenge) { - data->peer_challenge = os_malloc(CHALLENGE_LEN); + data->peer_challenge = os_memdup(sm->peer_challenge, + CHALLENGE_LEN); if (data->peer_challenge == NULL) { os_free(data); return NULL; } - os_memcpy(data->peer_challenge, sm->peer_challenge, - CHALLENGE_LEN); } return data; diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c index 782b8c316537e..3257789695cde 100644 --- a/src/eap_server/eap_server_pax.c +++ b/src/eap_server/eap_server_pax.c @@ -327,13 +327,12 @@ static void eap_pax_process_std_2(struct eap_sm *sm, } data->cid_len = cid_len; os_free(data->cid); - data->cid = os_malloc(data->cid_len); + data->cid = os_memdup(pos + 2, data->cid_len); if (data->cid == NULL) { wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for " "CID"); return; } - os_memcpy(data->cid, pos + 2, data->cid_len); pos += 2 + data->cid_len; left -= 2 + data->cid_len; wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID", diff --git a/src/eap_server/eap_server_psk.c b/src/eap_server/eap_server_psk.c index 857d421393bc8..0eab89339ff61 100644 --- a/src/eap_server/eap_server_psk.c +++ b/src/eap_server/eap_server_psk.c @@ -236,13 +236,12 @@ static void eap_psk_process_2(struct eap_sm *sm, left -= sizeof(*resp); os_free(data->id_p); - data->id_p = os_malloc(left); + data->id_p = os_memdup(cpos, left); if (data->id_p == NULL) { wpa_printf(MSG_INFO, "EAP-PSK: Failed to allocate memory for " "ID_P"); return; } - os_memcpy(data->id_p, cpos, left); data->id_p_len = left; wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PSK: ID_P", data->id_p, data->id_p_len); @@ -371,10 +370,9 @@ static void eap_psk_process_4(struct eap_sm *sm, pos += 16; left -= 16; - decrypted = os_malloc(left); + decrypted = os_memdup(pos, left); if (decrypted == NULL) return; - os_memcpy(decrypted, pos, left); if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), wpabuf_head(respData), 22, decrypted, left, @@ -450,10 +448,9 @@ static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -468,10 +465,9 @@ static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c index 64bf708e039a4..d0fa54a3aba7d 100644 --- a/src/eap_server/eap_server_pwd.c +++ b/src/eap_server/eap_server_pwd.c @@ -11,6 +11,7 @@ #include "common.h" #include "crypto/sha256.h" #include "crypto/ms_funcs.h" +#include "crypto/crypto.h" #include "eap_server/eap_i.h" #include "eap_common/eap_pwd_common.h" @@ -26,8 +27,11 @@ struct eap_pwd_data { u8 *password; size_t password_len; int password_hash; + u8 *salt; + size_t salt_len; u32 token; u16 group_num; + u8 password_prep; EAP_PWD_group *grp; struct wpabuf *inbuf; @@ -36,20 +40,18 @@ struct eap_pwd_data { size_t out_frag_pos; size_t mtu; - BIGNUM *k; - BIGNUM *private_value; - BIGNUM *peer_scalar; - BIGNUM *my_scalar; - EC_POINT *my_element; - EC_POINT *peer_element; + struct crypto_bignum *k; + struct crypto_bignum *private_value; + struct crypto_bignum *peer_scalar; + struct crypto_bignum *my_scalar; + struct crypto_ec_point *my_element; + struct crypto_ec_point *peer_element; u8 my_confirm[SHA256_MAC_LEN]; u8 msk[EAP_MSK_LEN]; u8 emsk[EAP_EMSK_LEN]; u8 session_id[1 + SHA256_MAC_LEN]; - - BN_CTX *bnctx; }; @@ -116,13 +118,17 @@ static void * eap_pwd_init(struct eap_sm *sm) os_memcpy(data->password, sm->user->password, data->password_len); data->password_hash = sm->user->password_hash; - data->bnctx = BN_CTX_new(); - if (data->bnctx == NULL) { - wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail"); - bin_clear_free(data->password, data->password_len); - bin_clear_free(data->id_server, data->id_server_len); - os_free(data); - return NULL; + data->salt_len = sm->user->salt_len; + if (data->salt_len) { + data->salt = os_memdup(sm->user->salt, sm->user->salt_len); + if (!data->salt) { + wpa_printf(MSG_INFO, + "EAP-pwd: Memory allocation of salt failed"); + bin_clear_free(data->id_server, data->id_server_len); + bin_clear_free(data->password, data->password_len); + os_free(data); + return NULL; + } } data->in_frag_pos = data->out_frag_pos = 0; @@ -138,21 +144,19 @@ static void eap_pwd_reset(struct eap_sm *sm, void *priv) { struct eap_pwd_data *data = priv; - BN_clear_free(data->private_value); - BN_clear_free(data->peer_scalar); - BN_clear_free(data->my_scalar); - BN_clear_free(data->k); - BN_CTX_free(data->bnctx); - EC_POINT_clear_free(data->my_element); - EC_POINT_clear_free(data->peer_element); + crypto_bignum_deinit(data->private_value, 1); + crypto_bignum_deinit(data->peer_scalar, 1); + crypto_bignum_deinit(data->my_scalar, 1); + crypto_bignum_deinit(data->k, 1); + crypto_ec_point_deinit(data->my_element, 1); + crypto_ec_point_deinit(data->peer_element, 1); bin_clear_free(data->id_peer, data->id_peer_len); bin_clear_free(data->id_server, data->id_server_len); bin_clear_free(data->password, data->password_len); + bin_clear_free(data->salt, data->salt_len); if (data->grp) { - EC_GROUP_free(data->grp->group); - EC_POINT_clear_free(data->grp->pwe); - BN_clear_free(data->grp->order); - BN_clear_free(data->grp->prime); + crypto_ec_deinit(data->grp->group); + crypto_ec_point_deinit(data->grp->pwe, 1); os_free(data->grp); } wpabuf_free(data->inbuf); @@ -185,12 +189,45 @@ static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, return; } + wpa_hexdump_key(MSG_DEBUG, "EAP-pwd (server): password", + data->password, data->password_len); + if (data->salt_len) + wpa_hexdump(MSG_DEBUG, "EAP-pwd (server): salt", + data->salt, data->salt_len); + + /* + * If this is a salted password then figure out how it was hashed + * based on the length. + */ + if (data->salt_len) { + switch (data->password_len) { + case 20: + data->password_prep = EAP_PWD_PREP_SSHA1; + break; + case 32: + data->password_prep = EAP_PWD_PREP_SSHA256; + break; + case 64: + data->password_prep = EAP_PWD_PREP_SSHA512; + break; + default: + wpa_printf(MSG_INFO, + "EAP-pwd (server): bad size %d for salted password", + (int) data->password_len); + eap_pwd_state(data, FAILURE); + return; + } + } else { + /* Otherwise, figure out whether it's MS hashed or plain */ + data->password_prep = data->password_hash ? EAP_PWD_PREP_MS : + EAP_PWD_PREP_NONE; + } + wpabuf_put_be16(data->outbuf, data->group_num); wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC); wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF); wpabuf_put_data(data->outbuf, &data->token, sizeof(data->token)); - wpabuf_put_u8(data->outbuf, data->password_hash ? EAP_PWD_PREP_MS : - EAP_PWD_PREP_NONE); + wpabuf_put_u8(data->outbuf, data->password_prep); wpabuf_put_data(data->outbuf, data->id_server, data->id_server_len); } @@ -198,9 +235,9 @@ static void eap_pwd_build_id_req(struct eap_sm *sm, struct eap_pwd_data *data, static void eap_pwd_build_commit_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) { - BIGNUM *mask = NULL, *x = NULL, *y = NULL; + struct crypto_bignum *mask = NULL; u8 *scalar = NULL, *element = NULL; - u16 offset; + size_t prime_len, order_len; wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request"); /* @@ -210,93 +247,82 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm, if (data->out_frag_pos) return; - if (((data->private_value = BN_new()) == NULL) || - ((data->my_element = EC_POINT_new(data->grp->group)) == NULL) || - ((data->my_scalar = BN_new()) == NULL) || - ((mask = BN_new()) == NULL)) { + prime_len = crypto_ec_prime_len(data->grp->group); + order_len = crypto_ec_order_len(data->grp->group); + + data->private_value = crypto_bignum_init(); + data->my_element = crypto_ec_point_init(data->grp->group); + data->my_scalar = crypto_bignum_init(); + mask = crypto_bignum_init(); + if (!data->private_value || !data->my_element || !data->my_scalar || + !mask) { wpa_printf(MSG_INFO, "EAP-PWD (server): scalar allocation " "fail"); goto fin; } - if (BN_rand_range(data->private_value, data->grp->order) != 1 || - BN_rand_range(mask, data->grp->order) != 1 || - BN_add(data->my_scalar, data->private_value, mask) != 1 || - BN_mod(data->my_scalar, data->my_scalar, data->grp->order, - data->bnctx) != 1) { + if (crypto_bignum_rand(data->private_value, + crypto_ec_get_order(data->grp->group)) < 0 || + crypto_bignum_rand(mask, + crypto_ec_get_order(data->grp->group)) < 0 || + crypto_bignum_add(data->private_value, mask, data->my_scalar) < 0 || + crypto_bignum_mod(data->my_scalar, + crypto_ec_get_order(data->grp->group), + data->my_scalar) < 0) { wpa_printf(MSG_INFO, "EAP-pwd (server): unable to get randomness"); goto fin; } - if (!EC_POINT_mul(data->grp->group, data->my_element, NULL, - data->grp->pwe, mask, data->bnctx)) { + if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask, + data->my_element) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): element allocation " "fail"); eap_pwd_state(data, FAILURE); goto fin; } - if (!EC_POINT_invert(data->grp->group, data->my_element, data->bnctx)) - { + if (crypto_ec_point_invert(data->grp->group, data->my_element) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): element inversion " "fail"); goto fin; } - BN_clear_free(mask); - if (((x = BN_new()) == NULL) || - ((y = BN_new()) == NULL)) { - wpa_printf(MSG_INFO, "EAP-PWD (server): point allocation " - "fail"); + scalar = os_malloc(order_len); + element = os_malloc(prime_len * 2); + if (!scalar || !element) { + wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail"); goto fin; } - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->my_element, x, y, - data->bnctx)) { + + if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element, + element + prime_len) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment " "fail"); goto fin; } - if (((scalar = os_malloc(BN_num_bytes(data->grp->order))) == NULL) || - ((element = os_malloc(BN_num_bytes(data->grp->prime) * 2)) == - NULL)) { - wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail"); - goto fin; - } + crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len); - /* - * bignums occupy as little memory as possible so one that is - * sufficiently smaller than the prime or order might need pre-pending - * with zeros. - */ - os_memset(scalar, 0, BN_num_bytes(data->grp->order)); - os_memset(element, 0, BN_num_bytes(data->grp->prime) * 2); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->my_scalar); - BN_bn2bin(data->my_scalar, scalar + offset); - - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, element + offset); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, element + BN_num_bytes(data->grp->prime) + offset); - - data->outbuf = wpabuf_alloc(2 * BN_num_bytes(data->grp->prime) + - BN_num_bytes(data->grp->order)); + data->outbuf = wpabuf_alloc(2 * prime_len + order_len + + (data->salt ? 1 + data->salt_len : 0)); if (data->outbuf == NULL) goto fin; + /* If we're doing salted password prep, add the salt */ + if (data->salt_len) { + wpabuf_put_u8(data->outbuf, data->salt_len); + wpabuf_put_data(data->outbuf, data->salt, data->salt_len); + } + /* We send the element as (x,y) followed by the scalar */ - wpabuf_put_data(data->outbuf, element, - 2 * BN_num_bytes(data->grp->prime)); - wpabuf_put_data(data->outbuf, scalar, BN_num_bytes(data->grp->order)); + wpabuf_put_data(data->outbuf, element, 2 * prime_len); + wpabuf_put_data(data->outbuf, scalar, order_len); fin: + crypto_bignum_deinit(mask, 1); os_free(scalar); os_free(element); - BN_clear_free(x); - BN_clear_free(y); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); } @@ -305,11 +331,10 @@ fin: static void eap_pwd_build_confirm_req(struct eap_sm *sm, struct eap_pwd_data *data, u8 id) { - BIGNUM *x = NULL, *y = NULL; struct crypto_hash *hash; u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; u16 grp; - int offset; + size_t prime_len, order_len; wpa_printf(MSG_DEBUG, "EAP-pwd: Confirm/Request"); /* @@ -319,9 +344,12 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm, if (data->out_frag_pos) return; + prime_len = crypto_ec_prime_len(data->grp->group); + order_len = crypto_ec_order_len(data->grp->group); + /* Each component of the cruft will be at most as big as the prime */ - if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || - ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { + cruft = os_malloc(prime_len * 2); + if (!cruft) { wpa_printf(MSG_INFO, "EAP-PWD (server): debug allocation " "fail"); goto fin; @@ -341,64 +369,38 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm, * * First is k */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); - BN_bn2bin(data->k, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len); + eap_pwd_h_update(hash, cruft, prime_len); /* server element: x, y */ - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->my_element, x, y, - data->bnctx)) { + if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft, + cruft + prime_len) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " "assignment fail"); goto fin; } - - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, prime_len * 2); /* server scalar */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->my_scalar); - BN_bn2bin(data->my_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); /* peer element: x, y */ - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->peer_element, x, y, - data->bnctx)) { + if (crypto_ec_point_to_bin(data->grp->group, data->peer_element, cruft, + cruft + prime_len) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " "assignment fail"); goto fin; } - - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, prime_len * 2); /* peer scalar */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->peer_scalar); - BN_bn2bin(data->peer_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + crypto_bignum_to_bin(data->peer_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); /* ciphersuite */ grp = htons(data->group_num); - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); + os_memset(cruft, 0, prime_len); ptr = cruft; os_memcpy(ptr, &grp, sizeof(u16)); ptr += sizeof(u16); @@ -419,9 +421,7 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm, wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN); fin: - bin_clear_free(cruft, BN_num_bytes(data->grp->prime)); - BN_clear_free(x); - BN_clear_free(y); + bin_clear_free(cruft, prime_len * 2); if (data->outbuf == NULL) eap_pwd_state(data, FAILURE); } @@ -602,11 +602,16 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm, if ((data->group_num != be_to_host16(id->group_num)) || (id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) || (os_memcmp(id->token, (u8 *)&data->token, sizeof(data->token))) || - (id->prf != EAP_PWD_DEFAULT_PRF)) { + (id->prf != EAP_PWD_DEFAULT_PRF) || + (id->prep != data->password_prep)) { wpa_printf(MSG_INFO, "EAP-pwd: peer changed parameters"); eap_pwd_state(data, FAILURE); return; } + if (data->id_peer || data->grp) { + wpa_printf(MSG_INFO, "EAP-pwd: data was already allocated"); + return; + } data->id_peer = os_malloc(payload_len - sizeof(struct eap_pwd_id)); if (data->id_peer == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: memory allocation id fail"); @@ -617,14 +622,19 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm, wpa_hexdump_ascii(MSG_DEBUG, "EAP-PWD (server): peer sent id of", data->id_peer, data->id_peer_len); - data->grp = os_zalloc(sizeof(EAP_PWD_group)); + data->grp = get_eap_pwd_group(data->group_num); if (data->grp == NULL) { wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for " "group"); return; } - if (data->password_hash) { + /* + * If it's PREP_MS then hash the password again, otherwise regardless + * of the prep the client is doing, the password we have is the one to + * use to generate the password element. + */ + if (data->password_prep == EAP_PWD_PREP_MS) { res = hash_nt_password_hash(data->password, pwhashhash); if (res) return; @@ -647,7 +657,7 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm, return; } wpa_printf(MSG_DEBUG, "EAP-PWD (server): computed %d bit PWE...", - BN_num_bits(data->grp->prime)); + (int) crypto_ec_prime_len_bits(data->grp->group)); eap_pwd_state(data, PWD_Commit_Req); } @@ -657,16 +667,16 @@ static void eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, const u8 *payload, size_t payload_len) { - u8 *ptr; - BIGNUM *x = NULL, *y = NULL, *cofactor = NULL; - EC_POINT *K = NULL, *point = NULL; + const u8 *ptr; + struct crypto_bignum *cofactor = NULL; + struct crypto_ec_point *K = NULL, *point = NULL; int res = 0; size_t prime_len, order_len; wpa_printf(MSG_DEBUG, "EAP-pwd: Received commit response"); - prime_len = BN_num_bytes(data->grp->prime); - order_len = BN_num_bytes(data->grp->order); + prime_len = crypto_ec_prime_len(data->grp->group); + order_len = crypto_ec_order_len(data->grp->group); if (payload_len != 2 * prime_len + order_len) { wpa_printf(MSG_INFO, @@ -676,49 +686,47 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, goto fin; } - if (((data->peer_scalar = BN_new()) == NULL) || - ((data->k = BN_new()) == NULL) || - ((cofactor = BN_new()) == NULL) || - ((x = BN_new()) == NULL) || - ((y = BN_new()) == NULL) || - ((point = EC_POINT_new(data->grp->group)) == NULL) || - ((K = EC_POINT_new(data->grp->group)) == NULL) || - ((data->peer_element = EC_POINT_new(data->grp->group)) == NULL)) { + data->k = crypto_bignum_init(); + cofactor = crypto_bignum_init(); + point = crypto_ec_point_init(data->grp->group); + K = crypto_ec_point_init(data->grp->group); + if (!data->k || !cofactor || !point || !K) { wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation " "fail"); goto fin; } - if (!EC_GROUP_get_cofactor(data->grp->group, cofactor, NULL)) { + if (crypto_ec_cofactor(data->grp->group, cofactor) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): unable to get " "cofactor for curve"); goto fin; } /* element, x then y, followed by scalar */ - ptr = (u8 *) payload; - BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), x); - ptr += BN_num_bytes(data->grp->prime); - BN_bin2bn(ptr, BN_num_bytes(data->grp->prime), y); - ptr += BN_num_bytes(data->grp->prime); - BN_bin2bn(ptr, BN_num_bytes(data->grp->order), data->peer_scalar); - if (!EC_POINT_set_affine_coordinates_GFp(data->grp->group, - data->peer_element, x, y, - data->bnctx)) { + ptr = payload; + data->peer_element = crypto_ec_point_from_bin(data->grp->group, ptr); + if (!data->peer_element) { wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element " "fail"); goto fin; } + ptr += prime_len * 2; + data->peer_scalar = crypto_bignum_init_set(ptr, order_len); + if (!data->peer_scalar) { + wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation " + "fail"); + goto fin; + } /* check to ensure peer's element is not in a small sub-group */ - if (BN_cmp(cofactor, BN_value_one())) { - if (!EC_POINT_mul(data->grp->group, point, NULL, - data->peer_element, cofactor, NULL)) { + if (!crypto_bignum_is_one(cofactor)) { + if (crypto_ec_point_mul(data->grp->group, data->peer_element, + cofactor, point) != 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): cannot " "multiply peer element by order"); goto fin; } - if (EC_POINT_is_at_infinity(data->grp->group, point)) { + if (crypto_ec_point_is_at_infinity(data->grp->group, point)) { wpa_printf(MSG_INFO, "EAP-PWD (server): peer element " "is at infinity!\n"); goto fin; @@ -726,21 +734,21 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, } /* compute the shared key, k */ - if ((!EC_POINT_mul(data->grp->group, K, NULL, data->grp->pwe, - data->peer_scalar, data->bnctx)) || - (!EC_POINT_add(data->grp->group, K, K, data->peer_element, - data->bnctx)) || - (!EC_POINT_mul(data->grp->group, K, NULL, K, data->private_value, - data->bnctx))) { + if ((crypto_ec_point_mul(data->grp->group, data->grp->pwe, + data->peer_scalar, K) < 0) || + (crypto_ec_point_add(data->grp->group, K, data->peer_element, + K) < 0) || + (crypto_ec_point_mul(data->grp->group, K, data->private_value, + K) < 0)) { wpa_printf(MSG_INFO, "EAP-PWD (server): computing shared key " "fail"); goto fin; } /* ensure that the shared key isn't in a small sub-group */ - if (BN_cmp(cofactor, BN_value_one())) { - if (!EC_POINT_mul(data->grp->group, K, NULL, K, cofactor, - NULL)) { + if (!crypto_bignum_is_one(cofactor)) { + if (crypto_ec_point_mul(data->grp->group, K, cofactor, + K) != 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): cannot " "multiply shared key point by order!\n"); goto fin; @@ -753,13 +761,12 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, * never going to happen it is a simple and safe check "just to be * sure" so let's be safe. */ - if (EC_POINT_is_at_infinity(data->grp->group, K)) { + if (crypto_ec_point_is_at_infinity(data->grp->group, K)) { wpa_printf(MSG_INFO, "EAP-PWD (server): shared key point is " "at infinity"); goto fin; } - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, K, data->k, - NULL, data->bnctx)) { + if (crypto_ec_point_x(data->grp->group, K, data->k)) { wpa_printf(MSG_INFO, "EAP-PWD (server): unable to extract " "shared secret from secret point"); goto fin; @@ -767,11 +774,9 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data, res = 1; fin: - EC_POINT_clear_free(K); - EC_POINT_clear_free(point); - BN_clear_free(cofactor); - BN_clear_free(x); - BN_clear_free(y); + crypto_ec_point_deinit(K, 1); + crypto_ec_point_deinit(point, 1); + crypto_bignum_deinit(cofactor, 1); if (res) eap_pwd_state(data, PWD_Confirm_Req); @@ -784,12 +789,14 @@ static void eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, const u8 *payload, size_t payload_len) { - BIGNUM *x = NULL, *y = NULL; struct crypto_hash *hash; u32 cs; u16 grp; u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr; - int offset; + size_t prime_len, order_len; + + prime_len = crypto_ec_prime_len(data->grp->group); + order_len = crypto_ec_order_len(data->grp->group); if (payload_len != SHA256_MAC_LEN) { wpa_printf(MSG_INFO, @@ -808,8 +815,8 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, *ptr = EAP_PWD_DEFAULT_PRF; /* each component of the cruft will be at most as big as the prime */ - if (((cruft = os_malloc(BN_num_bytes(data->grp->prime))) == NULL) || - ((x = BN_new()) == NULL) || ((y = BN_new()) == NULL)) { + cruft = os_malloc(prime_len * 2); + if (!cruft) { wpa_printf(MSG_INFO, "EAP-PWD (peer): allocation fail"); goto fin; } @@ -823,62 +830,36 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, goto fin; /* k */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(data->k); - BN_bn2bin(data->k, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + crypto_bignum_to_bin(data->k, cruft, prime_len, prime_len); + eap_pwd_h_update(hash, cruft, prime_len); /* peer element: x, y */ - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->peer_element, x, y, - data->bnctx)) { + if (crypto_ec_point_to_bin(data->grp->group, data->peer_element, cruft, + cruft + prime_len) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " "assignment fail"); goto fin; } - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, prime_len * 2); /* peer scalar */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->peer_scalar); - BN_bn2bin(data->peer_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + crypto_bignum_to_bin(data->peer_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); /* server element: x, y */ - if (!EC_POINT_get_affine_coordinates_GFp(data->grp->group, - data->my_element, x, y, - data->bnctx)) { + if (crypto_ec_point_to_bin(data->grp->group, data->my_element, cruft, + cruft + prime_len) < 0) { wpa_printf(MSG_INFO, "EAP-PWD (server): confirm point " "assignment fail"); goto fin; } - - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(x); - BN_bn2bin(x, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->prime) - BN_num_bytes(y); - BN_bn2bin(y, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->prime)); + eap_pwd_h_update(hash, cruft, prime_len * 2); /* server scalar */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); - offset = BN_num_bytes(data->grp->order) - - BN_num_bytes(data->my_scalar); - BN_bn2bin(data->my_scalar, cruft + offset); - eap_pwd_h_update(hash, cruft, BN_num_bytes(data->grp->order)); + crypto_bignum_to_bin(data->my_scalar, cruft, order_len, order_len); + eap_pwd_h_update(hash, cruft, order_len); /* ciphersuite */ - os_memset(cruft, 0, BN_num_bytes(data->grp->prime)); eap_pwd_h_update(hash, (u8 *) &cs, sizeof(u32)); /* all done */ @@ -892,7 +873,7 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, } wpa_printf(MSG_DEBUG, "EAP-pwd (server): confirm verified"); - if (compute_keys(data->grp, data->bnctx, data->k, + if (compute_keys(data->grp, data->k, data->peer_scalar, data->my_scalar, conf, data->my_confirm, &cs, data->msk, data->emsk, data->session_id) < 0) @@ -901,9 +882,7 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data, eap_pwd_state(data, SUCCESS); fin: - bin_clear_free(cruft, BN_num_bytes(data->grp->prime)); - BN_clear_free(x); - BN_clear_free(y); + bin_clear_free(cruft, prime_len * 2); } @@ -1033,11 +1012,10 @@ static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -1052,11 +1030,10 @@ static u8 * eap_pwd_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; @@ -1085,11 +1062,10 @@ static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - id = os_malloc(1 + SHA256_MAC_LEN); + id = os_memdup(data->session_id, 1 + SHA256_MAC_LEN); if (id == NULL) return NULL; - os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN); *len = 1 + SHA256_MAC_LEN; return id; @@ -1127,4 +1103,3 @@ int eap_server_pwd_register(void) return eap_server_method_register(eap); } - diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c index 84d0e0be4dd12..66183f5f5c042 100644 --- a/src/eap_server/eap_server_sake.c +++ b/src/eap_server/eap_server_sake.c @@ -326,10 +326,9 @@ static void eap_sake_process_challenge(struct eap_sm *sm, data->peerid = NULL; data->peerid_len = 0; if (attr.peerid) { - data->peerid = os_malloc(attr.peerid_len); + data->peerid = os_memdup(attr.peerid, attr.peerid_len); if (data->peerid == NULL) return; - os_memcpy(data->peerid, attr.peerid, attr.peerid_len); data->peerid_len = attr.peerid_len; } @@ -460,10 +459,9 @@ static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_MSK_LEN); + key = os_memdup(data->msk, EAP_MSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_MSK_LEN); *len = EAP_MSK_LEN; return key; @@ -478,10 +476,9 @@ static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c index 3a6ed795c7680..10637d4c66b90 100644 --- a/src/eap_server/eap_server_sim.c +++ b/src/eap_server/eap_server_sim.c @@ -787,10 +787,9 @@ static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_SIM_KEYING_DATA_LEN); + key = os_memdup(data->msk, EAP_SIM_KEYING_DATA_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); *len = EAP_SIM_KEYING_DATA_LEN; return key; } @@ -804,10 +803,9 @@ static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = os_malloc(EAP_EMSK_LEN); + key = os_memdup(data->emsk, EAP_EMSK_LEN); if (key == NULL) return NULL; - os_memcpy(key, data->emsk, EAP_EMSK_LEN); *len = EAP_EMSK_LEN; return key; } diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c index 7249858844ef2..8b9e53c61d799 100644 --- a/src/eap_server/eap_server_tls.c +++ b/src/eap_server/eap_server_tls.c @@ -302,17 +302,22 @@ static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_tls_data *data = priv; u8 *eapKeyData; + const char *label; if (data->state != SUCCESS) return NULL; - eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, - "client EAP encryption", - EAP_TLS_KEY_LEN); + if (data->ssl.tls_v13) + label = "EXPORTER_EAP_TLS_Key_Material"; + else + label = "client EAP encryption"; + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label, + EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { *len = EAP_TLS_KEY_LEN; wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key", eapKeyData, EAP_TLS_KEY_LEN); + os_memset(eapKeyData + EAP_TLS_KEY_LEN, 0, EAP_EMSK_LEN); } else { wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key"); } @@ -325,12 +330,16 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) { struct eap_tls_data *data = priv; u8 *eapKeyData, *emsk; + const char *label; if (data->state != SUCCESS) return NULL; - eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, - "client EAP encryption", + if (data->ssl.tls_v13) + label = "EXPORTER_EAP_TLS_Key_Material"; + else + label = "client EAP encryption"; + eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label, EAP_TLS_KEY_LEN + EAP_EMSK_LEN); if (eapKeyData) { emsk = os_malloc(EAP_EMSK_LEN); diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c index 69096954b8262..0ae7867fccf7f 100644 --- a/src/eap_server/eap_server_tls_common.c +++ b/src/eap_server/eap_server_tls_common.c @@ -47,7 +47,7 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, int verify_peer, int eap_type) { u8 session_ctx[8]; - unsigned int flags = 0; + unsigned int flags = sm->tls_flags; if (sm->ssl_ctx == NULL) { wpa_printf(MSG_ERROR, "TLS context not initialized - cannot use TLS-based EAP method"); @@ -107,7 +107,7 @@ void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, - char *label, size_t len) + const char *label, size_t len) { u8 *out; @@ -145,6 +145,13 @@ u8 * eap_server_tls_derive_session_id(struct eap_sm *sm, struct tls_random keys; u8 *out; + if (eap_type == EAP_TYPE_TLS && data->tls_v13) { + *len = 64; + return eap_server_tls_derive_key(sm, data, + "EXPORTER_EAP_TLS_Session-Id", + 64); + } + if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys)) return NULL; @@ -305,6 +312,8 @@ static int eap_server_tls_process_fragment(struct eap_ssl_data *data, int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data) { + char buf[20]; + if (data->tls_out) { /* This should not happen.. */ wpa_printf(MSG_INFO, "SSL: pending tls_out data when " @@ -327,6 +336,16 @@ int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data) return -1; } + if (tls_get_version(sm->ssl_ctx, data->conn, buf, sizeof(buf)) == 0) { + wpa_printf(MSG_DEBUG, "SSL: Using TLS version %s", buf); + data->tls_v13 = os_strcmp(buf, "TLSv1.3") == 0; + } + + if (!sm->serial_num && + tls_connection_established(sm->ssl_ctx, data->conn)) + sm->serial_num = tls_connection_peer_serial_num(sm->ssl_ctx, + data->conn); + return 0; } @@ -373,7 +392,7 @@ static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags, if (data->tls_in && eap_server_tls_process_cont(data, *pos, end - *pos) < 0) return -1; - + if (flags & EAP_TLS_FLAGS_MORE_FRAGMENTS) { if (eap_server_tls_process_fragment(data, flags, tls_msg_len, *pos, end - *pos) < 0) diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c index a53633f8f1fe0..b14996b0b9909 100644 --- a/src/eap_server/eap_server_ttls.c +++ b/src/eap_server/eap_server_ttls.c @@ -228,14 +228,13 @@ static int eap_ttls_avp_parse(struct wpabuf *buf, struct eap_ttls_avp *parse) if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) { wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message"); if (parse->eap == NULL) { - parse->eap = os_malloc(dlen); + parse->eap = os_memdup(dpos, dlen); if (parse->eap == NULL) { wpa_printf(MSG_WARNING, "EAP-TTLS: " "failed to allocate memory " "for Phase 2 EAP data"); goto fail; } - os_memcpy(parse->eap, dpos, dlen); parse->eap_len = dlen; } else { u8 *neweap = os_realloc(parse->eap, @@ -372,7 +371,7 @@ static void eap_ttls_reset(struct eap_sm *sm, void *priv) static struct wpabuf * eap_ttls_build_start(struct eap_sm *sm, struct eap_ttls_data *data, u8 id) -{ +{ struct wpabuf *req; req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, 1, @@ -666,11 +665,14 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, } os_free(chal); - if (sm->user->password_hash) - challenge_response(challenge, sm->user->password, nt_response); - else - nt_challenge_response(challenge, sm->user->password, - sm->user->password_len, nt_response); + if ((sm->user->password_hash && + challenge_response(challenge, sm->user->password, nt_response)) || + (!sm->user->password_hash && + nt_challenge_response(challenge, sm->user->password, + sm->user->password_len, nt_response))) { + eap_ttls_state(data, FAILURE); + return; + } if (os_memcmp_const(nt_response, response + 2 + 24, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response"); @@ -1051,12 +1053,11 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, } os_free(sm->identity); - sm->identity = os_malloc(parse.user_name_len); + sm->identity = os_memdup(parse.user_name, parse.user_name_len); if (sm->identity == NULL) { eap_ttls_state(data, FAILURE); goto done; } - os_memcpy(sm->identity, parse.user_name, parse.user_name_len); sm->identity_len = parse.user_name_len; if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1) != 0) { diff --git a/src/eap_server/eap_server_wsc.c b/src/eap_server/eap_server_wsc.c index 7d9d285c39d01..4a5cb980afac7 100644 --- a/src/eap_server/eap_server_wsc.c +++ b/src/eap_server/eap_server_wsc.c @@ -257,7 +257,7 @@ static struct wpabuf * eap_wsc_buildReq(struct eap_sm *sm, void *priv, u8 id) } data->out_used = 0; } - /* pass through */ + /* fall through */ case WAIT_FRAG_ACK: return eap_wsc_build_msg(data, id); case FRAG_ACK: diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h index dc943eb207d74..31f6e72d779a1 100644 --- a/src/eap_server/eap_tls_common.h +++ b/src/eap_server/eap_tls_common.h @@ -50,6 +50,11 @@ struct eap_ssl_data { enum { MSG, FRAG_ACK, WAIT_FRAG_ACK } state; struct wpabuf tmpbuf; + + /** + * tls_v13 - Whether TLS v1.3 or newer is used + */ + int tls_v13; }; @@ -73,7 +78,7 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, int verify_peer, int eap_type); void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, - char *label, size_t len); + const char *label, size_t len); u8 * eap_server_tls_derive_session_id(struct eap_sm *sm, struct eap_ssl_data *data, u8 eap_type, size_t *len); diff --git a/src/eap_server/ikev2.c b/src/eap_server/ikev2.c index 5385cd89246fc..0e9210e67b50c 100644 --- a/src/eap_server/ikev2.c +++ b/src/eap_server/ikev2.c @@ -544,10 +544,9 @@ static int ikev2_process_idr(struct ikev2_initiator_data *data, } os_free(data->IDr); } - data->IDr = os_malloc(idr_len); + data->IDr = os_memdup(idr, idr_len); if (data->IDr == NULL) return -1; - os_memcpy(data->IDr, idr, idr_len); data->IDr_len = idr_len; data->IDr_type = id_type; @@ -1147,10 +1146,9 @@ static struct wpabuf * ikev2_build_sa_auth(struct ikev2_initiator_data *data) return NULL; } else { os_free(data->shared_secret); - data->shared_secret = os_malloc(secret_len); + data->shared_secret = os_memdup(secret, secret_len); if (data->shared_secret == NULL) return NULL; - os_memcpy(data->shared_secret, secret, secret_len); data->shared_secret_len = secret_len; } diff --git a/src/eap_server/tncs.c b/src/eap_server/tncs.c index cfcbd3ed828c6..942a195761ac7 100644 --- a/src/eap_server/tncs.c +++ b/src/eap_server/tncs.c @@ -161,12 +161,10 @@ static TNC_Result TNC_TNCS_ReportMessageTypes( if (imv == NULL) return TNC_RESULT_INVALID_PARAMETER; os_free(imv->supported_types); - imv->supported_types = - os_malloc(typeCount * sizeof(TNC_MessageType)); + imv->supported_types = os_memdup(supportedTypes, + typeCount * sizeof(TNC_MessageType)); if (imv->supported_types == NULL) return TNC_RESULT_FATAL; - os_memcpy(imv->supported_types, supportedTypes, - typeCount * sizeof(TNC_MessageType)); imv->num_supported_types = typeCount; return TNC_RESULT_SUCCESS; diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c index ff673bb2e7858..36074d3e04740 100644 --- a/src/eapol_auth/eapol_auth_sm.c +++ b/src/eapol_auth/eapol_auth_sm.c @@ -848,6 +848,7 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, eap_conf.server_id_len = eapol->conf.server_id_len; eap_conf.erp = eapol->conf.erp; eap_conf.tls_session_lifetime = eapol->conf.tls_session_lifetime; + eap_conf.tls_flags = eapol->conf.tls_flags; sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf); if (sm->eap == NULL) { eapol_auth_free(sm); @@ -1197,30 +1198,27 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, dst->server_id = src->server_id; dst->server_id_len = src->server_id_len; if (src->eap_req_id_text) { - dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len); + dst->eap_req_id_text = os_memdup(src->eap_req_id_text, + src->eap_req_id_text_len); if (dst->eap_req_id_text == NULL) return -1; - os_memcpy(dst->eap_req_id_text, src->eap_req_id_text, - src->eap_req_id_text_len); dst->eap_req_id_text_len = src->eap_req_id_text_len; } else { dst->eap_req_id_text = NULL; dst->eap_req_id_text_len = 0; } if (src->pac_opaque_encr_key) { - dst->pac_opaque_encr_key = os_malloc(16); + dst->pac_opaque_encr_key = os_memdup(src->pac_opaque_encr_key, + 16); if (dst->pac_opaque_encr_key == NULL) goto fail; - os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key, - 16); } else dst->pac_opaque_encr_key = NULL; if (src->eap_fast_a_id) { - dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len); + dst->eap_fast_a_id = os_memdup(src->eap_fast_a_id, + src->eap_fast_a_id_len); if (dst->eap_fast_a_id == NULL) goto fail; - os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id, - src->eap_fast_a_id_len); dst->eap_fast_a_id_len = src->eap_fast_a_id_len; } else dst->eap_fast_a_id = NULL; @@ -1249,6 +1247,7 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, dst->erp_send_reauth_start = src->erp_send_reauth_start; dst->erp = src->erp; dst->tls_session_lifetime = src->tls_session_lifetime; + dst->tls_flags = src->tls_flags; return 0; diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h index e1974e4354dac..44f3f31cc6015 100644 --- a/src/eapol_auth/eapol_auth_sm.h +++ b/src/eapol_auth/eapol_auth_sm.h @@ -28,6 +28,7 @@ struct eapol_auth_config { char *erp_domain; /* a copy of this will be allocated */ int erp; /* Whether ERP is enabled on authentication server */ unsigned int tls_session_lifetime; + unsigned int tls_flags; u8 *pac_opaque_encr_key; u8 *eap_fast_a_id; size_t eap_fast_a_id_len; diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index 65460fc3bec05..9f029b0d3710f 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -16,6 +16,7 @@ #include "crypto/md5.h" #include "common/eapol_common.h" #include "eap_peer/eap.h" +#include "eap_peer/eap_config.h" #include "eap_peer/eap_proxy.h" #include "eapol_supp_sm.h" @@ -95,7 +96,7 @@ struct eapol_sm { SUPP_BE_RECEIVE = 4, SUPP_BE_RESPONSE = 5, SUPP_BE_FAIL = 6, - SUPP_BE_TIMEOUT = 7, + SUPP_BE_TIMEOUT = 7, SUPP_BE_SUCCESS = 8 } SUPP_BE_state; /* dot1xSuppBackendPaeState */ /* Variables */ @@ -250,6 +251,8 @@ SM_STATE(SUPP_PAE, CONNECTING) if (sm->eapTriggerStart) send_start = 1; + if (sm->ctx->preauth) + send_start = 1; sm->eapTriggerStart = FALSE; if (send_start) { @@ -490,9 +493,24 @@ SM_STATE(SUPP_BE, SUCCESS) #ifdef CONFIG_EAP_PROXY if (sm->use_eap_proxy) { if (eap_proxy_key_available(sm->eap_proxy)) { + u8 *session_id, *emsk; + size_t session_id_len, emsk_len; + /* New key received - clear IEEE 802.1X EAPOL-Key replay * counter */ sm->replay_counter_valid = FALSE; + + session_id = eap_proxy_get_eap_session_id( + sm->eap_proxy, &session_id_len); + emsk = eap_proxy_get_emsk(sm->eap_proxy, &emsk_len); + if (sm->config->erp && session_id && emsk) { + eap_peer_erp_init(sm->eap, session_id, + session_id_len, emsk, + emsk_len); + } else { + os_free(session_id); + bin_clear_free(emsk, emsk_len); + } } return; } @@ -897,6 +915,9 @@ static void eapol_sm_abortSupp(struct eapol_sm *sm) wpabuf_free(sm->eapReqData); sm->eapReqData = NULL; eap_sm_abort(sm->eap); +#ifdef CONFIG_EAP_PROXY + eap_proxy_sm_abort(sm->eap_proxy); +#endif /* CONFIG_EAP_PROXY */ } @@ -1998,7 +2019,17 @@ static void eapol_sm_notify_status(void *ctx, const char *status, } +static void eapol_sm_notify_eap_error(void *ctx, int error_code) +{ + struct eapol_sm *sm = ctx; + + if (sm->ctx->eap_error_cb) + sm->ctx->eap_error_cb(sm->ctx->ctx, error_code); +} + + #ifdef CONFIG_EAP_PROXY + static void eapol_sm_eap_proxy_cb(void *ctx) { struct eapol_sm *sm = ctx; @@ -2006,6 +2037,18 @@ static void eapol_sm_eap_proxy_cb(void *ctx) if (sm->ctx->eap_proxy_cb) sm->ctx->eap_proxy_cb(sm->ctx->ctx); } + + +static void +eapol_sm_eap_proxy_notify_sim_status(void *ctx, + enum eap_proxy_sim_state sim_state) +{ + struct eapol_sm *sm = ctx; + + if (sm->ctx->eap_proxy_notify_sim_status) + sm->ctx->eap_proxy_notify_sim_status(sm->ctx->ctx, sim_state); +} + #endif /* CONFIG_EAP_PROXY */ @@ -2032,8 +2075,11 @@ static const struct eapol_callbacks eapol_cb = eapol_sm_eap_param_needed, eapol_sm_notify_cert, eapol_sm_notify_status, + eapol_sm_notify_eap_error, #ifdef CONFIG_EAP_PROXY eapol_sm_eap_proxy_cb, + eapol_sm_eap_proxy_notify_sim_status, + eapol_sm_get_eap_proxy_imsi, #endif /* CONFIG_EAP_PROXY */ eapol_sm_set_anon_id }; @@ -2141,16 +2187,16 @@ int eapol_sm_failed(struct eapol_sm *sm) } -int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len) -{ #ifdef CONFIG_EAP_PROXY +int eapol_sm_get_eap_proxy_imsi(void *ctx, int sim_num, char *imsi, size_t *len) +{ + struct eapol_sm *sm = ctx; + if (sm->eap_proxy == NULL) return -1; - return eap_proxy_get_imsi(sm->eap_proxy, imsi, len); -#else /* CONFIG_EAP_PROXY */ - return -1; -#endif /* CONFIG_EAP_PROXY */ + return eap_proxy_get_imsi(sm->eap_proxy, sim_num, imsi, len); } +#endif /* CONFIG_EAP_PROXY */ void eapol_sm_erp_flush(struct eapol_sm *sm) @@ -2158,3 +2204,56 @@ void eapol_sm_erp_flush(struct eapol_sm *sm) if (sm) eap_peer_erp_free_keys(sm->eap); } + + +struct wpabuf * eapol_sm_build_erp_reauth_start(struct eapol_sm *sm) +{ +#ifdef CONFIG_ERP + if (!sm) + return NULL; + return eap_peer_build_erp_reauth_start(sm->eap, 0); +#else /* CONFIG_ERP */ + return NULL; +#endif /* CONFIG_ERP */ +} + + +void eapol_sm_process_erp_finish(struct eapol_sm *sm, const u8 *buf, + size_t len) +{ +#ifdef CONFIG_ERP + if (!sm) + return; + eap_peer_finish(sm->eap, (const struct eap_hdr *) buf, len); +#endif /* CONFIG_ERP */ +} + + +int eapol_sm_update_erp_next_seq_num(struct eapol_sm *sm, u16 next_seq_num) +{ +#ifdef CONFIG_ERP + if (!sm) + return -1; + return eap_peer_update_erp_next_seq_num(sm->eap, next_seq_num); +#else /* CONFIG_ERP */ + return -1; +#endif /* CONFIG_ERP */ +} + + +int eapol_sm_get_erp_info(struct eapol_sm *sm, struct eap_peer_config *config, + const u8 **username, size_t *username_len, + const u8 **realm, size_t *realm_len, + u16 *erp_next_seq_num, const u8 **rrk, + size_t *rrk_len) +{ +#ifdef CONFIG_ERP + if (!sm) + return -1; + return eap_peer_get_erp_info(sm->eap, config, username, username_len, + realm, realm_len, erp_next_seq_num, rrk, + rrk_len); +#else /* CONFIG_ERP */ + return -1; +#endif /* CONFIG_ERP */ +} diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h index 1309ff7547e8b..74f40bb1cd63c 100644 --- a/src/eapol_supp/eapol_supp_sm.h +++ b/src/eapol_supp/eapol_supp_sm.h @@ -271,12 +271,27 @@ struct eapol_ctx { void (*status_cb)(void *ctx, const char *status, const char *parameter); + /** + * eap_error_cb - Notification of EAP method error + * @ctx: Callback context (ctx) + * @error_code: EAP method error code + */ + void (*eap_error_cb)(void *ctx, int error_code); + #ifdef CONFIG_EAP_PROXY /** * eap_proxy_cb - Callback signifying any updates from eap_proxy * @ctx: eapol_ctx from eap_peer_sm_init() call */ void (*eap_proxy_cb)(void *ctx); + + /** + * eap_proxy_notify_sim_status - Notification of SIM status change + * @ctx: eapol_ctx from eap_peer_sm_init() call + * @status: One of enum value from sim_state + */ + void (*eap_proxy_notify_sim_status)(void *ctx, + enum eap_proxy_sim_state sim_state); #endif /* CONFIG_EAP_PROXY */ /** @@ -328,7 +343,18 @@ void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, struct ext_password_data *ext); int eapol_sm_failed(struct eapol_sm *sm); void eapol_sm_erp_flush(struct eapol_sm *sm); -int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len); +struct wpabuf * eapol_sm_build_erp_reauth_start(struct eapol_sm *sm); +void eapol_sm_process_erp_finish(struct eapol_sm *sm, const u8 *buf, + size_t len); +int eapol_sm_get_eap_proxy_imsi(void *ctx, int sim_num, char *imsi, + size_t *len); +int eapol_sm_update_erp_next_seq_num(struct eapol_sm *sm, u16 next_seq_num); +int eapol_sm_get_erp_info(struct eapol_sm *sm, struct eap_peer_config *config, + const u8 **username, size_t *username_len, + const u8 **realm, size_t *realm_len, + u16 *erp_next_seq_num, const u8 **rrk, + size_t *rrk_len); + #else /* IEEE8021X_EAPOL */ static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) { @@ -438,6 +464,28 @@ static inline int eapol_sm_failed(struct eapol_sm *sm) static inline void eapol_sm_erp_flush(struct eapol_sm *sm) { } +static inline struct wpabuf * +eapol_sm_build_erp_reauth_start(struct eapol_sm *sm) +{ + return NULL; +} +static inline void eapol_sm_process_erp_finish(struct eapol_sm *sm, + const u8 *buf, size_t len) +{ +} +static inline int eapol_sm_update_erp_next_seq_num(struct eapol_sm *sm, + u16 next_seq_num) +{ + return -1; +} +static inline int +eapol_sm_get_erp_info(struct eapol_sm *sm, struct eap_peer_config *config, + const u8 **username, size_t *username_len, + const u8 **realm, size_t *realm_len, + u16 *erp_next_seq_num, const u8 **rrk, size_t *rrk_len) +{ + return -1; +} #endif /* IEEE8021X_EAPOL */ #endif /* EAPOL_SUPP_SM_H */ diff --git a/src/fst/fst_ctrl_aux.h b/src/fst/fst_ctrl_aux.h index e2133f5062bd1..0aff5d061eae3 100644 --- a/src/fst/fst_ctrl_aux.h +++ b/src/fst/fst_ctrl_aux.h @@ -35,6 +35,17 @@ enum fst_event_type { * more info */ }; +enum fst_reason { + REASON_TEARDOWN, + REASON_SETUP, + REASON_SWITCH, + REASON_STT, + REASON_REJECT, + REASON_ERROR_PARAMS, + REASON_RESET, + REASON_DETACH_IFACE, +}; + enum fst_initiator { FST_INITIATOR_UNDEFINED, FST_INITIATOR_LOCAL, @@ -57,16 +68,7 @@ union fst_event_extra { enum fst_session_state new_state; union fst_session_state_switch_extra { struct { - enum fst_reason { - REASON_TEARDOWN, - REASON_SETUP, - REASON_SWITCH, - REASON_STT, - REASON_REJECT, - REASON_ERROR_PARAMS, - REASON_RESET, - REASON_DETACH_IFACE, - } reason; + enum fst_reason reason; u8 reject_code; /* REASON_REJECT */ /* REASON_SWITCH, * REASON_TEARDOWN, diff --git a/src/fst/fst_ctrl_iface.c b/src/fst/fst_ctrl_iface.c index 7820e586629f2..7df3362b6e938 100644 --- a/src/fst/fst_ctrl_iface.c +++ b/src/fst/fst_ctrl_iface.c @@ -49,7 +49,7 @@ static Boolean format_session_state_extra(const union fst_event_extra *extra, if (ss->extra.to_initial.reject_code != WLAN_STATUS_SUCCESS) os_snprintf(reject_str, sizeof(reject_str), "%u", ss->extra.to_initial.reject_code); - /* no break */ + /* fall through */ case REASON_TEARDOWN: case REASON_SWITCH: switch (ss->extra.to_initial.initiator) { diff --git a/src/fst/fst_group.c b/src/fst/fst_group.c index 321d40d50cd23..a4ae016d91635 100644 --- a/src/fst/fst_group.c +++ b/src/fst/fst_group.c @@ -29,7 +29,7 @@ static void fst_dump_mb_ies(const char *group_id, const char *ifname, const struct multi_band_ie *mbie = (const struct multi_band_ie *) p; WPA_ASSERT(mbie->eid == WLAN_EID_MULTI_BAND); - WPA_ASSERT(2 + mbie->len >= sizeof(*mbie)); + WPA_ASSERT(2U + mbie->len >= sizeof(*mbie)); fst_printf(MSG_WARNING, "%s: %s: mb_ctrl=%u band_id=%u op_class=%u chan=%u bssid=" diff --git a/src/fst/fst_iface.h b/src/fst/fst_iface.h index 0eb27325a2b88..cbaa7d81788d9 100644 --- a/src/fst/fst_iface.h +++ b/src/fst/fst_iface.h @@ -106,7 +106,7 @@ static inline void fst_iface_update_mb_ie(struct fst_iface *i, const u8 *addr, const u8 *buf, size_t size) { - return i->iface_obj.update_mb_ie(i->iface_obj.ctx, addr, buf, size); + i->iface_obj.update_mb_ie(i->iface_obj.ctx, addr, buf, size); } static inline const u8 * fst_iface_get_peer_first(struct fst_iface *i, diff --git a/src/fst/fst_session.c b/src/fst/fst_session.c index 76e2c78f4ff6c..a02a93e76e433 100644 --- a/src/fst/fst_session.c +++ b/src/fst/fst_session.c @@ -756,8 +756,6 @@ struct fst_session * fst_session_create(struct fst_group *g) struct fst_session *s; u32 id; - WPA_ASSERT(!is_zero_ether_addr(own_addr)); - id = fst_find_free_session_id(); if (id == FST_INVALID_SESSION_ID) { fst_printf(MSG_ERROR, "Cannot assign new session ID"); diff --git a/src/l2_packet/l2_packet.h b/src/l2_packet/l2_packet.h index 2a452458214be..53871774b4e75 100644 --- a/src/l2_packet/l2_packet.h +++ b/src/l2_packet/l2_packet.h @@ -42,6 +42,7 @@ struct l2_ethhdr { enum l2_packet_filter_type { L2_PACKET_FILTER_DHCP, L2_PACKET_FILTER_NDISC, + L2_PACKET_FILTER_PKTTYPE, }; /** diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c index a7a300e568e6d..291c9dd263a6e 100644 --- a/src/l2_packet/l2_packet_linux.c +++ b/src/l2_packet/l2_packet_linux.c @@ -84,6 +84,26 @@ static const struct sock_fprog ndisc_sock_filter = { .filter = ndisc_sock_filter_insns, }; +/* drop packet if skb->pkt_type is PACKET_OTHERHOST (0x03). Generated by: + * $ bpfc - <<EOF + * > ldb #type + * > jeq #0x03, drop + * > pass: ret #-1 + * > drop: ret #0 + * > EOF + */ +static struct sock_filter pkt_type_filter_insns[] = { + { 0x30, 0, 0, 0xfffff004 }, + { 0x15, 1, 0, 0x00000003 }, + { 0x6, 0, 0, 0xffffffff }, + { 0x6, 0, 0, 0x00000000 }, +}; + +static const struct sock_fprog pkt_type_sock_filter = { + .len = ARRAY_SIZE(pkt_type_filter_insns), + .filter = pkt_type_filter_insns, +}; + int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) { @@ -96,6 +116,9 @@ int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, const u8 *buf, size_t len) { int ret; + + if (TEST_FAIL()) + return -1; if (l2 == NULL) return -1; if (l2->l2_hdr) { @@ -458,6 +481,9 @@ int l2_packet_set_packet_filter(struct l2_packet_data *l2, { const struct sock_fprog *sock_filter; + if (TEST_FAIL()) + return -1; + switch (type) { case L2_PACKET_FILTER_DHCP: sock_filter = &dhcp_sock_filter; @@ -465,6 +491,9 @@ int l2_packet_set_packet_filter(struct l2_packet_data *l2, case L2_PACKET_FILTER_NDISC: sock_filter = &ndisc_sock_filter; break; + case L2_PACKET_FILTER_PKTTYPE: + sock_filter = &pkt_type_sock_filter; + break; default: return -1; } diff --git a/src/l2_packet/l2_packet_privsep.c b/src/l2_packet/l2_packet_privsep.c index e26ca20a8625d..ce86802c23533 100644 --- a/src/l2_packet/l2_packet_privsep.c +++ b/src/l2_packet/l2_packet_privsep.c @@ -51,7 +51,7 @@ static int wpa_priv_cmd(struct l2_packet_data *l2, int cmd, return 0; } - + int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) { os_memcpy(addr, l2->own_addr, ETH_ALEN); @@ -258,7 +258,7 @@ void l2_packet_deinit(struct l2_packet_data *l2) unlink(l2->own_socket_path); os_free(l2->own_socket_path); } - + os_free(l2); } diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c index 996b4e8249863..b4660c4f9616e 100644 --- a/src/p2p/p2p.c +++ b/src/p2p/p2p.c @@ -711,6 +711,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, struct p2p_message msg; const u8 *p2p_dev_addr; int wfd_changed; + int dev_name_changed; int i; struct os_reltime time_now; @@ -821,6 +822,9 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, } dev->info.level = level; + dev_name_changed = os_strncmp(dev->info.device_name, msg.device_name, + WPS_DEV_NAME_MAX_LEN) != 0; + p2p_copy_wps_info(p2p, dev, 0, &msg); for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) { @@ -839,9 +843,12 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, wfd_changed = p2p_compare_wfd_info(dev, &msg); - if (msg.wfd_subelems) { + if (wfd_changed) { wpabuf_free(dev->info.wfd_subelems); - dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + if (msg.wfd_subelems) + dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems); + else + dev->info.wfd_subelems = NULL; } if (scan_res) { @@ -855,6 +862,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq, p2p_update_peer_vendor_elems(dev, ies, ies_len); if (dev->flags & P2P_DEV_REPORTED && !wfd_changed && + !dev_name_changed && (!msg.adv_service_instance || (dev->flags & P2P_DEV_P2PS_REPORTED))) return 0; @@ -1002,8 +1010,16 @@ static void p2p_search(struct p2p_data *p2p) } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); - if (p2p->find_type == P2P_FIND_PROGRESSIVE && - (freq = p2p_get_next_prog_freq(p2p)) > 0) { + if (p2p->find_pending_full && + (p2p->find_type == P2P_FIND_PROGRESSIVE || + p2p->find_type == P2P_FIND_START_WITH_FULL)) { + type = P2P_SCAN_FULL; + p2p_dbg(p2p, "Starting search (pending full scan)"); + p2p->find_pending_full = 0; + } else if ((p2p->find_type == P2P_FIND_PROGRESSIVE && + (freq = p2p_get_next_prog_freq(p2p)) > 0) || + (p2p->find_type == P2P_FIND_START_WITH_FULL && + (freq = p2p->find_specified_freq) > 0)) { type = P2P_SCAN_SOCIAL_PLUS_ONE; p2p_dbg(p2p, "Starting search (+ freq %u)", freq); } else { @@ -1165,12 +1181,11 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, p2p_free_req_dev_types(p2p); if (req_dev_types && num_req_dev_types) { - p2p->req_dev_types = os_malloc(num_req_dev_types * + p2p->req_dev_types = os_memdup(req_dev_types, + num_req_dev_types * WPS_DEV_TYPE_LEN); if (p2p->req_dev_types == NULL) return -1; - os_memcpy(p2p->req_dev_types, req_dev_types, - num_req_dev_types * WPS_DEV_TYPE_LEN); p2p->num_req_dev_types = num_req_dev_types; } @@ -1227,7 +1242,12 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, p2p->pending_listen_freq = 0; } p2p->cfg->stop_listen(p2p->cfg->cb_ctx); + p2p->find_pending_full = 0; p2p->find_type = type; + if (freq != 2412 && freq != 2437 && freq != 2462 && freq != 60480) + p2p->find_specified_freq = freq; + else + p2p->find_specified_freq = 0; p2p_device_clear_reported(p2p); os_memset(p2p->sd_query_no_ack, 0, ETH_ALEN); p2p_set_state(p2p, P2P_SEARCH); @@ -1243,7 +1263,7 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, if (freq > 0) { /* * Start with the specified channel and then move to - * social channels only scans. + * scans for social channels and this specific channel. */ res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_SPECIFIC, freq, @@ -1272,6 +1292,9 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout, if (res != 0 && p2p->p2p_scan_running) { p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running"); /* wait for the previous p2p_scan to complete */ + if (type == P2P_FIND_PROGRESSIVE || + (type == P2P_FIND_START_WITH_FULL && freq == 0)) + p2p->find_pending_full = 1; res = 0; /* do not report failure */ } else if (res != 0) { p2p_dbg(p2p, "Failed to start p2p_scan"); @@ -1833,6 +1856,7 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer) p2p_clear_timeout(p2p); p2p->ssid_set = 0; peer->go_neg_req_sent = 0; + peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE; peer->wps_method = WPS_NOT_READY; peer->oob_pw_id = 0; wpabuf_free(peer->go_neg_conf); @@ -2822,6 +2846,7 @@ void p2p_service_flush_asp(struct p2p_data *p2p) } p2p->p2ps_adv_list = NULL; + p2ps_prov_free(p2p); p2p_dbg(p2p, "All ASP advertisements flushed"); } @@ -2983,6 +3008,7 @@ void p2p_deinit(struct p2p_data *p2p) wpabuf_free(p2p->wfd_dev_info); wpabuf_free(p2p->wfd_assoc_bssid); wpabuf_free(p2p->wfd_coupled_sink_info); + wpabuf_free(p2p->wfd_r2_dev_info); #endif /* CONFIG_WIFI_DISPLAY */ eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL); @@ -3022,6 +3048,10 @@ void p2p_flush(struct p2p_data *p2p) os_free(p2p->after_scan_tx); p2p->after_scan_tx = NULL; p2p->ssid_set = 0; + p2ps_prov_free(p2p); + p2p_reset_pending_pd(p2p); + p2p->override_pref_op_class = 0; + p2p->override_pref_channel = 0; } @@ -3859,6 +3889,19 @@ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq) if (p2p->in_listen) return 0; /* Internal timeout will trigger the next step */ + if (p2p->state == P2P_WAIT_PEER_CONNECT && p2p->go_neg_peer && + p2p->pending_listen_freq) { + /* + * Better wait a bit if the driver is unable to start + * offchannel operation for some reason to continue with + * P2P_WAIT_PEER_(IDLE/CONNECT) state transitions. + */ + p2p_dbg(p2p, + "Listen operation did not seem to start - delay idle phase to avoid busy loop"); + p2p_set_timeout(p2p, 0, 100000); + return 1; + } + if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) { if (p2p->go_neg_peer->connect_reqs >= 120) { p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response"); @@ -4803,11 +4846,10 @@ int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan, struct p2p_channel *n; if (pref_chan) { - n = os_malloc(num_pref_chan * sizeof(struct p2p_channel)); + n = os_memdup(pref_chan, + num_pref_chan * sizeof(struct p2p_channel)); if (n == NULL) return -1; - os_memcpy(n, pref_chan, - num_pref_chan * sizeof(struct p2p_channel)); } else n = NULL; @@ -5129,6 +5171,20 @@ int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem) } +int p2p_set_wfd_r2_dev_info(struct p2p_data *p2p, const struct wpabuf *elem) +{ + wpabuf_free(p2p->wfd_r2_dev_info); + if (elem) { + p2p->wfd_r2_dev_info = wpabuf_dup(elem); + if (p2p->wfd_r2_dev_info == NULL) + return -1; + } else + p2p->wfd_r2_dev_info = NULL; + + return 0; +} + + int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem) { wpabuf_free(p2p->wfd_assoc_bssid); @@ -5510,6 +5566,14 @@ void p2p_set_own_pref_freq_list(struct p2p_data *p2p, } +void p2p_set_override_pref_op_chan(struct p2p_data *p2p, u8 op_class, + u8 chan) +{ + p2p->override_pref_op_class = op_class; + p2p->override_pref_channel = chan; +} + + struct wpabuf * p2p_build_probe_resp_template(struct p2p_data *p2p, unsigned int freq) { diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h index 7b18dcfc3ff3a..fac5ce05ae6e1 100644 --- a/src/p2p/p2p.h +++ b/src/p2p/p2p.h @@ -2266,6 +2266,7 @@ int p2p_set_wfd_ie_prov_disc_req(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_prov_disc_resp(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_ie_go_neg(struct p2p_data *p2p, struct wpabuf *ie); int p2p_set_wfd_dev_info(struct p2p_data *p2p, const struct wpabuf *elem); +int p2p_set_wfd_r2_dev_info(struct p2p_data *p2p, const struct wpabuf *elem); int p2p_set_wfd_assoc_bssid(struct p2p_data *p2p, const struct wpabuf *elem); int p2p_set_wfd_coupled_sink_info(struct p2p_data *p2p, const struct wpabuf *elem); @@ -2373,6 +2374,8 @@ void p2p_expire_peers(struct p2p_data *p2p); void p2p_set_own_pref_freq_list(struct p2p_data *p2p, const unsigned int *pref_freq_list, unsigned int size); +void p2p_set_override_pref_op_chan(struct p2p_data *p2p, u8 op_class, + u8 chan); /** * p2p_group_get_common_freqs - Get the group common frequencies diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c index 9f0b3f3d37a44..65ab4b8d3fd60 100644 --- a/src/p2p/p2p_go_neg.c +++ b/src/p2p/p2p_go_neg.c @@ -315,7 +315,12 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p, group_capab); p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker); p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout); - if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) { + if (p2p->override_pref_op_class) { + p2p_dbg(p2p, "Override operating channel preference"); + p2p_buf_add_operating_channel(buf, p2p->cfg->country, + p2p->override_pref_op_class, + p2p->override_pref_channel); + } else if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) { p2p_dbg(p2p, "Omit Operating Channel attribute"); } else { p2p_buf_add_operating_channel(buf, p2p->cfg->country, @@ -562,26 +567,11 @@ static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go, * also supported by the peer device. */ for (i = 0; i < size && !found; i++) { - /* - * Make sure that the common frequency is: - * 1. Supported by peer - * 2. Allowed for P2P use. - */ + /* Make sure that the common frequency is supported by peer. */ oper_freq = freq_list[i]; if (p2p_freq_to_channel(oper_freq, &op_class, - &op_channel) < 0) { - p2p_dbg(p2p, "Unsupported frequency %u MHz", oper_freq); - continue; - } - if (!p2p_channels_includes(&p2p->cfg->channels, - op_class, op_channel) && - (go || !p2p_channels_includes(&p2p->cfg->cli_channels, - op_class, op_channel))) { - p2p_dbg(p2p, - "Freq %u MHz (oper_class %u channel %u) not allowed for P2P", - oper_freq, op_class, op_channel); - break; - } + &op_channel) < 0) + continue; /* cannot happen due to earlier check */ for (j = 0; j < msg->channel_list_len; j++) { if (op_channel != msg->channel_list[j]) @@ -602,8 +592,7 @@ static void p2p_check_pref_chan_no_recv(struct p2p_data *p2p, int go, oper_freq); } else { p2p_dbg(p2p, - "None of our preferred channels are supported by peer!. Use: %d MHz for oper_channel", - dev->oper_freq); + "None of our preferred channels are supported by peer!"); } } @@ -629,29 +618,9 @@ static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go, msg->pref_freq_list[2 * j + 1]); if (freq_list[i] != oper_freq) continue; - - /* - * Make sure that the found frequency is: - * 1. Supported - * 2. Allowed for P2P use. - */ if (p2p_freq_to_channel(oper_freq, &op_class, - &op_channel) < 0) { - p2p_dbg(p2p, "Unsupported frequency %u MHz", - oper_freq); - continue; - } - - if (!p2p_channels_includes(&p2p->cfg->channels, - op_class, op_channel) && - (go || - !p2p_channels_includes(&p2p->cfg->cli_channels, - op_class, op_channel))) { - p2p_dbg(p2p, - "Freq %u MHz (oper_class %u channel %u) not allowed for P2P", - oper_freq, op_class, op_channel); - break; - } + &op_channel) < 0) + continue; /* cannot happen */ p2p->op_reg_class = op_class; p2p->op_channel = op_channel; os_memcpy(&p2p->channels, &p2p->cfg->channels, @@ -666,9 +635,7 @@ static void p2p_check_pref_chan_recv(struct p2p_data *p2p, int go, "Freq %d MHz is a common preferred channel for both peer and local, use it as operating channel", oper_freq); } else { - p2p_dbg(p2p, - "No common preferred channels found! Use: %d MHz for oper_channel", - dev->oper_freq); + p2p_dbg(p2p, "No common preferred channels found!"); } } @@ -679,6 +646,8 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go, unsigned int freq_list[P2P_MAX_PREF_CHANNELS], size; unsigned int i; u8 op_class, op_channel; + char txt[100], *pos, *end; + int res; /* * Use the preferred channel list from the driver only if there is no @@ -694,6 +663,39 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go, if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go, &size, freq_list)) return; + /* Filter out frequencies that are not acceptable for P2P use */ + i = 0; + while (i < size) { + if (p2p_freq_to_channel(freq_list[i], &op_class, + &op_channel) < 0 || + (!p2p_channels_includes(&p2p->cfg->channels, + op_class, op_channel) && + (go || !p2p_channels_includes(&p2p->cfg->cli_channels, + op_class, op_channel)))) { + p2p_dbg(p2p, + "Ignore local driver frequency preference %u MHz since it is not acceptable for P2P use (go=%d)", + freq_list[i], go); + if (size - i - 1 > 0) + os_memmove(&freq_list[i], &freq_list[i + 1], size - i - 1); + size--; + continue; + } + + /* Preferred frequency is acceptable for P2P use */ + i++; + } + + pos = txt; + end = pos + sizeof(txt); + for (i = 0; i < size; i++) { + res = os_snprintf(pos, end - pos, " %u", freq_list[i]); + if (os_snprintf_error(end - pos, res)) + break; + pos += res; + } + *pos = '\0'; + p2p_dbg(p2p, "Local driver frequency preference (size=%u):%s", + size, txt); /* * Check if peer's preference of operating channel is in @@ -703,20 +705,14 @@ void p2p_check_pref_chan(struct p2p_data *p2p, int go, if (freq_list[i] == (unsigned int) dev->oper_freq) break; } - if (i != size) { + if (i != size && + p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) == 0) { /* Peer operating channel preference matches our preference */ - if (p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) < - 0) { - p2p_dbg(p2p, - "Peer operating channel preference is unsupported frequency %u MHz", - freq_list[i]); - } else { - p2p->op_reg_class = op_class; - p2p->op_channel = op_channel; - os_memcpy(&p2p->channels, &p2p->cfg->channels, - sizeof(struct p2p_channels)); - return; - } + p2p->op_reg_class = op_class; + p2p->op_channel = op_channel; + os_memcpy(&p2p->channels, &p2p->cfg->channels, + sizeof(struct p2p_channels)); + return; } p2p_dbg(p2p, diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c index 051b4e391505e..16c28a0d95ee7 100644 --- a/src/p2p/p2p_group.c +++ b/src/p2p/p2p_group.c @@ -367,6 +367,8 @@ wifi_display_build_go_ie(struct p2p_group *group) return NULL; if (group->p2p->wfd_dev_info) wpabuf_put_buf(wfd_subelems, group->p2p->wfd_dev_info); + if (group->p2p->wfd_r2_dev_info) + wpabuf_put_buf(wfd_subelems, group->p2p->wfd_r2_dev_info); if (group->p2p->wfd_assoc_bssid) wpabuf_put_buf(wfd_subelems, group->p2p->wfd_assoc_bssid); diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h index 47524d4991a5a..6a4d751c090ab 100644 --- a/src/p2p/p2p_i.h +++ b/src/p2p/p2p_i.h @@ -437,9 +437,11 @@ struct p2p_data { int inv_persistent; enum p2p_discovery_type find_type; + int find_specified_freq; unsigned int last_p2p_find_timeout; u8 last_prog_scan_class; u8 last_prog_scan_chan; + unsigned int find_pending_full:1; int p2p_scan_running; enum p2p_after_scan { P2P_AFTER_SCAN_NOTHING, @@ -545,6 +547,7 @@ struct p2p_data { struct wpabuf *wfd_dev_info; struct wpabuf *wfd_assoc_bssid; struct wpabuf *wfd_coupled_sink_info; + struct wpabuf *wfd_r2_dev_info; #endif /* CONFIG_WIFI_DISPLAY */ u16 authorized_oob_dev_pw_id; @@ -553,6 +556,10 @@ struct p2p_data { unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS]; unsigned int num_pref_freq; + + /* Override option for preferred operating channel in GO Negotiation */ + u8 override_pref_op_class; + u8 override_pref_channel; }; /** diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c index 93a0535f873a0..3994ec03f86ba 100644 --- a/src/p2p/p2p_pd.c +++ b/src/p2p/p2p_pd.c @@ -1163,6 +1163,9 @@ out: msg.group_id, msg.group_id_len); } + if (reject != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) + p2ps_prov_free(p2p); + if (reject == P2P_SC_SUCCESS) { switch (config_methods) { case WPS_CONFIG_DISPLAY: @@ -1581,7 +1584,7 @@ out: report_config_methods); if (p2p->state == P2P_PD_DURING_FIND) { - p2p_clear_timeout(p2p); + p2p_stop_listen_for_freq(p2p, 0); p2p_continue_find(p2p); } } diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c index a8bc5ba7f344c..b9e753f205168 100644 --- a/src/p2p/p2p_sd.c +++ b/src/p2p/p2p_sd.c @@ -426,6 +426,7 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, { struct wpabuf *resp; size_t max_len; + unsigned int wait_time = 200; /* * In the 60 GHz, we have a smaller maximum frame length for management @@ -460,6 +461,7 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, 1, p2p->srv_update_indic, NULL); } else { p2p_dbg(p2p, "SD response fits in initial response"); + wait_time = 0; /* no more SD frames in the sequence */ resp = p2p_build_sd_response(dialog_token, WLAN_STATUS_SUCCESS, 0, p2p->srv_update_indic, resp_tlvs); @@ -470,7 +472,7 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst, p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr, p2p->cfg->dev_addr, - wpabuf_head(resp), wpabuf_len(resp), 200) < 0) + wpabuf_head(resp), wpabuf_len(resp), wait_time) < 0) p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(resp); @@ -623,6 +625,7 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, u8 dialog_token; size_t frag_len, max_len; int more = 0; + unsigned int wait_time = 200; wpa_hexdump(MSG_DEBUG, "P2P: RX GAS Comeback Request", data, len); if (len < 1) @@ -675,12 +678,13 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa, p2p_dbg(p2p, "All fragments of SD response sent"); wpabuf_free(p2p->sd_resp); p2p->sd_resp = NULL; + wait_time = 0; /* no more SD frames in the sequence */ } p2p->pending_action_state = P2P_NO_PENDING_ACTION; if (p2p_send_action(p2p, rx_freq, sa, p2p->cfg->dev_addr, p2p->cfg->dev_addr, - wpabuf_head(resp), wpabuf_len(resp), 200) < 0) + wpabuf_head(resp), wpabuf_len(resp), wait_time) < 0) p2p_dbg(p2p, "Failed to send Action frame"); wpabuf_free(resp); diff --git a/src/pae/ieee802_1x_cp.c b/src/pae/ieee802_1x_cp.c index e294e64662852..360fcd3f5fcdf 100644 --- a/src/pae/ieee802_1x_cp.c +++ b/src/pae/ieee802_1x_cp.c @@ -159,6 +159,7 @@ SM_STATE(CP, ALLOWED) secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled); secy_cp_control_protect_frames(sm->kay, sm->protect_frames); + secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt); secy_cp_control_validate_frames(sm->kay, sm->validate_frames); secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window); } @@ -177,6 +178,7 @@ SM_STATE(CP, AUTHENTICATED) secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled); secy_cp_control_protect_frames(sm->kay, sm->protect_frames); + secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt); secy_cp_control_validate_frames(sm->kay, sm->validate_frames); secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window); } @@ -203,6 +205,7 @@ SM_STATE(CP, SECURED) secy_cp_control_confidentiality_offset(sm->kay, sm->confidentiality_offset); secy_cp_control_protect_frames(sm->kay, sm->protect_frames); + secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt); secy_cp_control_validate_frames(sm->kay, sm->validate_frames); secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window); } @@ -466,6 +469,7 @@ struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay) wpa_printf(MSG_DEBUG, "CP: state machine created"); secy_cp_control_protect_frames(sm->kay, sm->protect_frames); + secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt); secy_cp_control_validate_frames(sm->kay, sm->validate_frames); secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window); secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled); diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c index a8e7efc9b3bda..cda23fcab41aa 100644 --- a/src/pae/ieee802_1x_kay.c +++ b/src/pae/ieee802_1x_kay.c @@ -45,6 +45,14 @@ static struct macsec_ciphersuite cipher_suite_tbl[] = { .sak_len = DEFAULT_SA_KEY_LEN, .index = 0, }, + /* GCM-AES-256 */ + { + .id = CS_ID_GCM_AES_256, + .name = CS_NAME_GCM_AES_256, + .capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50, + .sak_len = 32, + .index = 1 /* index */ + }, }; #define CS_TABLE_SIZE (ARRAY_SIZE(cipher_suite_tbl)) #define DEFAULT_CS_INDEX 0 @@ -245,13 +253,15 @@ ieee802_1x_mka_dump_sak_use_body(struct ieee802_1x_mka_sak_use_body *body) * ieee802_1x_kay_get_participant - */ static struct ieee802_1x_mka_participant * -ieee802_1x_kay_get_participant(struct ieee802_1x_kay *kay, const u8 *ckn) +ieee802_1x_kay_get_participant(struct ieee802_1x_kay *kay, const u8 *ckn, + size_t len) { struct ieee802_1x_mka_participant *participant; dl_list_for_each(participant, &kay->participant_list, struct ieee802_1x_mka_participant, list) { - if (os_memcmp(participant->ckn.name, ckn, + if (participant->ckn.len == len && + os_memcmp(participant->ckn.name, ckn, participant->ckn.len) == 0) return participant; } @@ -379,6 +389,17 @@ ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant, } +u64 mka_sci_u64(struct ieee802_1x_mka_sci *sci) +{ + struct ieee802_1x_mka_sci tmp; + + os_memcpy(tmp.addr, sci->addr, ETH_ALEN); + tmp.port = sci->port; + + return *((u64 *) &tmp); +} + + static Boolean sci_equal(const struct ieee802_1x_mka_sci *a, const struct ieee802_1x_mka_sci *b) { @@ -411,6 +432,8 @@ ieee802_1x_kay_get_peer_sci(struct ieee802_1x_mka_participant *participant, } +static void ieee802_1x_kay_use_data_key(struct data_key *pkey); + /** * ieee802_1x_kay_init_receive_sa - */ @@ -429,6 +452,7 @@ ieee802_1x_kay_init_receive_sa(struct receive_sc *psc, u8 an, u32 lowest_pn, return NULL; } + ieee802_1x_kay_use_data_key(key); psa->pkey = key; psa->lowest_pn = lowest_pn; psa->next_pn = lowest_pn; @@ -440,18 +464,21 @@ ieee802_1x_kay_init_receive_sa(struct receive_sc *psc, u8 an, u32 lowest_pn, dl_list_add(&psc->sa_list, &psa->list); wpa_printf(MSG_DEBUG, - "KaY: Create receive SA(AN: %hhu lowest_pn: %u of SC(channel: %d)", - an, lowest_pn, psc->channel); + "KaY: Create receive SA(AN: %hhu lowest_pn: %u of SC", + an, lowest_pn); return psa; } +static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey); + /** * ieee802_1x_kay_deinit_receive_sa - */ static void ieee802_1x_kay_deinit_receive_sa(struct receive_sa *psa) { + ieee802_1x_kay_deinit_data_key(psa->pkey); psa->pkey = NULL; wpa_printf(MSG_DEBUG, "KaY: Delete receive SA(an: %hhu) of SC", @@ -465,8 +492,7 @@ static void ieee802_1x_kay_deinit_receive_sa(struct receive_sa *psa) * ieee802_1x_kay_init_receive_sc - */ static struct receive_sc * -ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci, - int channel) +ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci) { struct receive_sc *psc; @@ -480,19 +506,27 @@ ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci, } os_memcpy(&psc->sci, psci, sizeof(psc->sci)); - psc->channel = channel; os_get_time(&psc->created_time); psc->receiving = FALSE; dl_list_init(&psc->sa_list); - wpa_printf(MSG_DEBUG, "KaY: Create receive SC(channel: %d)", channel); + wpa_printf(MSG_DEBUG, "KaY: Create receive SC"); wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)psci, sizeof(*psci)); return psc; } +static void ieee802_1x_delete_receive_sa(struct ieee802_1x_kay *kay, + struct receive_sa *sa) +{ + secy_disable_receive_sa(kay, sa); + secy_delete_receive_sa(kay, sa); + ieee802_1x_kay_deinit_receive_sa(sa); +} + + /** * ieee802_1x_kay_deinit_receive_sc - **/ @@ -502,14 +536,13 @@ ieee802_1x_kay_deinit_receive_sc( { struct receive_sa *psa, *pre_sa; - wpa_printf(MSG_DEBUG, "KaY: Delete receive SC(channel: %d)", - psc->channel); + wpa_printf(MSG_DEBUG, "KaY: Delete receive SC"); dl_list_for_each_safe(psa, pre_sa, &psc->sa_list, struct receive_sa, - list) { - secy_disable_receive_sa(participant->kay, psa); - ieee802_1x_kay_deinit_receive_sa(psa); - } + list) + ieee802_1x_delete_receive_sa(participant->kay, psa); + dl_list_del(&psc->list); + secy_delete_receive_sc(participant->kay, psc); os_free(psc); } @@ -552,7 +585,6 @@ ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant, { struct ieee802_1x_kay_peer *peer; struct receive_sc *rxsc; - u32 sc_ch = 0; peer = ieee802_1x_kay_create_peer(mi, mn); if (!peer) @@ -561,9 +593,7 @@ ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant, os_memcpy(&peer->sci, &participant->current_peer_sci, sizeof(peer->sci)); - secy_get_available_receive_sc(participant->kay, &sc_ch); - - rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci, sc_ch); + rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci); if (!rxsc) { os_free(peer); return NULL; @@ -611,12 +641,12 @@ ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant, { struct ieee802_1x_kay_peer *peer; struct receive_sc *rxsc; - u32 sc_ch = 0; peer = ieee802_1x_kay_get_potential_peer(participant, mi); + if (!peer) + return NULL; - rxsc = ieee802_1x_kay_init_receive_sc(&participant->current_peer_sci, - sc_ch); + rxsc = ieee802_1x_kay_init_receive_sc(&participant->current_peer_sci); if (!rxsc) return NULL; @@ -631,8 +661,6 @@ ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant, dl_list_del(&peer->list); dl_list_add_tail(&participant->live_peers, &peer->list); - secy_get_available_receive_sc(participant->kay, &sc_ch); - dl_list_add(&participant->rxsc_list, &rxsc->list); secy_create_receive_sc(participant->kay, rxsc); @@ -730,6 +758,8 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, struct ieee802_1x_mka_participant *participant; const struct ieee802_1x_mka_basic_body *body; struct ieee802_1x_kay_peer *peer; + size_t ckn_len; + size_t body_len; body = (const struct ieee802_1x_mka_basic_body *) mka_msg; @@ -743,7 +773,15 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg, return NULL; } - participant = ieee802_1x_kay_get_participant(kay, body->ckn); + body_len = get_mka_param_body_len(body); + if (body_len < sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN) { + wpa_printf(MSG_DEBUG, "KaY: Too small body length %zu", + body_len); + return NULL; + } + ckn_len = body_len - + (sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN); + participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len); if (!participant) { wpa_printf(MSG_DEBUG, "Peer is not included in my CA"); return NULL; @@ -946,21 +984,19 @@ ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant, body_len = get_mka_param_body_len(hdr); body_type = get_mka_param_body_type(hdr); - if (body_type != MKA_LIVE_PEER_LIST && - body_type != MKA_POTENTIAL_PEER_LIST) - continue; - - ieee802_1x_mka_dump_peer_body( - (struct ieee802_1x_mka_peer_body *)pos); - - if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) { + if (left_len < (MKA_HDR_LEN + MKA_ALIGN_LENGTH(body_len) + DEFAULT_ICV_LEN)) { wpa_printf(MSG_ERROR, "KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV", left_len, MKA_HDR_LEN, - body_len, DEFAULT_ICV_LEN); - continue; + MKA_ALIGN_LENGTH(body_len), + DEFAULT_ICV_LEN); + return FALSE; } + if (body_type != MKA_LIVE_PEER_LIST && + body_type != MKA_POTENTIAL_PEER_LIST) + continue; + if ((body_len % 16) != 0) { wpa_printf(MSG_ERROR, "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets", @@ -968,6 +1004,9 @@ ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant, continue; } + ieee802_1x_mka_dump_peer_body( + (struct ieee802_1x_mka_peer_body *)pos); + for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) { const struct ieee802_1x_mka_peer_id *peer_mi; @@ -1312,8 +1351,8 @@ ieee802_1x_mka_decode_sak_use_body( } } - /* check old key is valid */ - if (body->otx || body->orx) { + /* check old key is valid (but only if we remember our old key) */ + if (participant->oki.kn != 0 && (body->otx || body->orx)) { if (os_memcmp(participant->oki.mi, body->osrv_mi, sizeof(participant->oki.mi)) != 0 || be_to_host32(body->okn) != participant->oki.kn || @@ -1544,7 +1583,7 @@ ieee802_1x_mka_decode_dist_sak_body( ieee802_1x_cp_connect_authenticated(kay->cp); ieee802_1x_cp_sm_step(kay->cp); wpa_printf(MSG_WARNING, "KaY:The Key server advise no MACsec"); - participant->to_use_sak = TRUE; + participant->to_use_sak = FALSE; return 0; } @@ -1595,7 +1634,8 @@ ieee802_1x_mka_decode_dist_sak_body( os_free(unwrap_sak); return -1; } - wpa_hexdump(MSG_DEBUG, "\tAES Key Unwrap of SAK:", unwrap_sak, sak_len); + wpa_hexdump_key(MSG_DEBUG, "\tAES Key Unwrap of SAK:", + unwrap_sak, sak_len); sa_key = os_zalloc(sizeof(*sa_key)); if (!sa_key) { @@ -1614,6 +1654,7 @@ ieee802_1x_mka_decode_dist_sak_body( sa_key->an = body->dan; ieee802_1x_kay_init_data_key(sa_key); + ieee802_1x_kay_use_data_key(sa_key); dl_list_add(&participant->sak_list, &sa_key->list); ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id); @@ -1625,6 +1666,7 @@ ieee802_1x_mka_decode_dist_sak_body( ieee802_1x_cp_signal_newsak(kay->cp); ieee802_1x_cp_sm_step(kay->cp); + kay->rcvd_keys++; participant->to_use_sak = TRUE; return 0; @@ -1875,7 +1917,17 @@ static struct mka_param_body_handler mka_body_handler[] = { /** - * ieee802_1x_kay_deinit_data_key - + * ieee802_1x_kay_use_data_key - Take reference on a key + */ +static void ieee802_1x_kay_use_data_key(struct data_key *pkey) +{ + pkey->user++; +} + + +/** + * ieee802_1x_kay_deinit_data_key - Release reference on a key and + * free if there are no remaining users */ static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey) { @@ -1886,7 +1938,6 @@ static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey) if (pkey->user > 1) return; - dl_list_del(&pkey->list); os_free(pkey->key); os_free(pkey); } @@ -1975,7 +2026,7 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) wpa_printf(MSG_ERROR, "KaY: SAK Length not support"); goto fail; } - wpa_hexdump(MSG_DEBUG, "KaY: generated new SAK", key, key_len); + wpa_hexdump_key(MSG_DEBUG, "KaY: generated new SAK", key, key_len); os_free(context); context = NULL; @@ -1996,7 +2047,9 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant) participant->new_key = sa_key; + ieee802_1x_kay_use_data_key(sa_key); dl_list_add(&participant->sak_list, &sa_key->list); + ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id); ieee802_1x_cp_sm_step(kay->cp); ieee802_1x_cp_set_offset(kay->cp, kay->macsec_confidentiality); @@ -2049,6 +2102,7 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant) struct ieee802_1x_kay_peer *key_server = NULL; struct ieee802_1x_kay *kay = participant->kay; Boolean i_is_key_server; + int priority_comparison; if (participant->is_obliged_key_server) { participant->new_sak = TRUE; @@ -2079,8 +2133,14 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant) tmp.key_server_priority = kay->actor_priority; os_memcpy(&tmp.sci, &kay->actor_sci, sizeof(tmp.sci)); - if (compare_priorities(&tmp, key_server) < 0) + priority_comparison = compare_priorities(&tmp, key_server); + if (priority_comparison < 0) { i_is_key_server = TRUE; + } else if (priority_comparison == 0) { + wpa_printf(MSG_WARNING, + "KaY: Cannot elect key server between me and peer, duplicate MAC detected"); + key_server = NULL; + } } else if (participant->can_be_key_server) { i_is_key_server = TRUE; } @@ -2280,6 +2340,16 @@ ieee802_1x_participant_send_mkpdu( static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa); + +static void ieee802_1x_delete_transmit_sa(struct ieee802_1x_kay *kay, + struct transmit_sa *sa) +{ + secy_disable_transmit_sa(kay, sa); + secy_delete_transmit_sa(kay, sa); + ieee802_1x_kay_deinit_transmit_sa(sa); +} + + /** * ieee802_1x_participant_timer - */ @@ -2323,7 +2393,6 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) &participant->rxsc_list, struct receive_sc, list) { if (sci_equal(&rxsc->sci, &peer->sci)) { - secy_delete_receive_sc(kay, rxsc); ieee802_1x_kay_deinit_receive_sc( participant, rxsc); } @@ -2340,7 +2409,13 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) participant->advised_capability = MACSEC_CAP_NOT_IMPLEMENTED; participant->to_use_sak = FALSE; - kay->authenticated = TRUE; + participant->ltx = FALSE; + participant->lrx = FALSE; + participant->otx = FALSE; + participant->orx = FALSE; + participant->is_key_server = FALSE; + participant->is_elected = FALSE; + kay->authenticated = FALSE; kay->secured = FALSE; kay->failed = FALSE; kay->ltx_kn = 0; @@ -2354,11 +2429,10 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) dl_list_for_each_safe(txsa, pre_txsa, &participant->txsc->sa_list, struct transmit_sa, list) { - secy_disable_transmit_sa(kay, txsa); - ieee802_1x_kay_deinit_transmit_sa(txsa); + ieee802_1x_delete_transmit_sa(kay, txsa); } - ieee802_1x_cp_connect_authenticated(kay->cp); + ieee802_1x_cp_connect_pending(kay->cp); ieee802_1x_cp_sm_step(kay->cp); } else { ieee802_1x_kay_elect_key_server(participant); @@ -2385,7 +2459,8 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx) participant->new_sak = FALSE; } - if (participant->retry_count < MAX_RETRY_CNT) { + if (participant->retry_count < MAX_RETRY_CNT || + participant->mode == PSK) { ieee802_1x_participant_send_mkpdu(participant); participant->retry_count++; } @@ -2429,6 +2504,7 @@ ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN, psa->confidentiality = FALSE; psa->an = an; + ieee802_1x_kay_use_data_key(key); psa->pkey = key; psa->next_pn = next_PN; psa->sc = psc; @@ -2438,8 +2514,8 @@ ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN, dl_list_add(&psc->sa_list, &psa->list); wpa_printf(MSG_DEBUG, - "KaY: Create transmit SA(an: %hhu, next_PN: %u) of SC(channel: %d)", - an, next_PN, psc->channel); + "KaY: Create transmit SA(an: %hhu, next_PN: %u) of SC", + an, next_PN); return psa; } @@ -2450,6 +2526,7 @@ ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN, */ static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa) { + ieee802_1x_kay_deinit_data_key(psa->pkey); psa->pkey = NULL; wpa_printf(MSG_DEBUG, "KaY: Delete transmit SA(an: %hhu) of SC", @@ -2463,8 +2540,7 @@ static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa) * init_transmit_sc - */ static struct transmit_sc * -ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci, - int channel) +ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci) { struct transmit_sc *psc; @@ -2474,7 +2550,6 @@ ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci, return NULL; } os_memcpy(&psc->sci, sci, sizeof(psc->sci)); - psc->channel = channel; os_get_time(&psc->created_time); psc->transmitting = FALSE; @@ -2482,7 +2557,7 @@ ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci, psc->enciphering_sa = FALSE; dl_list_init(&psc->sa_list); - wpa_printf(MSG_DEBUG, "KaY: Create transmit SC(channel: %d)", channel); + wpa_printf(MSG_DEBUG, "KaY: Create transmit SC"); wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)sci , sizeof(*sci)); return psc; @@ -2498,14 +2573,11 @@ ieee802_1x_kay_deinit_transmit_sc( { struct transmit_sa *psa, *tmp; - wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC(channel: %d)", - psc->channel); - dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa, - list) { - secy_disable_transmit_sa(participant->kay, psa); - ieee802_1x_kay_deinit_transmit_sa(psa); - } + wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC"); + dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa, list) + ieee802_1x_delete_transmit_sa(participant->kay, psa); + secy_delete_transmit_sc(participant->kay, psc); os_free(psc); } @@ -2582,6 +2654,32 @@ int ieee802_1x_kay_set_old_sa_attr(struct ieee802_1x_kay *kay, } +static struct transmit_sa * lookup_txsa_by_an(struct transmit_sc *txsc, u8 an) +{ + struct transmit_sa *txsa; + + dl_list_for_each(txsa, &txsc->sa_list, struct transmit_sa, list) { + if (txsa->an == an) + return txsa; + } + + return NULL; +} + + +static struct receive_sa * lookup_rxsa_by_an(struct receive_sc *rxsc, u8 an) +{ + struct receive_sa *rxsa; + + dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list) { + if (rxsa->an == an) + return rxsa; + } + + return NULL; +} + + /** * ieee802_1x_kay_create_sas - */ @@ -2616,6 +2714,9 @@ int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay, } dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) { + while ((rxsa = lookup_rxsa_by_an(rxsc, latest_sak->an)) != NULL) + ieee802_1x_delete_receive_sa(kay, rxsa); + rxsa = ieee802_1x_kay_init_receive_sa(rxsc, latest_sak->an, 1, latest_sak); if (!rxsa) @@ -2624,6 +2725,10 @@ int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay, secy_create_receive_sa(kay, rxsa); } + while ((txsa = lookup_txsa_by_an(principal->txsc, latest_sak->an)) != + NULL) + ieee802_1x_delete_transmit_sa(kay, txsa); + txsa = ieee802_1x_kay_init_transmit_sa(principal->txsc, latest_sak->an, 1, latest_sak); if (!txsa) @@ -2657,20 +2762,16 @@ int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay, /* remove the transmit sa */ dl_list_for_each_safe(txsa, pre_txsa, &principal->txsc->sa_list, struct transmit_sa, list) { - if (is_ki_equal(&txsa->pkey->key_identifier, ki)) { - secy_disable_transmit_sa(kay, txsa); - ieee802_1x_kay_deinit_transmit_sa(txsa); - } + if (is_ki_equal(&txsa->pkey->key_identifier, ki)) + ieee802_1x_delete_transmit_sa(kay, txsa); } /* remove the receive sa */ dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) { dl_list_for_each_safe(rxsa, pre_rxsa, &rxsc->sa_list, struct receive_sa, list) { - if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) { - secy_disable_receive_sa(kay, rxsa); - ieee802_1x_kay_deinit_receive_sa(rxsa); - } + if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) + ieee802_1x_delete_receive_sa(kay, rxsa); } } @@ -2678,6 +2779,7 @@ int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay, dl_list_for_each_safe(sa_key, pre_key, &principal->sak_list, struct data_key, list) { if (is_ki_equal(&sa_key->key_identifier, ki)) { + dl_list_del(&sa_key->list); ieee802_1x_kay_deinit_data_key(sa_key); break; } @@ -2759,7 +2861,7 @@ int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay) if (!principal) return -1; - if (principal->retry_count < MAX_RETRY_CNT) { + if (principal->retry_count < MAX_RETRY_CNT || principal->mode == PSK) { ieee802_1x_participant_send_mkpdu(principal); principal->retry_count++; } @@ -2782,6 +2884,7 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, size_t mka_msg_len; struct ieee802_1x_mka_participant *participant; size_t body_len; + size_t ckn_len; u8 icv[MAX_ICV_LEN]; u8 *msg_icv; @@ -2821,8 +2924,22 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay, return -1; } + if (body_len < sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN) { + wpa_printf(MSG_DEBUG, "KaY: Too small body length %zu", + body_len); + return -1; + } + ckn_len = body_len - + (sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN); + if (ckn_len < 1 || ckn_len > MAX_CKN_LEN) { + wpa_printf(MSG_ERROR, + "KaY: Received EAPOL-MKA CKN Length (%zu bytes) is out of range (<= %u bytes)", + ckn_len, MAX_CKN_LEN); + return -1; + } + /* CKN should be owned by I */ - participant = ieee802_1x_kay_get_participant(kay, body->ckn); + participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len); if (!participant) { wpa_printf(MSG_DEBUG, "CKN is not included in my CA"); return -1; @@ -2945,7 +3062,7 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay, "KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV", left_len, MKA_HDR_LEN, body_len, DEFAULT_ICV_LEN); - continue; + return -1; } if (handled[body_type]) @@ -3020,13 +3137,14 @@ static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf, */ struct ieee802_1x_kay * ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, - const char *ifname, const u8 *addr) + u16 port, u8 priority, const char *ifname, const u8 *addr) { struct ieee802_1x_kay *kay; kay = os_zalloc(sizeof(*kay)); if (!kay) { wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__); + os_free(ctx); return NULL; } @@ -3042,8 +3160,8 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, os_strlcpy(kay->if_name, ifname, IFNAMSIZ); os_memcpy(kay->actor_sci.addr, addr, ETH_ALEN); - kay->actor_sci.port = host_to_be16(0x0001); - kay->actor_priority = DEFAULT_PRIO_NOT_KEY_SERVER; + kay->actor_sci.port = host_to_be16(port ? port : 0x0001); + kay->actor_priority = priority; /* While actor acts as a key server, shall distribute sakey */ kay->dist_kn = 1; @@ -3060,7 +3178,12 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, dl_list_init(&kay->participant_list); - if (policy == DO_NOT_SECURE) { + if (policy != DO_NOT_SECURE && + secy_get_capability(kay, &kay->macsec_capable) < 0) + goto error; + + if (policy == DO_NOT_SECURE || + kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) { kay->macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED; kay->macsec_desired = FALSE; kay->macsec_protect = FALSE; @@ -3069,29 +3192,32 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, kay->macsec_replay_window = 0; kay->macsec_confidentiality = CONFIDENTIALITY_NONE; } else { - kay->macsec_capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50; kay->macsec_desired = TRUE; kay->macsec_protect = TRUE; + kay->macsec_encrypt = policy == SHOULD_ENCRYPT; kay->macsec_validate = Strict; kay->macsec_replay_protect = FALSE; kay->macsec_replay_window = 0; - kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0; + if (kay->macsec_capable >= MACSEC_CAP_INTEG_AND_CONF) + kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0; + else + kay->macsec_confidentiality = CONFIDENTIALITY_NONE; } wpa_printf(MSG_DEBUG, "KaY: state machine created"); /* Initialize the SecY must be prio to CP, as CP will control SecY */ - secy_init_macsec(kay); - secy_get_available_transmit_sc(kay, &kay->sc_ch); + if (secy_init_macsec(kay) < 0) { + wpa_printf(MSG_DEBUG, "KaY: Could not initialize MACsec"); + goto error; + } wpa_printf(MSG_DEBUG, "KaY: secy init macsec done"); /* init CP */ kay->cp = ieee802_1x_cp_sm_init(kay); - if (kay->cp == NULL) { - ieee802_1x_kay_deinit(kay); - return NULL; - } + if (kay->cp == NULL) + goto error; if (policy == DO_NOT_SECURE) { ieee802_1x_cp_connect_authenticated(kay->cp); @@ -3102,12 +3228,15 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, if (kay->l2_mka == NULL) { wpa_printf(MSG_WARNING, "KaY: Failed to initialize L2 packet processing for MKA packet"); - ieee802_1x_kay_deinit(kay); - return NULL; + goto error; } } return kay; + +error: + ieee802_1x_kay_deinit(kay); + return NULL; } @@ -3148,8 +3277,9 @@ ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay) * ieee802_1x_kay_create_mka - */ struct ieee802_1x_mka_participant * -ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn, - struct mka_key *cak, u32 life, +ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, + const struct mka_key_name *ckn, + const struct mka_key *cak, u32 life, enum mka_created_mode mode, Boolean is_authenticator) { struct ieee802_1x_mka_participant *participant; @@ -3243,8 +3373,7 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn, dl_list_init(&participant->sak_list); participant->new_key = NULL; dl_list_init(&participant->rxsc_list); - participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci, - kay->sc_ch); + participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci); secy_cp_control_protect_frames(kay, kay->macsec_protect); secy_cp_control_replay(kay, kay->macsec_replay_protect, kay->macsec_replay_window); @@ -3281,8 +3410,17 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn, usecs = os_random() % (MKA_HELLO_TIME * 1000); eloop_register_timeout(0, usecs, ieee802_1x_participant_timer, participant, NULL); - participant->mka_life = MKA_LIFE_TIME / 1000 + time(NULL) + - usecs / 1000000; + + /* Disable MKA lifetime for PSK mode. + * The peer(s) can take a long time to come up, because we + * create a "standby" MKA, and we need it to remain live until + * some peer appears. + */ + if (mode != PSK) { + participant->mka_life = MKA_LIFE_TIME / 1000 + time(NULL) + + usecs / 1000000; + } + participant->mode = mode; return participant; @@ -3309,7 +3447,7 @@ ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn) wpa_printf(MSG_DEBUG, "KaY: participant removed"); /* get the participant */ - participant = ieee802_1x_kay_get_participant(kay, ckn->name); + participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len); if (!participant) { wpa_hexdump(MSG_DEBUG, "KaY: participant is not found", ckn->name, ckn->len); @@ -3340,16 +3478,13 @@ ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn) sak = dl_list_entry(participant->sak_list.next, struct data_key, list); dl_list_del(&sak->list); - os_free(sak->key); - os_free(sak); + ieee802_1x_kay_deinit_data_key(sak); } while (!dl_list_empty(&participant->rxsc_list)) { rxsc = dl_list_entry(participant->rxsc_list.next, struct receive_sc, list); - secy_delete_receive_sc(kay, rxsc); ieee802_1x_kay_deinit_receive_sc(participant, rxsc); } - secy_delete_transmit_sc(kay, participant->txsc); ieee802_1x_kay_deinit_transmit_sc(participant, participant->txsc); os_memset(&participant->cak, 0, sizeof(participant->cak)); @@ -3371,7 +3506,7 @@ void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay, if (!kay || !ckn) return; - participant = ieee802_1x_kay_get_participant(kay, ckn->name); + participant = ieee802_1x_kay_get_participant(kay, ckn->name, ckn->len); if (!participant) return; @@ -3409,6 +3544,7 @@ ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, unsigned int cs_index) { struct ieee802_1x_mka_participant *participant; + enum macsec_cap secy_cap; if (!kay) return -1; @@ -3427,6 +3563,12 @@ ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, kay->macsec_csindex = cs_index; kay->macsec_capable = cipher_suite_tbl[kay->macsec_csindex].capable; + if (secy_get_capability(kay, &secy_cap) < 0) + return -3; + + if (kay->macsec_capable > secy_cap) + kay->macsec_capable = secy_cap; + participant = ieee802_1x_kay_get_principal_participant(kay); if (participant) { wpa_printf(MSG_INFO, "KaY: Cipher Suite changed"); @@ -3435,3 +3577,51 @@ ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, return 0; } + + +#ifdef CONFIG_CTRL_IFACE +/** + * ieee802_1x_kay_get_status - Get IEEE 802.1X KaY status details + * @sm: Pointer to KaY allocated with ieee802_1x_kay_init() + * @buf: Buffer for status information + * @buflen: Maximum buffer length + * @verbose: Whether to include verbose status information + * Returns: Number of bytes written to buf. + * + * Query KAY status information. This function fills in a text area with current + * status information. If the buffer (buf) is not large enough, status + * information will be truncated to fit the buffer. + */ +int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf, + size_t buflen) +{ + int len; + + if (!kay) + return 0; + + len = os_snprintf(buf, buflen, + "PAE KaY status=%s\n" + "Authenticated=%s\n" + "Secured=%s\n" + "Failed=%s\n" + "Actor Priority=%u\n" + "Key Server Priority=%u\n" + "Is Key Server=%s\n" + "Number of Keys Distributed=%u\n" + "Number of Keys Received=%u\n", + kay->active ? "Active" : "Not-Active", + kay->authenticated ? "Yes" : "No", + kay->secured ? "Yes" : "No", + kay->failed ? "Yes" : "No", + kay->actor_priority, + kay->key_server_priority, + kay->is_key_server ? "Yes" : "No", + kay->dist_kn - 1, + kay->rcvd_keys); + if (os_snprintf_error(buflen, len)) + return 0; + + return len; +} +#endif /* CONFIG_CTRL_IFACE */ diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h index afbaa336cbda6..b2650596cae3c 100644 --- a/src/pae/ieee802_1x_kay.h +++ b/src/pae/ieee802_1x_kay.h @@ -15,7 +15,7 @@ struct macsec_init_params; -#define MI_LEN 12 +#define MI_LEN 12 /* 96-bit Member Identifier */ #define MAX_KEY_LEN 32 /* 32 bytes, 256 bits */ #define MAX_CKN_LEN 32 /* 32 bytes, 256 bits */ @@ -24,6 +24,12 @@ struct macsec_init_params; #define MKA_LIFE_TIME 6000 #define MKA_SAK_RETIRE_TIME 3000 +/** + * struct ieee802_1x_mka_ki - Key Identifier (KI) + * @mi: Key Server's Member Identifier + * @kn: Key Number, assigned by the Key Server + * IEEE 802.1X-2010 9.8 SAK generation, distribution, and selection + */ struct ieee802_1x_mka_ki { u8 mi[MI_LEN]; u32 kn; @@ -49,6 +55,84 @@ enum mka_created_mode { EAP_EXCHANGE, }; +struct data_key { + u8 *key; + int key_len; + struct ieee802_1x_mka_ki key_identifier; + enum confidentiality_offset confidentiality_offset; + u8 an; + Boolean transmits; + Boolean receives; + struct os_time created_time; + u32 next_pn; + + /* not defined data */ + Boolean rx_latest; + Boolean tx_latest; + + int user; + + struct dl_list list; +}; + +/* TransmitSC in IEEE Std 802.1AE-2006, Figure 10-6 */ +struct transmit_sc { + struct ieee802_1x_mka_sci sci; /* const SCI sci */ + Boolean transmitting; /* bool transmitting (read only) */ + + struct os_time created_time; /* Time createdTime */ + + u8 encoding_sa; /* AN encodingSA (read only) */ + u8 enciphering_sa; /* AN encipheringSA (read only) */ + + /* not defined data */ + struct dl_list list; + struct dl_list sa_list; +}; + +/* TransmitSA in IEEE Std 802.1AE-2006, Figure 10-6 */ +struct transmit_sa { + Boolean in_use; /* bool inUse (read only) */ + u32 next_pn; /* PN nextPN (read only) */ + struct os_time created_time; /* Time createdTime */ + + Boolean enable_transmit; /* bool EnableTransmit */ + + u8 an; + Boolean confidentiality; + struct data_key *pkey; + + struct transmit_sc *sc; + struct dl_list list; /* list entry in struct transmit_sc::sa_list */ +}; + +/* ReceiveSC in IEEE Std 802.1AE-2006, Figure 10-6 */ +struct receive_sc { + struct ieee802_1x_mka_sci sci; /* const SCI sci */ + Boolean receiving; /* bool receiving (read only) */ + + struct os_time created_time; /* Time createdTime */ + + struct dl_list list; + struct dl_list sa_list; +}; + +/* ReceiveSA in IEEE Std 802.1AE-2006, Figure 10-6 */ +struct receive_sa { + Boolean enable_receive; /* bool enableReceive */ + Boolean in_use; /* bool inUse (read only) */ + + u32 next_pn; /* PN nextPN (read only) */ + u32 lowest_pn; /* PN lowestPN (read only) */ + u8 an; + struct os_time created_time; + + struct data_key *pkey; + struct receive_sc *sc; /* list entry in struct receive_sc::sa_list */ + + struct dl_list list; +}; + struct ieee802_1x_kay_ctx { /* pointer to arbitrary upper level context */ void *ctx; @@ -56,34 +140,30 @@ struct ieee802_1x_kay_ctx { /* abstract wpa driver interface */ int (*macsec_init)(void *ctx, struct macsec_init_params *params); int (*macsec_deinit)(void *ctx); + int (*macsec_get_capability)(void *priv, enum macsec_cap *cap); int (*enable_protect_frames)(void *ctx, Boolean enabled); + int (*enable_encrypt)(void *ctx, Boolean enabled); int (*set_replay_protect)(void *ctx, Boolean enabled, u32 window); int (*set_current_cipher_suite)(void *ctx, u64 cs); int (*enable_controlled_port)(void *ctx, Boolean enabled); - int (*get_receive_lowest_pn)(void *ctx, u32 channel, u8 an, - u32 *lowest_pn); - int (*get_transmit_next_pn)(void *ctx, u32 channel, u8 an, - u32 *next_pn); - int (*set_transmit_next_pn)(void *ctx, u32 channel, u8 an, u32 next_pn); - int (*get_available_receive_sc)(void *ctx, u32 *channel); - int (*create_receive_sc)(void *ctx, u32 channel, - struct ieee802_1x_mka_sci *sci, + int (*get_receive_lowest_pn)(void *ctx, struct receive_sa *sa); + int (*get_transmit_next_pn)(void *ctx, struct transmit_sa *sa); + int (*set_transmit_next_pn)(void *ctx, struct transmit_sa *sa); + int (*create_receive_sc)(void *ctx, struct receive_sc *sc, enum validate_frames vf, enum confidentiality_offset co); - int (*delete_receive_sc)(void *ctx, u32 channel); - int (*create_receive_sa)(void *ctx, u32 channel, u8 an, u32 lowest_pn, - const u8 *sak); - int (*enable_receive_sa)(void *ctx, u32 channel, u8 an); - int (*disable_receive_sa)(void *ctx, u32 channel, u8 an); - int (*get_available_transmit_sc)(void *ctx, u32 *channel); - int (*create_transmit_sc)(void *ctx, u32 channel, - const struct ieee802_1x_mka_sci *sci, + int (*delete_receive_sc)(void *ctx, struct receive_sc *sc); + int (*create_receive_sa)(void *ctx, struct receive_sa *sa); + int (*delete_receive_sa)(void *ctx, struct receive_sa *sa); + int (*enable_receive_sa)(void *ctx, struct receive_sa *sa); + int (*disable_receive_sa)(void *ctx, struct receive_sa *sa); + int (*create_transmit_sc)(void *ctx, struct transmit_sc *sc, enum confidentiality_offset co); - int (*delete_transmit_sc)(void *ctx, u32 channel); - int (*create_transmit_sa)(void *ctx, u32 channel, u8 an, u32 next_pn, - Boolean confidentiality, const u8 *sak); - int (*enable_transmit_sa)(void *ctx, u32 channel, u8 an); - int (*disable_transmit_sa)(void *ctx, u32 channel, u8 an); + int (*delete_transmit_sc)(void *ctx, struct transmit_sc *sc); + int (*create_transmit_sa)(void *ctx, struct transmit_sa *sa); + int (*delete_transmit_sa)(void *ctx, struct transmit_sa *sa); + int (*enable_transmit_sa)(void *ctx, struct transmit_sa *sa); + int (*disable_transmit_sa)(void *ctx, struct transmit_sa *sa); }; struct ieee802_1x_kay { @@ -102,6 +182,7 @@ struct ieee802_1x_kay { enum macsec_cap macsec_capable; Boolean macsec_desired; Boolean macsec_protect; + Boolean macsec_encrypt; Boolean macsec_replay_protect; u32 macsec_replay_window; enum validate_frames macsec_validate; @@ -127,12 +208,12 @@ struct ieee802_1x_kay { int mka_algindex; /* MKA alg table index */ u32 dist_kn; + u32 rcvd_keys; u8 dist_an; time_t dist_time; u8 mka_version; u8 algo_agility[4]; - u32 sc_ch; u32 pn_exhaustion; Boolean port_enable; @@ -151,14 +232,17 @@ struct ieee802_1x_kay { }; +u64 mka_sci_u64(struct ieee802_1x_mka_sci *sci); + struct ieee802_1x_kay * ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy, - const char *ifname, const u8 *addr); + u16 port, u8 priority, const char *ifname, const u8 *addr); void ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay); struct ieee802_1x_mka_participant * ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, - struct mka_key_name *ckn, struct mka_key *cak, + const struct mka_key_name *ckn, + const struct mka_key *cak, u32 life, enum mka_created_mode mode, Boolean is_authenticator); void ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, @@ -185,5 +269,7 @@ int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay, int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay, struct ieee802_1x_mka_ki *lki); int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay); +int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf, + size_t buflen); #endif /* IEEE802_1X_KAY_H */ diff --git a/src/pae/ieee802_1x_kay_i.h b/src/pae/ieee802_1x_kay_i.h index 622282e97c513..bc522d89852bf 100644 --- a/src/pae/ieee802_1x_kay_i.h +++ b/src/pae/ieee802_1x_kay_i.h @@ -54,88 +54,6 @@ struct ieee802_1x_kay_peer { struct dl_list list; }; -struct data_key { - u8 *key; - int key_len; - struct ieee802_1x_mka_ki key_identifier; - enum confidentiality_offset confidentiality_offset; - u8 an; - Boolean transmits; - Boolean receives; - struct os_time created_time; - u32 next_pn; - - /* not defined data */ - Boolean rx_latest; - Boolean tx_latest; - - int user; /* FIXME: to indicate if it can be delete safely */ - - struct dl_list list; -}; - -/* TransmitSC in IEEE Std 802.1AE-2006, Figure 10-6 */ -struct transmit_sc { - struct ieee802_1x_mka_sci sci; /* const SCI sci */ - Boolean transmitting; /* bool transmitting (read only) */ - - struct os_time created_time; /* Time createdTime */ - - u8 encoding_sa; /* AN encodingSA (read only) */ - u8 enciphering_sa; /* AN encipheringSA (read only) */ - - /* not defined data */ - unsigned int channel; - - struct dl_list list; - struct dl_list sa_list; -}; - -/* TransmitSA in IEEE Std 802.1AE-2006, Figure 10-6 */ -struct transmit_sa { - Boolean in_use; /* bool inUse (read only) */ - u32 next_pn; /* PN nextPN (read only) */ - struct os_time created_time; /* Time createdTime */ - - Boolean enable_transmit; /* bool EnableTransmit */ - - u8 an; - Boolean confidentiality; - struct data_key *pkey; - - struct transmit_sc *sc; - struct dl_list list; /* list entry in struct transmit_sc::sa_list */ -}; - -/* ReceiveSC in IEEE Std 802.1AE-2006, Figure 10-6 */ -struct receive_sc { - struct ieee802_1x_mka_sci sci; /* const SCI sci */ - Boolean receiving; /* bool receiving (read only) */ - - struct os_time created_time; /* Time createdTime */ - - unsigned int channel; - - struct dl_list list; - struct dl_list sa_list; -}; - -/* ReceiveSA in IEEE Std 802.1AE-2006, Figure 10-6 */ -struct receive_sa { - Boolean enable_receive; /* bool enableReceive */ - Boolean in_use; /* bool inUse (read only) */ - - u32 next_pn; /* PN nextPN (read only) */ - u32 lowest_pn; /* PN lowestPN (read only) */ - u8 an; - struct os_time created_time; - - struct data_key *pkey; - struct receive_sc *sc; /* list entry in struct receive_sc::sa_list */ - - struct dl_list list; -}; - struct macsec_ciphersuite { u64 id; char name[32]; @@ -175,6 +93,7 @@ struct ieee802_1x_mka_participant { Boolean active; Boolean participant; Boolean retain; + enum mka_created_mode mode; enum { DEFAULT, DISABLED, ON_OPER_UP, ALWAYS } activate; @@ -250,6 +169,22 @@ struct ieee802_1x_mka_hdr { #define MKA_HDR_LEN sizeof(struct ieee802_1x_mka_hdr) +/** + * struct ieee802_1x_mka_basic_body - Basic Parameter Set (Figure 11-8) + * @version: MKA Version Identifier + * @priority: Key Server Priority + * @length: Parameter set body length + * @macsec_capability: MACsec capability, as defined in ieee802_1x_defs.h + * @macsec_desired: the participant wants MACsec to be used to protect frames + * (9.6.1) + * @key_server: the participant has not decided that another participant is or + * will be the key server (9.5.1) + * @length1: Parameter set body length (cont) + * @actor_mi: Actor's Member Identifier + * @actor_mn: Actor's Message Number + * @algo_agility: Algorithm Agility parameter + * @ckn: CAK Name + */ struct ieee802_1x_mka_basic_body { /* octet 1 */ u8 version; @@ -279,6 +214,14 @@ struct ieee802_1x_mka_basic_body { u8 ckn[0]; }; +/** + * struct ieee802_1x_mka_peer_body - Live Peer List and Potential Peer List + * parameter sets (Figure 11-9) + * @type: Parameter set type (1 or 2) + * @length: Parameter set body length + * @length1: Parameter set body length (cont) + * @peer: array of (MI, MN) pairs + */ struct ieee802_1x_mka_peer_body { /* octet 1 */ u8 type; @@ -299,6 +242,28 @@ struct ieee802_1x_mka_peer_body { /* followed by Peers */ }; +/** + * struct ieee802_1x_mka_sak_use_body - MACsec SAK Use parameter set (Figure + * 11-10) + * @type: MKA message type + * @lan: latest key AN + * @ltx: latest key TX + * @lrx: latest key RX + * @oan: old key AN + * @otx: old key TX + * @orx: old key RX + * @ptx: plain TX, ie protectFrames is False + * @prx: plain RX, ie validateFrames is not Strict + * @delay_protect: True if LPNs are being reported sufficiently frequently to + * allow the recipient to provide data delay protection. If False, the LPN + * can be reported as zero. + * @lsrv_mi: latest key server MI + * @lkn: latest key number (together with MI, form the KI) + * @llpn: latest lowest acceptable PN (LPN) + * @osrv_mi: old key server MI + * @okn: old key number (together with MI, form the KI) + * @olpn: old lowest acceptable PN (LPN) + */ struct ieee802_1x_mka_sak_use_body { /* octet 1 */ u8 type; @@ -352,7 +317,21 @@ struct ieee802_1x_mka_sak_use_body { be32 olpn; }; - +/** + * struct ieee802_1x_mka_dist_sak_body - Distributed SAK parameter set + * (GCM-AES-128, Figure 11-11) + * @type: Parameter set type (4) + * @length: Parameter set body length + * @length1: Parameter set body length (cont) + * Total parameter body length values: + * - 0 for plain text + * - 28 for GCM-AES-128 + * - 36 or more for other cipher suites + * @confid_offset: confidentiality offset, as defined in ieee802_1x_defs.h + * @dan: distributed AN (0 for plain text) + * @kn: Key Number + * @sak: AES Key Wrap of SAK (see 9.8) + */ struct ieee802_1x_mka_dist_sak_body { /* octet 1 */ u8 type; @@ -385,6 +364,41 @@ struct ieee802_1x_mka_dist_sak_body { u8 sak[0]; }; +/** + * struct ieee802_1x_mka_dist_cak_body - Distributed CAK parameter set (Figure + * 11-13) + * @type: Parameter set type (5) + * @length: Parameter set body length + * @length1: Parameter set body length (cont) + * Total parameter body length values: + * - 0 for plain text + * - 28 for GCM-AES-128 + * - 36 or more for other cipher suites + * @cak: AES Key Wrap of CAK (see 9.8) + * @ckn: CAK Name + */ +struct ieee802_1x_mka_dist_cak_body { + /* octet 1 */ + u8 type; + /* octet 2 */ + u8 reserve; + /* octet 3 */ +#if __BYTE_ORDER == __LITTLE_ENDIAN + u8 length:4; + u8 reserve1:4; +#elif __BYTE_ORDER == __BIG_ENDIAN + u8 reserve1:4; + u8 length:4; +#endif + /* octet 4 */ + u8 length1; + + /* octet 5 - 28 */ + u8 cak[24]; + + /* followed by CAK Name, 29- */ + u8 ckn[0]; +}; struct ieee802_1x_mka_icv_body { /* octet 1 */ diff --git a/src/pae/ieee802_1x_secy_ops.c b/src/pae/ieee802_1x_secy_ops.c index 2d12911dbfcfd..ab5339bb20462 100644 --- a/src/pae/ieee802_1x_secy_ops.c +++ b/src/pae/ieee802_1x_secy_ops.c @@ -45,6 +45,26 @@ int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, Boolean enabled) } +int secy_cp_control_encrypt(struct ieee802_1x_kay *kay, Boolean enabled) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->enable_encrypt) { + wpa_printf(MSG_ERROR, + "KaY: secy enable_encrypt operation not supported"); + return -1; + } + + return ops->enable_encrypt(ops->ctx, enabled); +} + + int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean enabled, u32 win) { struct ieee802_1x_kay_ctx *ops; @@ -113,55 +133,48 @@ int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, Boolean enabled) } -int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay, - struct receive_sa *rxsa) +int secy_get_capability(struct ieee802_1x_kay *kay, enum macsec_cap *cap) { struct ieee802_1x_kay_ctx *ops; - if (!kay || !rxsa) { + if (!kay) { wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); return -1; } ops = kay->ctx; - if (!ops || !ops->get_receive_lowest_pn) { + if (!ops || !ops->macsec_get_capability) { wpa_printf(MSG_ERROR, - "KaY: secy get_receive_lowest_pn operation not supported"); + "KaY: secy macsec_get_capability operation not supported"); return -1; } - return ops->get_receive_lowest_pn(ops->ctx, - rxsa->sc->channel, - rxsa->an, - &rxsa->lowest_pn); + return ops->macsec_get_capability(ops->ctx, cap); } -int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay, - struct transmit_sa *txsa) +int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay, + struct receive_sa *rxsa) { struct ieee802_1x_kay_ctx *ops; - if (!kay || !txsa) { + if (!kay || !rxsa) { wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); return -1; } ops = kay->ctx; - if (!ops || !ops->get_transmit_next_pn) { + if (!ops || !ops->get_receive_lowest_pn) { wpa_printf(MSG_ERROR, "KaY: secy get_receive_lowest_pn operation not supported"); return -1; } - return ops->get_transmit_next_pn(ops->ctx, - txsa->sc->channel, - txsa->an, - &txsa->next_pn); + return ops->get_receive_lowest_pn(ops->ctx, rxsa); } -int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay, +int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay, struct transmit_sa *txsa) { struct ieee802_1x_kay_ctx *ops; @@ -172,36 +185,34 @@ int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay, } ops = kay->ctx; - if (!ops || !ops->set_transmit_next_pn) { + if (!ops || !ops->get_transmit_next_pn) { wpa_printf(MSG_ERROR, "KaY: secy get_receive_lowest_pn operation not supported"); return -1; } - return ops->set_transmit_next_pn(ops->ctx, - txsa->sc->channel, - txsa->an, - txsa->next_pn); + return ops->get_transmit_next_pn(ops->ctx, txsa); } -int secy_get_available_receive_sc(struct ieee802_1x_kay *kay, u32 *channel) +int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay, + struct transmit_sa *txsa) { struct ieee802_1x_kay_ctx *ops; - if (!kay) { + if (!kay || !txsa) { wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); return -1; } ops = kay->ctx; - if (!ops || !ops->get_available_receive_sc) { + if (!ops || !ops->set_transmit_next_pn) { wpa_printf(MSG_ERROR, - "KaY: secy get_available_receive_sc operation not supported"); + "KaY: secy get_receive_lowest_pn operation not supported"); return -1; } - return ops->get_available_receive_sc(ops->ctx, channel); + return ops->set_transmit_next_pn(ops->ctx, txsa); } @@ -221,8 +232,7 @@ int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc) return -1; } - return ops->create_receive_sc(ops->ctx, rxsc->channel, &rxsc->sci, - kay->vf, kay->co); + return ops->create_receive_sc(ops->ctx, rxsc, kay->vf, kay->co); } @@ -242,7 +252,7 @@ int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc) return -1; } - return ops->delete_receive_sc(ops->ctx, rxsc->channel); + return ops->delete_receive_sc(ops->ctx, rxsc); } @@ -262,12 +272,11 @@ int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa) return -1; } - return ops->create_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an, - rxsa->lowest_pn, rxsa->pkey->key); + return ops->create_receive_sa(ops->ctx, rxsa); } -int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa) +int secy_delete_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa) { struct ieee802_1x_kay_ctx *ops; @@ -277,19 +286,17 @@ int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa) } ops = kay->ctx; - if (!ops || !ops->enable_receive_sa) { + if (!ops || !ops->delete_receive_sa) { wpa_printf(MSG_ERROR, - "KaY: secy enable_receive_sa operation not supported"); + "KaY: secy delete_receive_sa operation not supported"); return -1; } - rxsa->enable_receive = TRUE; - - return ops->enable_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an); + return ops->delete_receive_sa(ops->ctx, rxsa); } -int secy_disable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa) +int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa) { struct ieee802_1x_kay_ctx *ops; @@ -299,35 +306,37 @@ int secy_disable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa) } ops = kay->ctx; - if (!ops || !ops->disable_receive_sa) { + if (!ops || !ops->enable_receive_sa) { wpa_printf(MSG_ERROR, - "KaY: secy disable_receive_sa operation not supported"); + "KaY: secy enable_receive_sa operation not supported"); return -1; } - rxsa->enable_receive = FALSE; + rxsa->enable_receive = TRUE; - return ops->disable_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an); + return ops->enable_receive_sa(ops->ctx, rxsa); } -int secy_get_available_transmit_sc(struct ieee802_1x_kay *kay, u32 *channel) +int secy_disable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa) { struct ieee802_1x_kay_ctx *ops; - if (!kay) { + if (!kay || !rxsa) { wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); return -1; } ops = kay->ctx; - if (!ops || !ops->get_available_transmit_sc) { + if (!ops || !ops->disable_receive_sa) { wpa_printf(MSG_ERROR, - "KaY: secy get_available_transmit_sc operation not supported"); + "KaY: secy disable_receive_sa operation not supported"); return -1; } - return ops->get_available_transmit_sc(ops->ctx, channel); + rxsa->enable_receive = FALSE; + + return ops->disable_receive_sa(ops->ctx, rxsa); } @@ -348,8 +357,7 @@ int secy_create_transmit_sc(struct ieee802_1x_kay *kay, return -1; } - return ops->create_transmit_sc(ops->ctx, txsc->channel, &txsc->sci, - kay->co); + return ops->create_transmit_sc(ops->ctx, txsc, kay->co); } @@ -370,7 +378,7 @@ int secy_delete_transmit_sc(struct ieee802_1x_kay *kay, return -1; } - return ops->delete_transmit_sc(ops->ctx, txsc->channel); + return ops->delete_transmit_sc(ops->ctx, txsc); } @@ -391,9 +399,28 @@ int secy_create_transmit_sa(struct ieee802_1x_kay *kay, return -1; } - return ops->create_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an, - txsa->next_pn, txsa->confidentiality, - txsa->pkey->key); + return ops->create_transmit_sa(ops->ctx, txsa); +} + + +int secy_delete_transmit_sa(struct ieee802_1x_kay *kay, + struct transmit_sa *txsa) +{ + struct ieee802_1x_kay_ctx *ops; + + if (!kay || !txsa) { + wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__); + return -1; + } + + ops = kay->ctx; + if (!ops || !ops->delete_transmit_sa) { + wpa_printf(MSG_ERROR, + "KaY: secy delete_transmit_sa operation not supported"); + return -1; + } + + return ops->delete_transmit_sa(ops->ctx, txsa); } @@ -416,7 +443,7 @@ int secy_enable_transmit_sa(struct ieee802_1x_kay *kay, txsa->enable_transmit = TRUE; - return ops->enable_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an); + return ops->enable_transmit_sa(ops->ctx, txsa); } @@ -439,7 +466,7 @@ int secy_disable_transmit_sa(struct ieee802_1x_kay *kay, txsa->enable_transmit = FALSE; - return ops->disable_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an); + return ops->disable_transmit_sa(ops->ctx, txsa); } diff --git a/src/pae/ieee802_1x_secy_ops.h b/src/pae/ieee802_1x_secy_ops.h index f5057ee119583..9fb29c3ddfa01 100644 --- a/src/pae/ieee802_1x_secy_ops.h +++ b/src/pae/ieee802_1x_secy_ops.h @@ -13,10 +13,6 @@ #include "common/ieee802_1x_defs.h" struct ieee802_1x_kay_conf; -struct receive_sa; -struct transmit_sa; -struct receive_sc; -struct transmit_sc; int secy_init_macsec(struct ieee802_1x_kay *kay); int secy_deinit_macsec(struct ieee802_1x_kay *kay); @@ -25,6 +21,7 @@ int secy_deinit_macsec(struct ieee802_1x_kay *kay); int secy_cp_control_validate_frames(struct ieee802_1x_kay *kay, enum validate_frames vf); int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, Boolean flag); +int secy_cp_control_encrypt(struct ieee802_1x_kay *kay, Boolean enabled); int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean flag, u32 win); int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, u64 cs); int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay, @@ -32,27 +29,29 @@ int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay, int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, Boolean flag); /****** KaY -> SecY *******/ +int secy_get_capability(struct ieee802_1x_kay *kay, enum macsec_cap *cap); int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay, struct receive_sa *rxsa); int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay, struct transmit_sa *txsa); int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay, struct transmit_sa *txsa); -int secy_get_available_receive_sc(struct ieee802_1x_kay *kay, u32 *channel); int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc); int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc); int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa); +int secy_delete_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa); int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa); int secy_disable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa); -int secy_get_available_transmit_sc(struct ieee802_1x_kay *kay, u32 *channel); int secy_create_transmit_sc(struct ieee802_1x_kay *kay, struct transmit_sc *txsc); int secy_delete_transmit_sc(struct ieee802_1x_kay *kay, struct transmit_sc *txsc); int secy_create_transmit_sa(struct ieee802_1x_kay *kay, struct transmit_sa *txsa); +int secy_delete_transmit_sa(struct ieee802_1x_kay *kay, + struct transmit_sa *txsa); int secy_enable_transmit_sa(struct ieee802_1x_kay *kay, struct transmit_sa *txsa); int secy_disable_transmit_sa(struct ieee802_1x_kay *kay, diff --git a/src/radius/radius.c b/src/radius/radius.c index 407e4f8b96149..07240ea2243d9 100644 --- a/src/radius/radius.c +++ b/src/radius/radius.c @@ -210,7 +210,7 @@ static const struct radius_attr_type radius_attrs[] = { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 }, - { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", + { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords", RADIUS_ATTR_INT32 }, @@ -250,6 +250,8 @@ static const struct radius_attr_type radius_attrs[] = { RADIUS_ATTR_MOBILITY_DOMAIN_ID, "Mobility-Domain-Id", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_WLAN_HESSID, "WLAN-HESSID", RADIUS_ATTR_TEXT }, + { RADIUS_ATTR_WLAN_REASON_CODE, "WLAN-Reason-Code", + RADIUS_ATTR_INT32 }, { RADIUS_ATTR_WLAN_PAIRWISE_CIPHER, "WLAN-Pairwise-Cipher", RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_WLAN_GROUP_CIPHER, "WLAN-Group-Cipher", @@ -631,6 +633,9 @@ struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, size_t buf_needed; struct radius_attr_hdr *attr; + if (TEST_FAIL()) + return NULL; + if (data_len > RADIUS_MAX_ATTR_LEN) { wpa_printf(MSG_ERROR, "radius_msg_add_attr: too long attribute (%lu bytes)", (unsigned long) data_len); @@ -960,10 +965,9 @@ static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, } len = vhdr->vendor_length - sizeof(*vhdr); - data = os_malloc(len); + data = os_memdup(pos + sizeof(*vhdr), len); if (data == NULL) return NULL; - os_memcpy(data, pos + sizeof(*vhdr), len); if (alen) *alen = len; return data; @@ -1040,12 +1044,11 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len, return NULL; } - res = os_malloc(plain[0]); + res = os_memdup(plain + 1, plain[0]); if (res == NULL) { os_free(plain); return NULL; } - os_memcpy(res, plain + 1, plain[0]); if (reslen) *reslen = plain[0]; os_free(plain); @@ -1594,10 +1597,9 @@ char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen, goto out; /* alloc writable memory for decryption */ - buf = os_malloc(fdlen); + buf = os_memdup(fdata, fdlen); if (buf == NULL) goto out; - os_memcpy(buf, fdata, fdlen); buflen = fdlen; /* init pointers */ @@ -1684,12 +1686,11 @@ int radius_copy_class(struct radius_class_data *dst, dst->count = 0; for (i = 0; i < src->count; i++) { - dst->attr[i].data = os_malloc(src->attr[i].len); + dst->attr[i].data = os_memdup(src->attr[i].data, + src->attr[i].len); if (dst->attr[i].data == NULL) break; dst->count++; - os_memcpy(dst->attr[i].data, src->attr[i].data, - src->attr[i].len); dst->attr[i].len = src->attr[i].len; } diff --git a/src/radius/radius.h b/src/radius/radius.h index cd510d2c88e22..630c0f9d0bc5b 100644 --- a/src/radius/radius.h +++ b/src/radius/radius.h @@ -102,8 +102,13 @@ enum { RADIUS_ATTR_USER_NAME = 1, RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES = 130, RADIUS_ATTR_LOCATION_CAPABLE = 131, RADIUS_ATTR_REQUESTED_LOCATION_INFO = 132, + RADIUS_ATTR_GSS_ACCEPTOR_SERVICE_NAME = 164, + RADIUS_ATTR_GSS_ACCEPTOR_HOST_NAME = 165, + RADIUS_ATTR_GSS_ACCEPTOR_SERVICE_SPECIFICS = 166, + RADIUS_ATTR_GSS_ACCEPTOR_REALM_NAME = 167, RADIUS_ATTR_MOBILITY_DOMAIN_ID = 177, RADIUS_ATTR_WLAN_HESSID = 181, + RADIUS_ATTR_WLAN_REASON_CODE = 185, RADIUS_ATTR_WLAN_PAIRWISE_CIPHER = 186, RADIUS_ATTR_WLAN_GROUP_CIPHER = 187, RADIUS_ATTR_WLAN_AKM_SUITE = 188, @@ -193,6 +198,11 @@ enum { RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION = 3, RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ = 4, RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL = 5, + RADIUS_VENDOR_ATTR_WFA_HS20_ROAMING_CONSORTIUM = 6, + RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILENAME = 7, + RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP = 8, + RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING = 9, + RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL = 10, }; #ifdef _MSC_VER diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c index 06c804d132fd5..a87ee745eb284 100644 --- a/src/radius/radius_client.c +++ b/src/radius/radius_client.c @@ -904,13 +904,13 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) switch (res) { case RADIUS_RX_PROCESSED: radius_msg_free(msg); - /* continue */ + /* fall through */ case RADIUS_RX_QUEUED: radius_client_msg_free(req); return; case RADIUS_RX_INVALID_AUTHENTICATOR: invalid_authenticator++; - /* continue */ + /* fall through */ case RADIUS_RX_UNKNOWN: /* continue with next handler */ break; diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c index 8a3d7e0324bc2..aaa3fc26723ad 100644 --- a/src/radius/radius_das.c +++ b/src/radius/radius_das.c @@ -27,6 +27,7 @@ struct radius_das_data { void *ctx; enum radius_das_res (*disconnect)(void *ctx, struct radius_das_attrs *attr); + enum radius_das_res (*coa)(void *ctx, struct radius_das_attrs *attr); }; @@ -161,6 +162,10 @@ static struct radius_msg * radius_das_disconnect(struct radius_das_data *das, abuf, from_port); error = 508; break; + case RADIUS_DAS_COA_FAILED: + /* not used with Disconnect-Request */ + error = 405; + break; case RADIUS_DAS_SUCCESS: error = 0; break; @@ -184,6 +189,195 @@ fail: } +static struct radius_msg * radius_das_coa(struct radius_das_data *das, + struct radius_msg *msg, + const char *abuf, int from_port) +{ + struct radius_hdr *hdr; + struct radius_msg *reply; + u8 allowed[] = { + RADIUS_ATTR_USER_NAME, + RADIUS_ATTR_NAS_IP_ADDRESS, + RADIUS_ATTR_CALLING_STATION_ID, + RADIUS_ATTR_NAS_IDENTIFIER, + RADIUS_ATTR_ACCT_SESSION_ID, + RADIUS_ATTR_ACCT_MULTI_SESSION_ID, + RADIUS_ATTR_EVENT_TIMESTAMP, + RADIUS_ATTR_MESSAGE_AUTHENTICATOR, + RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, +#ifdef CONFIG_HS20 + RADIUS_ATTR_VENDOR_SPECIFIC, +#endif /* CONFIG_HS20 */ +#ifdef CONFIG_IPV6 + RADIUS_ATTR_NAS_IPV6_ADDRESS, +#endif /* CONFIG_IPV6 */ + 0 + }; + int error = 405; + u8 attr; + enum radius_das_res res; + struct radius_das_attrs attrs; + u8 *buf; + size_t len; + char tmp[100]; + u8 sta_addr[ETH_ALEN]; + + hdr = radius_msg_get_hdr(msg); + + if (!das->coa) { + wpa_printf(MSG_INFO, "DAS: CoA not supported"); + goto fail; + } + + attr = radius_msg_find_unlisted_attr(msg, allowed); + if (attr) { + wpa_printf(MSG_INFO, + "DAS: Unsupported attribute %u in CoA-Request from %s:%d", + attr, abuf, from_port); + error = 401; + goto fail; + } + + os_memset(&attrs, 0, sizeof(attrs)); + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, + &buf, &len, NULL) == 0) { + if (len != 4) { + wpa_printf(MSG_INFO, "DAS: Invalid NAS-IP-Address from %s:%d", + abuf, from_port); + error = 407; + goto fail; + } + attrs.nas_ip_addr = buf; + } + +#ifdef CONFIG_IPV6 + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, + &buf, &len, NULL) == 0) { + if (len != 16) { + wpa_printf(MSG_INFO, "DAS: Invalid NAS-IPv6-Address from %s:%d", + abuf, from_port); + error = 407; + goto fail; + } + attrs.nas_ipv6_addr = buf; + } +#endif /* CONFIG_IPV6 */ + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER, + &buf, &len, NULL) == 0) { + attrs.nas_identifier = buf; + attrs.nas_identifier_len = len; + } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID, + &buf, &len, NULL) == 0) { + if (len >= sizeof(tmp)) + len = sizeof(tmp) - 1; + os_memcpy(tmp, buf, len); + tmp[len] = '\0'; + if (hwaddr_aton2(tmp, sta_addr) < 0) { + wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id " + "'%s' from %s:%d", tmp, abuf, from_port); + error = 407; + goto fail; + } + attrs.sta_addr = sta_addr; + } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, + &buf, &len, NULL) == 0) { + attrs.user_name = buf; + attrs.user_name_len = len; + } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID, + &buf, &len, NULL) == 0) { + attrs.acct_session_id = buf; + attrs.acct_session_id_len = len; + } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID, + &buf, &len, NULL) == 0) { + attrs.acct_multi_session_id = buf; + attrs.acct_multi_session_id_len = len; + } + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + &buf, &len, NULL) == 0) { + attrs.cui = buf; + attrs.cui_len = len; + } + +#ifdef CONFIG_HS20 + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, + &buf, &len, NULL) == 0) { + if (len < 10 || WPA_GET_BE32(buf) != RADIUS_VENDOR_ID_WFA || + buf[4] != RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING || + buf[5] < 6) { + wpa_printf(MSG_INFO, + "DAS: Unsupported attribute %u in CoA-Request from %s:%d", + attr, abuf, from_port); + error = 401; + goto fail; + } + attrs.hs20_t_c_filtering = &buf[6]; + } + + if (!attrs.hs20_t_c_filtering) { + wpa_printf(MSG_INFO, + "DAS: No supported authorization change attribute in CoA-Request from %s:%d", + abuf, from_port); + error = 402; + goto fail; + } +#endif /* CONFIG_HS20 */ + + res = das->coa(das->ctx, &attrs); + switch (res) { + case RADIUS_DAS_NAS_MISMATCH: + wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d", + abuf, from_port); + error = 403; + break; + case RADIUS_DAS_SESSION_NOT_FOUND: + wpa_printf(MSG_INFO, + "DAS: Session not found for request from %s:%d", + abuf, from_port); + error = 503; + break; + case RADIUS_DAS_MULTI_SESSION_MATCH: + wpa_printf(MSG_INFO, + "DAS: Multiple sessions match for request from %s:%d", + abuf, from_port); + error = 508; + break; + case RADIUS_DAS_COA_FAILED: + wpa_printf(MSG_INFO, "DAS: CoA failed for request from %s:%d", + abuf, from_port); + error = 407; + break; + case RADIUS_DAS_SUCCESS: + error = 0; + break; + } + +fail: + reply = radius_msg_new(error ? RADIUS_CODE_COA_NAK : + RADIUS_CODE_COA_ACK, hdr->identifier); + if (!reply) + return NULL; + + if (error && + !radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, error)) { + radius_msg_free(reply); + return NULL; + } + + return reply; +} + + static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct radius_das_data *das = eloop_ctx; @@ -219,7 +413,8 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d", len, abuf, from_port); - if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) { + if (das->client_addr.u.v4.s_addr && + das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) { wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client"); return; } @@ -270,19 +465,7 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) reply = radius_das_disconnect(das, msg, abuf, from_port); break; case RADIUS_CODE_COA_REQUEST: - /* TODO */ - reply = radius_msg_new(RADIUS_CODE_COA_NAK, - hdr->identifier); - if (reply == NULL) - break; - - /* Unsupported Service */ - if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, - 405)) { - radius_msg_free(reply); - reply = NULL; - break; - } + reply = radius_das_coa(das, msg, abuf, from_port); break; default: wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in " @@ -369,17 +552,17 @@ radius_das_init(struct radius_das_conf *conf) conf->require_message_authenticator; das->ctx = conf->ctx; das->disconnect = conf->disconnect; + das->coa = conf->coa; os_memcpy(&das->client_addr, conf->client_addr, sizeof(das->client_addr)); - das->shared_secret = os_malloc(conf->shared_secret_len); + das->shared_secret = os_memdup(conf->shared_secret, + conf->shared_secret_len); if (das->shared_secret == NULL) { radius_das_deinit(das); return NULL; } - os_memcpy(das->shared_secret, conf->shared_secret, - conf->shared_secret_len); das->shared_secret_len = conf->shared_secret_len; das->sock = radius_das_open_socket(conf->port); diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h index 9863fdc1eaca0..233d662f631b0 100644 --- a/src/radius/radius_das.h +++ b/src/radius/radius_das.h @@ -16,6 +16,7 @@ enum radius_das_res { RADIUS_DAS_NAS_MISMATCH, RADIUS_DAS_SESSION_NOT_FOUND, RADIUS_DAS_MULTI_SESSION_MATCH, + RADIUS_DAS_COA_FAILED, }; struct radius_das_attrs { @@ -35,6 +36,9 @@ struct radius_das_attrs { size_t acct_multi_session_id_len; const u8 *cui; size_t cui_len; + + /* Authorization changes */ + const u8 *hs20_t_c_filtering; }; struct radius_das_conf { @@ -48,6 +52,7 @@ struct radius_das_conf { void *ctx; enum radius_das_res (*disconnect)(void *ctx, struct radius_das_attrs *attr); + enum radius_das_res (*coa)(void *ctx, struct radius_das_attrs *attr); }; struct radius_das_data * diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c index 744283c7dc9d7..e3afc0d532981 100644 --- a/src/radius/radius_server.c +++ b/src/radius/radius_server.c @@ -26,9 +26,14 @@ #define RADIUS_SESSION_TIMEOUT 60 /** + * RADIUS_SESSION_MAINTAIN - Completed session expiration timeout in seconds + */ +#define RADIUS_SESSION_MAINTAIN 5 + +/** * RADIUS_MAX_SESSION - Maximum number of active sessions */ -#define RADIUS_MAX_SESSION 100 +#define RADIUS_MAX_SESSION 1000 /** * RADIUS_MAX_MSG_LEN - Maximum message length for incoming RADIUS messages @@ -75,6 +80,7 @@ struct radius_session { struct eap_eapol_interface *eap_if; char *username; /* from User-Name attribute */ char *nas_ip; + u8 mac_addr[ETH_ALEN]; /* from Calling-Station-Id attribute */ struct radius_msg *last_msg; char *last_from_addr; @@ -87,8 +93,11 @@ struct radius_session { unsigned int remediation:1; unsigned int macacl:1; + unsigned int t_c_filtering:1; struct hostapd_radius_attr *accept_attr; + + u32 t_c_timestamp; /* Last read T&C timestamp from user DB */ }; /** @@ -106,6 +115,14 @@ struct radius_client { int shared_secret_len; struct radius_session *sessions; struct radius_server_counters counters; + + u8 next_dac_identifier; + struct radius_msg *pending_dac_coa_req; + u8 pending_dac_coa_id; + u8 pending_dac_coa_addr[ETH_ALEN]; + struct radius_msg *pending_dac_disconnect_req; + u8 pending_dac_disconnect_id; + u8 pending_dac_disconnect_addr[ETH_ALEN]; }; /** @@ -267,6 +284,8 @@ struct radius_server_data { unsigned int tls_session_lifetime; + unsigned int tls_flags; + /** * wps - Wi-Fi Protected Setup context * @@ -339,6 +358,8 @@ struct radius_server_data { char *subscr_remediation_url; u8 subscr_remediation_method; + char *t_c_server_url; + #ifdef CONFIG_SQLITE sqlite3 *db; #endif /* CONFIG_SQLITE */ @@ -621,8 +642,8 @@ radius_server_get_new_session(struct radius_server_data *data, struct radius_client *client, struct radius_msg *msg, const char *from_addr) { - u8 *user; - size_t user_len; + u8 *user, *id; + size_t user_len, id_len; int res; struct radius_session *sess; struct eap_config eap_conf; @@ -657,17 +678,32 @@ radius_server_get_new_session(struct radius_server_data *data, sess->username = os_malloc(user_len * 4 + 1); if (sess->username == NULL) { - radius_server_session_free(data, sess); + radius_server_session_remove(data, sess); return NULL; } printf_encode(sess->username, user_len * 4 + 1, user, user_len); sess->nas_ip = os_strdup(from_addr); if (sess->nas_ip == NULL) { - radius_server_session_free(data, sess); + radius_server_session_remove(data, sess); return NULL; } + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID, &id, + &id_len, NULL) == 0) { + char buf[3 * ETH_ALEN]; + + os_memset(buf, 0, sizeof(buf)); + if (id_len >= sizeof(buf)) + id_len = sizeof(buf) - 1; + os_memcpy(buf, id, id_len); + if (hwaddr_aton2(buf, sess->mac_addr) < 0) + os_memset(sess->mac_addr, 0, ETH_ALEN); + else + RADIUS_DEBUG("Calling-Station-Id: " MACSTR, + MAC2STR(sess->mac_addr)); + } + srv_log(sess, "New session created"); os_memset(&eap_conf, 0, sizeof(eap_conf)); @@ -691,13 +727,14 @@ radius_server_get_new_session(struct radius_server_data *data, eap_conf.server_id_len = os_strlen(data->server_id); eap_conf.erp = data->erp; eap_conf.tls_session_lifetime = data->tls_session_lifetime; + eap_conf.tls_flags = data->tls_flags; radius_server_testing_options(sess, &eap_conf); sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb, &eap_conf); if (sess->eap == NULL) { RADIUS_DEBUG("Failed to initialize EAP state machine for the " "new session"); - radius_server_session_free(data, sess); + radius_server_session_remove(data, sess); return NULL; } sess->eap_if = eap_get_interface(sess->eap); @@ -710,6 +747,125 @@ radius_server_get_new_session(struct radius_server_data *data, } +#ifdef CONFIG_HS20 +static void radius_srv_hs20_t_c_pending(struct radius_session *sess) +{ +#ifdef CONFIG_SQLITE + char *sql; + char addr[3 * ETH_ALEN], *id_str; + const u8 *id; + size_t id_len; + + if (!sess->server->db || !sess->eap || + is_zero_ether_addr(sess->mac_addr)) + return; + + os_snprintf(addr, sizeof(addr), MACSTR, MAC2STR(sess->mac_addr)); + + id = eap_get_identity(sess->eap, &id_len); + if (!id) + return; + id_str = os_malloc(id_len + 1); + if (!id_str) + return; + os_memcpy(id_str, id, id_len); + id_str[id_len] = '\0'; + + sql = sqlite3_mprintf("INSERT OR REPLACE INTO pending_tc (mac_addr,identity) VALUES (%Q,%Q)", + addr, id_str); + os_free(id_str); + if (!sql) + return; + + if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) != + SQLITE_OK) { + RADIUS_ERROR("Failed to add pending_tc entry into sqlite database: %s", + sqlite3_errmsg(sess->server->db)); + } + sqlite3_free(sql); +#endif /* CONFIG_SQLITE */ +} +#endif /* CONFIG_HS20 */ + + +static void radius_server_add_session(struct radius_session *sess) +{ +#ifdef CONFIG_SQLITE + char *sql; + char addr_txt[ETH_ALEN * 3]; + struct os_time now; + + if (!sess->server->db) + return; + + + os_snprintf(addr_txt, sizeof(addr_txt), MACSTR, + MAC2STR(sess->mac_addr)); + + os_get_time(&now); + sql = sqlite3_mprintf("INSERT OR REPLACE INTO current_sessions(mac_addr,identity,start_time,nas,hs20_t_c_filtering) VALUES (%Q,%Q,%d,%Q,%u)", + addr_txt, sess->username, now.sec, + sess->nas_ip, sess->t_c_filtering); + if (sql) { + if (sqlite3_exec(sess->server->db, sql, NULL, NULL, + NULL) != SQLITE_OK) { + RADIUS_ERROR("Failed to add current_sessions entry into sqlite database: %s", + sqlite3_errmsg(sess->server->db)); + } + sqlite3_free(sql); + } +#endif /* CONFIG_SQLITE */ +} + + +static void db_update_last_msk(struct radius_session *sess, const char *msk) +{ +#ifdef CONFIG_RADIUS_TEST +#ifdef CONFIG_SQLITE + char *sql = NULL; + char *id_str = NULL; + const u8 *id; + size_t id_len; + const char *serial_num; + + if (!sess->server->db) + return; + + serial_num = eap_get_serial_num(sess->eap); + if (serial_num) { + id_len = 5 + os_strlen(serial_num) + 1; + id_str = os_malloc(id_len); + if (!id_str) + return; + os_snprintf(id_str, id_len, "cert-%s", serial_num); + } else { + id = eap_get_identity(sess->eap, &id_len); + if (!id) + return; + id_str = os_malloc(id_len + 1); + if (!id_str) + return; + os_memcpy(id_str, id, id_len); + id_str[id_len] = '\0'; + } + + sql = sqlite3_mprintf("UPDATE users SET last_msk=%Q WHERE identity=%Q", + msk, id_str); + os_free(id_str); + if (!sql) + return; + + if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) != + SQLITE_OK) { + RADIUS_DEBUG("Failed to update last_msk: %s", + sqlite3_errmsg(sess->server->db)); + } + sqlite3_free(sql); +#endif /* CONFIG_SQLITE */ +#endif /* CONFIG_RADIUS_TEST */ +} + + static struct radius_msg * radius_server_encapsulate_eap(struct radius_server_data *data, struct radius_client *client, @@ -720,6 +876,7 @@ radius_server_encapsulate_eap(struct radius_server_data *data, int code; unsigned int sess_id; struct radius_hdr *hdr = radius_msg_get_hdr(request); + u16 reason = WLAN_REASON_IEEE_802_1X_AUTH_FAILED; if (sess->eap_if->eapFail) { sess->eap_if->eapFail = FALSE; @@ -754,9 +911,18 @@ radius_server_encapsulate_eap(struct radius_server_data *data, if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) { int len; #ifdef CONFIG_RADIUS_TEST + char buf[2 * 64 + 1]; + + len = sess->eap_if->eapKeyDataLen; + if (len > 64) + len = 64; + len = wpa_snprintf_hex(buf, sizeof(buf), + sess->eap_if->eapKeyData, len); + buf[len] = '\0'; + if (data->dump_msk_file) { FILE *f; - char buf[2 * 64 + 1]; + f = fopen(data->dump_msk_file, "a"); if (f) { len = sess->eap_if->eapKeyDataLen; @@ -770,6 +936,8 @@ radius_server_encapsulate_eap(struct radius_server_data *data, fclose(f); } } + + db_update_last_msk(sess, buf); #endif /* CONFIG_RADIUS_TEST */ if (sess->eap_if->eapKeyDataLen > 64) { len = 32; @@ -812,6 +980,61 @@ radius_server_encapsulate_eap(struct radius_server_data *data, RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem"); } } + + if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->t_c_filtering) { + u8 buf[4] = { 0x01, 0x00, 0x00, 0x00 }; /* E=1 */ + const char *url = data->t_c_server_url, *pos; + char *url2, *end2, *pos2; + size_t url_len; + + if (!radius_msg_add_wfa( + msg, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING, + buf, sizeof(buf))) { + RADIUS_DEBUG("Failed to add WFA-HS20-T-C-Filtering"); + radius_msg_free(msg); + return NULL; + } + + if (!url) { + RADIUS_DEBUG("No t_c_server_url configured"); + radius_msg_free(msg); + return NULL; + } + + pos = os_strstr(url, "@1@"); + if (!pos) { + RADIUS_DEBUG("No @1@ macro in t_c_server_url"); + radius_msg_free(msg); + return NULL; + } + + url_len = os_strlen(url) + ETH_ALEN * 3 - 1 - 3; + url2 = os_malloc(url_len + 1); + if (!url2) { + RADIUS_DEBUG("Failed to allocate room for T&C Server URL"); + os_free(url2); + radius_msg_free(msg); + return NULL; + } + pos2 = url2; + end2 = url2 + url_len + 1; + os_memcpy(pos2, url, pos - url); + pos2 += pos - url; + os_snprintf(pos2, end2 - pos2, MACSTR, MAC2STR(sess->mac_addr)); + pos2 += ETH_ALEN * 3 - 1; + os_memcpy(pos2, pos + 3, os_strlen(pos + 3)); + if (!radius_msg_add_wfa(msg, + RADIUS_VENDOR_ATTR_WFA_HS20_T_C_URL, + (const u8 *) url2, url_len)) { + RADIUS_DEBUG("Failed to add WFA-HS20-T-C-URL"); + os_free(url2); + radius_msg_free(msg); + return NULL; + } + os_free(url2); + + radius_srv_hs20_t_c_pending(sess); + } #endif /* CONFIG_HS20 */ if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) { @@ -833,12 +1056,24 @@ radius_server_encapsulate_eap(struct radius_server_data *data, } } + if (code == RADIUS_CODE_ACCESS_REJECT) { + if (radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_REASON_CODE, + reason) < 0) { + RADIUS_DEBUG("Failed to add WLAN-Reason-Code attribute"); + radius_msg_free(msg); + return NULL; + } + } + if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, client->shared_secret_len, hdr->authenticator) < 0) { RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); } + if (code == RADIUS_CODE_ACCESS_ACCEPT) + radius_server_add_session(sess); + return msg; } @@ -985,6 +1220,51 @@ static int radius_server_reject(struct radius_server_data *data, } +static void radius_server_hs20_t_c_check(struct radius_session *sess, + struct radius_msg *msg) +{ +#ifdef CONFIG_HS20 + u8 *buf, *pos, *end, type, sublen, *timestamp = NULL; + size_t len; + + buf = NULL; + for (;;) { + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, + &buf, &len, buf) < 0) + break; + if (len < 6) + continue; + pos = buf; + end = buf + len; + if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA) + continue; + pos += 4; + + type = *pos++; + sublen = *pos++; + if (sublen < 2) + continue; /* invalid length */ + sublen -= 2; /* skip header */ + if (pos + sublen > end) + continue; /* invalid WFA VSA */ + + if (type == RADIUS_VENDOR_ATTR_WFA_HS20_TIMESTAMP && len >= 4) { + timestamp = pos; + break; + } + } + + if (!timestamp) + return; + RADIUS_DEBUG("HS20-Timestamp: %u", WPA_GET_BE32(timestamp)); + if (sess->t_c_timestamp != WPA_GET_BE32(timestamp)) { + RADIUS_DEBUG("Last read T&C timestamp does not match HS20-Timestamp --> require filtering"); + sess->t_c_filtering = 1; + } +#endif /* CONFIG_HS20 */ +} + + static int radius_server_request(struct radius_server_data *data, struct radius_msg *msg, struct sockaddr *from, socklen_t fromlen, @@ -1057,7 +1337,7 @@ static int radius_server_request(struct radius_server_data *data, "message"); return -1; } - + eap = radius_msg_get_eap(msg); if (eap == NULL && sess->macacl) { reply = radius_server_macacl(data, client, sess, msg); @@ -1115,10 +1395,15 @@ static int radius_server_request(struct radius_server_data *data, if (sess->eap_if->eapSuccess || sess->eap_if->eapFail) is_complete = 1; - if (sess->eap_if->eapFail) + if (sess->eap_if->eapFail) { srv_log(sess, "EAP authentication failed"); - else if (sess->eap_if->eapSuccess) + db_update_last_msk(sess, "FAIL"); + } else if (sess->eap_if->eapSuccess) { srv_log(sess, "EAP authentication succeeded"); + } + + if (sess->eap_if->eapSuccess) + radius_server_hs20_t_c_check(sess, msg); reply = radius_server_encapsulate_eap(data, client, sess, msg); @@ -1172,7 +1457,7 @@ send_reply: sess->sess_id); eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess); - eloop_register_timeout(10, 0, + eloop_register_timeout(RADIUS_SESSION_MAINTAIN, 0, radius_server_session_remove_timeout, data, sess); } @@ -1181,6 +1466,116 @@ send_reply: } +static void +radius_server_receive_disconnect_resp(struct radius_server_data *data, + struct radius_client *client, + struct radius_msg *msg, int ack) +{ + struct radius_hdr *hdr; + + if (!client->pending_dac_disconnect_req) { + RADIUS_DEBUG("Ignore unexpected Disconnect response"); + radius_msg_free(msg); + return; + } + + hdr = radius_msg_get_hdr(msg); + if (hdr->identifier != client->pending_dac_disconnect_id) { + RADIUS_DEBUG("Ignore unexpected Disconnect response with unexpected identifier %u (expected %u)", + hdr->identifier, + client->pending_dac_disconnect_id); + radius_msg_free(msg); + return; + } + + if (radius_msg_verify(msg, (const u8 *) client->shared_secret, + client->shared_secret_len, + client->pending_dac_disconnect_req, 0)) { + RADIUS_DEBUG("Ignore Disconnect response with invalid authenticator"); + radius_msg_free(msg); + return; + } + + RADIUS_DEBUG("Disconnect-%s received for " MACSTR, + ack ? "ACK" : "NAK", + MAC2STR(client->pending_dac_disconnect_addr)); + + radius_msg_free(msg); + radius_msg_free(client->pending_dac_disconnect_req); + client->pending_dac_disconnect_req = NULL; +} + + +static void radius_server_receive_coa_resp(struct radius_server_data *data, + struct radius_client *client, + struct radius_msg *msg, int ack) +{ + struct radius_hdr *hdr; +#ifdef CONFIG_SQLITE + char addrtxt[3 * ETH_ALEN]; + char *sql; + int res; +#endif /* CONFIG_SQLITE */ + + if (!client->pending_dac_coa_req) { + RADIUS_DEBUG("Ignore unexpected CoA response"); + radius_msg_free(msg); + return; + } + + hdr = radius_msg_get_hdr(msg); + if (hdr->identifier != client->pending_dac_coa_id) { + RADIUS_DEBUG("Ignore unexpected CoA response with unexpected identifier %u (expected %u)", + hdr->identifier, + client->pending_dac_coa_id); + radius_msg_free(msg); + return; + } + + if (radius_msg_verify(msg, (const u8 *) client->shared_secret, + client->shared_secret_len, + client->pending_dac_coa_req, 0)) { + RADIUS_DEBUG("Ignore CoA response with invalid authenticator"); + radius_msg_free(msg); + return; + } + + RADIUS_DEBUG("CoA-%s received for " MACSTR, + ack ? "ACK" : "NAK", + MAC2STR(client->pending_dac_coa_addr)); + + radius_msg_free(msg); + radius_msg_free(client->pending_dac_coa_req); + client->pending_dac_coa_req = NULL; + +#ifdef CONFIG_SQLITE + if (!data->db) + return; + + os_snprintf(addrtxt, sizeof(addrtxt), MACSTR, + MAC2STR(client->pending_dac_coa_addr)); + + if (ack) { + sql = sqlite3_mprintf("UPDATE current_sessions SET hs20_t_c_filtering=0, waiting_coa_ack=0, coa_ack_received=1 WHERE mac_addr=%Q", + addrtxt); + } else { + sql = sqlite3_mprintf("UPDATE current_sessions SET waiting_coa_ack=0 WHERE mac_addr=%Q", + addrtxt); + } + if (!sql) + return; + + res = sqlite3_exec(data->db, sql, NULL, NULL, NULL); + sqlite3_free(sql); + if (res != SQLITE_OK) { + RADIUS_ERROR("Failed to update current_sessions entry: %s", + sqlite3_errmsg(data->db)); + return; + } +#endif /* CONFIG_SQLITE */ +} + + static void radius_server_receive_auth(int sock, void *eloop_ctx, void *sock_ctx) { @@ -1261,6 +1656,26 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, radius_msg_dump(msg); } + if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_DISCONNECT_ACK) { + radius_server_receive_disconnect_resp(data, client, msg, 1); + return; + } + + if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_DISCONNECT_NAK) { + radius_server_receive_disconnect_resp(data, client, msg, 0); + return; + } + + if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_COA_ACK) { + radius_server_receive_coa_resp(data, client, msg, 1); + return; + } + + if (radius_msg_get_hdr(msg)->code == RADIUS_CODE_COA_NAK) { + radius_server_receive_coa_resp(data, client, msg, 0); + return; + } + if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCESS_REQUEST) { RADIUS_DEBUG("Unexpected RADIUS code %d", radius_msg_get_hdr(msg)->code); @@ -1521,6 +1936,8 @@ static void radius_server_free_clients(struct radius_server_data *data, radius_server_free_sessions(data, prev->sessions); os_free(prev->shared_secret); + radius_msg_free(prev->pending_dac_coa_req); + radius_msg_free(prev->pending_dac_disconnect_req); os_free(prev); } } @@ -1749,6 +2166,7 @@ radius_server_init(struct radius_server_conf *conf) data->erp = conf->erp; data->erp_domain = conf->erp_domain; data->tls_session_lifetime = conf->tls_session_lifetime; + data->tls_flags = conf->tls_flags; if (conf->subscr_remediation_url) { data->subscr_remediation_url = @@ -1756,6 +2174,9 @@ radius_server_init(struct radius_server_conf *conf) } data->subscr_remediation_method = conf->subscr_remediation_method; + if (conf->t_c_server_url) + data->t_c_server_url = os_strdup(conf->t_c_server_url); + #ifdef CONFIG_SQLITE if (conf->sqlite_file) { if (sqlite3_open(conf->sqlite_file, &data->db)) { @@ -1872,6 +2293,7 @@ void radius_server_deinit(struct radius_server_data *data) os_free(data->dump_msk_file); #endif /* CONFIG_RADIUS_TEST */ os_free(data->subscr_remediation_url); + os_free(data->t_c_server_url); #ifdef CONFIG_SQLITE if (data->db) @@ -2040,6 +2462,7 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity, sess->accept_attr = user->accept_attr; sess->remediation = user->remediation; sess->macacl = user->macacl; + sess->t_c_timestamp = user->t_c_timestamp; } if (ret) { @@ -2166,3 +2589,212 @@ void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx) radius_msg_free(msg); } + + +#ifdef CONFIG_SQLITE + +struct db_session_fields { + char *identity; + char *nas; + int hs20_t_c_filtering; + int waiting_coa_ack; + int coa_ack_received; +}; + + +static int get_db_session_fields(void *ctx, int argc, char *argv[], char *col[]) +{ + struct db_session_fields *fields = ctx; + int i; + + for (i = 0; i < argc; i++) { + if (!argv[i]) + continue; + + RADIUS_DEBUG("Session DB: %s=%s", col[i], argv[i]); + + if (os_strcmp(col[i], "identity") == 0) { + os_free(fields->identity); + fields->identity = os_strdup(argv[i]); + } else if (os_strcmp(col[i], "nas") == 0) { + os_free(fields->nas); + fields->nas = os_strdup(argv[i]); + } else if (os_strcmp(col[i], "hs20_t_c_filtering") == 0) { + fields->hs20_t_c_filtering = atoi(argv[i]); + } else if (os_strcmp(col[i], "waiting_coa_ack") == 0) { + fields->waiting_coa_ack = atoi(argv[i]); + } else if (os_strcmp(col[i], "coa_ack_received") == 0) { + fields->coa_ack_received = atoi(argv[i]); + } + } + + return 0; +} + + +static void free_db_session_fields(struct db_session_fields *fields) +{ + os_free(fields->identity); + fields->identity = NULL; + os_free(fields->nas); + fields->nas = NULL; +} + +#endif /* CONFIG_SQLITE */ + + +int radius_server_dac_request(struct radius_server_data *data, const char *req) +{ +#ifdef CONFIG_SQLITE + char *sql; + int res; + int disconnect; + const char *pos = req; + u8 addr[ETH_ALEN]; + char addrtxt[3 * ETH_ALEN]; + int t_c_clear = 0; + struct db_session_fields fields; + struct sockaddr_in das; + struct radius_client *client; + struct radius_msg *msg; + struct wpabuf *buf; + u8 identifier; + struct os_time now; + + if (!data) + return -1; + + /* req: <disconnect|coa> <MAC Address> [t_c_clear] */ + + if (os_strncmp(pos, "disconnect ", 11) == 0) { + disconnect = 1; + pos += 11; + } else if (os_strncmp(req, "coa ", 4) == 0) { + disconnect = 0; + pos += 4; + } else { + return -1; + } + + if (hwaddr_aton(pos, addr)) + return -1; + pos = os_strchr(pos, ' '); + if (pos) { + if (os_strstr(pos, "t_c_clear")) + t_c_clear = 1; + } + + if (!disconnect && !t_c_clear) { + RADIUS_ERROR("DAC request for CoA without any authorization change"); + return -1; + } + + if (!data->db) { + RADIUS_ERROR("SQLite database not in use"); + return -1; + } + + os_snprintf(addrtxt, sizeof(addrtxt), MACSTR, MAC2STR(addr)); + + sql = sqlite3_mprintf("SELECT * FROM current_sessions WHERE mac_addr=%Q", + addrtxt); + if (!sql) + return -1; + + os_memset(&fields, 0, sizeof(fields)); + res = sqlite3_exec(data->db, sql, get_db_session_fields, &fields, NULL); + sqlite3_free(sql); + if (res != SQLITE_OK) { + RADIUS_ERROR("Failed to find matching current_sessions entry from sqlite database: %s", + sqlite3_errmsg(data->db)); + free_db_session_fields(&fields); + return -1; + } + + if (!fields.nas) { + RADIUS_ERROR("No NAS information found from current_sessions"); + free_db_session_fields(&fields); + return -1; + } + + os_memset(&das, 0, sizeof(das)); + das.sin_family = AF_INET; + das.sin_addr.s_addr = inet_addr(fields.nas); + das.sin_port = htons(3799); + + free_db_session_fields(&fields); + + client = radius_server_get_client(data, &das.sin_addr, 0); + if (!client) { + RADIUS_ERROR("No NAS information available to protect the packet"); + return -1; + } + + identifier = client->next_dac_identifier++; + + msg = radius_msg_new(disconnect ? RADIUS_CODE_DISCONNECT_REQUEST : + RADIUS_CODE_COA_REQUEST, identifier); + if (!msg) + return -1; + + os_snprintf(addrtxt, sizeof(addrtxt), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(addr)); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, + (u8 *) addrtxt, os_strlen(addrtxt))) { + RADIUS_ERROR("Could not add Calling-Station-Id"); + radius_msg_free(msg); + return -1; + } + + if (!disconnect && t_c_clear) { + u8 val[4] = { 0x00, 0x00, 0x00, 0x00 }; /* E=0 */ + + if (!radius_msg_add_wfa( + msg, RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING, + val, sizeof(val))) { + RADIUS_DEBUG("Failed to add WFA-HS20-T-C-Filtering"); + radius_msg_free(msg); + return -1; + } + } + + os_get_time(&now); + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, + now.sec)) { + RADIUS_ERROR("Failed to add Event-Timestamp attribute"); + radius_msg_free(msg); + return -1; + } + + radius_msg_finish_acct(msg, (u8 *) client->shared_secret, + client->shared_secret_len); + + if (wpa_debug_level <= MSG_MSGDUMP) + radius_msg_dump(msg); + + buf = radius_msg_get_buf(msg); + if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0, + (struct sockaddr *) &das, sizeof(das)) < 0) { + RADIUS_ERROR("Failed to send packet - sendto: %s", + strerror(errno)); + radius_msg_free(msg); + return -1; + } + + if (disconnect) { + radius_msg_free(client->pending_dac_disconnect_req); + client->pending_dac_disconnect_req = msg; + client->pending_dac_disconnect_id = identifier; + os_memcpy(client->pending_dac_disconnect_addr, addr, ETH_ALEN); + } else { + radius_msg_free(client->pending_dac_coa_req); + client->pending_dac_coa_req = msg; + client->pending_dac_coa_id = identifier; + os_memcpy(client->pending_dac_coa_addr, addr, ETH_ALEN); + } + + return 0; +#else /* CONFIG_SQLITE */ + return -1; +#endif /* CONFIG_SQLITE */ +} diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h index 7a25802c81527..167bbf5b28813 100644 --- a/src/radius/radius_server.h +++ b/src/radius/radius_server.h @@ -172,6 +172,8 @@ struct radius_server_conf { unsigned int tls_session_lifetime; + unsigned int tls_flags; + /** * wps - Wi-Fi Protected Setup context * @@ -231,6 +233,8 @@ struct radius_server_conf { char *subscr_remediation_url; u8 subscr_remediation_method; + + char *t_c_server_url; }; @@ -244,5 +248,6 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, size_t buflen); void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx); +int radius_server_dac_request(struct radius_server_data *data, const char *req); #endif /* RADIUS_SERVER_H */ diff --git a/src/rsn_supp/Makefile b/src/rsn_supp/Makefile index d5e61fe72dc8f..c2d81f279195d 100644 --- a/src/rsn_supp/Makefile +++ b/src/rsn_supp/Makefile @@ -10,7 +10,6 @@ include ../lib.rules CFLAGS += -DCONFIG_IEEE80211W CFLAGS += -DCONFIG_IEEE80211R -CFLAGS += -DCONFIG_PEERKEY CFLAGS += -DCONFIG_TDLS CFLAGS += -DCONFIG_WNM CFLAGS += -DIEEE8021X_EAPOL @@ -18,7 +17,6 @@ CFLAGS += -DIEEE8021X_EAPOL LIB_OBJS= \ pmksa_cache.o \ wpa_ft.o \ - peerkey.o \ tdls.o \ preauth.o \ wpa.o \ diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c deleted file mode 100644 index 79764d94b902c..0000000000000 --- a/src/rsn_supp/peerkey.c +++ /dev/null @@ -1,1155 +0,0 @@ -/* - * WPA Supplicant - PeerKey for Direct Link Setup (DLS) - * Copyright (c) 2006-2015, 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" - -#ifdef CONFIG_PEERKEY - -#include "common.h" -#include "eloop.h" -#include "crypto/sha1.h" -#include "crypto/sha256.h" -#include "crypto/random.h" -#include "common/ieee802_11_defs.h" -#include "wpa.h" -#include "wpa_i.h" -#include "wpa_ie.h" -#include "peerkey.h" - - -static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len) -{ - os_memcpy(pos, ie, ie_len); - return pos + ie_len; -} - - -static u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len) -{ - *pos++ = WLAN_EID_VENDOR_SPECIFIC; - *pos++ = RSN_SELECTOR_LEN + data_len; - RSN_SELECTOR_PUT(pos, kde); - pos += RSN_SELECTOR_LEN; - os_memcpy(pos, data, data_len); - pos += data_len; - return pos; -} - - -static void wpa_supplicant_smk_timeout(void *eloop_ctx, void *timeout_ctx) -{ -#if 0 - struct wpa_sm *sm = eloop_ctx; - struct wpa_peerkey *peerkey = timeout_ctx; -#endif - /* TODO: time out SMK and any STK that was generated using this SMK */ -} - - -static void wpa_supplicant_peerkey_free(struct wpa_sm *sm, - struct wpa_peerkey *peerkey) -{ - eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey); - os_free(peerkey); -} - - -static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst, - const u8 *peer, - u16 mui, u16 error_type, int ver) -{ - size_t rlen; - struct wpa_eapol_key *err; - struct wpa_eapol_key_192 *err192; - struct rsn_error_kde error; - u8 *rbuf, *pos; - size_t kde_len; - u16 key_info; - - kde_len = 2 + RSN_SELECTOR_LEN + sizeof(error); - if (peer) - kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN; - - rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, - NULL, sizeof(*err) + kde_len, &rlen, - (void *) &err); - if (rbuf == NULL) - return -1; - err192 = (struct wpa_eapol_key_192 *) err; - - err->type = EAPOL_KEY_TYPE_RSN; - key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_ERROR | - WPA_KEY_INFO_REQUEST; - WPA_PUT_BE16(err->key_info, key_info); - WPA_PUT_BE16(err->key_length, 0); - os_memcpy(err->replay_counter, sm->request_counter, - WPA_REPLAY_COUNTER_LEN); - inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); - - WPA_PUT_BE16(err->key_data_length, (u16) kde_len); - pos = (u8 *) (err + 1); - - if (peer) { - /* Peer MAC Address KDE */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN); - } - - /* Error KDE */ - error.mui = host_to_be16(mui); - error.error_type = host_to_be16(error_type); - wpa_add_kde(pos, RSN_KEY_DATA_ERROR, (u8 *) &error, sizeof(error)); - - if (peer) { - wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error (peer " - MACSTR " mui %d error_type %d)", - MAC2STR(peer), mui, error_type); - } else { - wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error " - "(mui %d error_type %d)", mui, error_type); - } - - wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, dst, - ETH_P_EAPOL, rbuf, rlen, err192->key_mic); - - return 0; -} - - -static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm, - const unsigned char *src_addr, - const struct wpa_eapol_key *key, - int ver, struct wpa_peerkey *peerkey) -{ - size_t rlen; - struct wpa_eapol_key *reply; - struct wpa_eapol_key_192 *reply192; - u8 *rbuf, *pos; - size_t kde_len; - u16 key_info; - - /* KDEs: Peer RSN IE, Initiator MAC Address, Initiator Nonce */ - kde_len = peerkey->rsnie_p_len + - 2 + RSN_SELECTOR_LEN + ETH_ALEN + - 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN; - - rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, - NULL, sizeof(*reply) + kde_len, &rlen, - (void *) &reply); - if (rbuf == NULL) - return -1; - reply192 = (struct wpa_eapol_key_192 *) reply; - - reply->type = EAPOL_KEY_TYPE_RSN; - key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_SECURE; - WPA_PUT_BE16(reply->key_info, key_info); - WPA_PUT_BE16(reply->key_length, 0); - os_memcpy(reply->replay_counter, key->replay_counter, - WPA_REPLAY_COUNTER_LEN); - - os_memcpy(reply->key_nonce, peerkey->pnonce, WPA_NONCE_LEN); - - WPA_PUT_BE16(reply->key_data_length, (u16) kde_len); - pos = (u8 *) (reply + 1); - - /* Peer RSN IE */ - pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len); - - /* Initiator MAC Address KDE */ - pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peerkey->addr, ETH_ALEN); - - /* Initiator Nonce */ - wpa_add_kde(pos, RSN_KEY_DATA_NONCE, peerkey->inonce, WPA_NONCE_LEN); - - wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK M3"); - wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, src_addr, - ETH_P_EAPOL, rbuf, rlen, reply192->key_mic); - - return 0; -} - - -static int wpa_supplicant_process_smk_m2( - struct wpa_sm *sm, const unsigned char *src_addr, - const struct wpa_eapol_key *key, size_t extra_len, int ver) -{ - struct wpa_peerkey *peerkey; - struct wpa_eapol_ie_parse kde; - struct wpa_ie_data ie; - int cipher; - struct rsn_ie_hdr *hdr; - u8 *pos; - - wpa_printf(MSG_DEBUG, "RSN: Received SMK M2"); - - if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { - wpa_printf(MSG_INFO, "RSN: SMK handshake not allowed for " - "the current network"); - return -1; - } - - if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < - 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M2"); - return -1; - } - - if (kde.rsn_ie == NULL || kde.mac_addr == NULL || - kde.mac_addr_len < ETH_ALEN) { - wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " - "SMK M2"); - return -1; - } - - wpa_printf(MSG_DEBUG, "RSN: SMK M2 - SMK initiator " MACSTR, - MAC2STR(kde.mac_addr)); - - if (kde.rsn_ie_len > PEERKEY_MAX_IE_LEN) { - wpa_printf(MSG_INFO, "RSN: Too long Initiator RSN IE in SMK " - "M2"); - return -1; - } - - if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse RSN IE in SMK M2"); - return -1; - } - - cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher & - sm->allowed_pairwise_cipher, 0); - if (cipher < 0) { - wpa_printf(MSG_INFO, "RSN: No acceptable cipher in SMK M2"); - wpa_supplicant_send_smk_error(sm, src_addr, kde.mac_addr, - STK_MUI_SMK, STK_ERR_CPHR_NS, - ver); - return -1; - } - wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey", - wpa_cipher_txt(cipher)); - - /* TODO: find existing entry and if found, use that instead of adding - * a new one; how to handle the case where both ends initiate at the - * same time? */ - peerkey = os_zalloc(sizeof(*peerkey)); - if (peerkey == NULL) - return -1; - os_memcpy(peerkey->addr, kde.mac_addr, ETH_ALEN); - os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN); - os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len); - peerkey->rsnie_i_len = kde.rsn_ie_len; - peerkey->cipher = cipher; - peerkey->akmp = ie.key_mgmt; - - if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) { - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "WPA: Failed to get random data for PNonce"); - wpa_supplicant_peerkey_free(sm, peerkey); - return -1; - } - - hdr = (struct rsn_ie_hdr *) peerkey->rsnie_p; - hdr->elem_id = WLAN_EID_RSN; - WPA_PUT_LE16(hdr->version, RSN_VERSION); - pos = (u8 *) (hdr + 1); - /* Group Suite can be anything for SMK RSN IE; receiver will just - * ignore it. */ - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - pos += RSN_SELECTOR_LEN; - /* Include only the selected cipher in pairwise cipher suite */ - WPA_PUT_LE16(pos, 1); - pos += 2; - RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, cipher)); - pos += RSN_SELECTOR_LEN; - - hdr->len = (pos - peerkey->rsnie_p) - 2; - peerkey->rsnie_p_len = pos - peerkey->rsnie_p; - wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake", - peerkey->rsnie_p, peerkey->rsnie_p_len); - - wpa_supplicant_send_smk_m3(sm, src_addr, key, ver, peerkey); - - peerkey->next = sm->peerkey; - sm->peerkey = peerkey; - - return 0; -} - - -/** - * rsn_smkid - Derive SMK identifier - * @smk: Station master key (32 bytes) - * @pnonce: Peer Nonce - * @mac_p: Peer MAC address - * @inonce: Initiator Nonce - * @mac_i: Initiator MAC address - * @akmp: Negotiated AKM - * - * 8.5.1.4 Station to station (STK) key hierarchy - * SMKID = HMAC-SHA1-128(SMK, "SMK Name" || PNonce || MAC_P || INonce || MAC_I) - */ -static void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p, - const u8 *inonce, const u8 *mac_i, u8 *smkid, - int akmp) -{ - char *title = "SMK Name"; - const u8 *addr[5]; - const size_t len[5] = { 8, WPA_NONCE_LEN, ETH_ALEN, WPA_NONCE_LEN, - ETH_ALEN }; - unsigned char hash[SHA256_MAC_LEN]; - - addr[0] = (u8 *) title; - addr[1] = pnonce; - addr[2] = mac_p; - addr[3] = inonce; - addr[4] = mac_i; - -#ifdef CONFIG_IEEE80211W - if (wpa_key_mgmt_sha256(akmp)) - hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash); - else -#endif /* CONFIG_IEEE80211W */ - hmac_sha1_vector(smk, PMK_LEN, 5, addr, len, hash); - os_memcpy(smkid, hash, PMKID_LEN); -} - - -static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, - struct wpa_peerkey *peerkey) -{ - size_t mlen; - struct wpa_eapol_key *msg; - u8 *mbuf; - size_t kde_len; - u16 key_info, ver; - - kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; - - mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, - sizeof(*msg) + kde_len, &mlen, - (void *) &msg); - if (mbuf == NULL) - return; - - msg->type = EAPOL_KEY_TYPE_RSN; - - if (peerkey->cipher != WPA_CIPHER_TKIP) - ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; - else - ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; - - key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK; - WPA_PUT_BE16(msg->key_info, key_info); - - if (peerkey->cipher != WPA_CIPHER_TKIP) - WPA_PUT_BE16(msg->key_length, 16); - else - WPA_PUT_BE16(msg->key_length, 32); - - os_memcpy(msg->replay_counter, peerkey->replay_counter, - WPA_REPLAY_COUNTER_LEN); - inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); - - WPA_PUT_BE16(msg->key_data_length, kde_len); - wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID, - peerkey->smkid, PMKID_LEN); - - if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) { - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "RSN: Failed to get random data for INonce (STK)"); - os_free(mbuf); - return; - } - wpa_hexdump(MSG_DEBUG, "RSN: INonce for STK 4-Way Handshake", - peerkey->inonce, WPA_NONCE_LEN); - os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN); - - wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR, - MAC2STR(peerkey->addr)); - wpa_eapol_key_send(sm, NULL, 0, ver, peerkey->addr, ETH_P_EAPOL, - mbuf, mlen, NULL); -} - - -static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm, - struct wpa_peerkey *peerkey) -{ - size_t mlen; - struct wpa_eapol_key *msg; - u8 *mbuf, *pos; - size_t kde_len; - u16 key_info, ver; - be32 lifetime; - - kde_len = peerkey->rsnie_i_len + - 2 + RSN_SELECTOR_LEN + sizeof(lifetime); - - mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, - sizeof(*msg) + kde_len, &mlen, - (void *) &msg); - if (mbuf == NULL) - return; - - msg->type = EAPOL_KEY_TYPE_RSN; - - if (peerkey->cipher != WPA_CIPHER_TKIP) - ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; - else - ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; - - key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK | - WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; - WPA_PUT_BE16(msg->key_info, key_info); - - if (peerkey->cipher != WPA_CIPHER_TKIP) - WPA_PUT_BE16(msg->key_length, 16); - else - WPA_PUT_BE16(msg->key_length, 32); - - os_memcpy(msg->replay_counter, peerkey->replay_counter, - WPA_REPLAY_COUNTER_LEN); - inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN); - - WPA_PUT_BE16(msg->key_data_length, kde_len); - pos = (u8 *) (msg + 1); - pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len); - lifetime = host_to_be32(peerkey->lifetime); - wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, - (u8 *) &lifetime, sizeof(lifetime)); - - os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN); - - wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR, - MAC2STR(peerkey->addr)); - wpa_eapol_key_send(sm, peerkey->stk.kck, peerkey->stk.kck_len, ver, - peerkey->addr, ETH_P_EAPOL, mbuf, mlen, - msg->key_mic); -} - - -static int wpa_supplicant_process_smk_m4(struct wpa_peerkey *peerkey, - struct wpa_eapol_ie_parse *kde) -{ - wpa_printf(MSG_DEBUG, "RSN: Received SMK M4 (Initiator " MACSTR ")", - MAC2STR(kde->mac_addr)); - - if (os_memcmp(kde->smk + PMK_LEN, peerkey->pnonce, WPA_NONCE_LEN) != 0) - { - wpa_printf(MSG_INFO, "RSN: PNonce in SMK KDE does not " - "match with the one used in SMK M3"); - return -1; - } - - if (os_memcmp(kde->nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) { - wpa_printf(MSG_INFO, "RSN: INonce in SMK M4 did not " - "match with the one received in SMK M2"); - return -1; - } - - return 0; -} - - -static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm, - const unsigned char *src_addr, - const struct wpa_eapol_key *key, - int ver, - struct wpa_peerkey *peerkey, - struct wpa_eapol_ie_parse *kde) -{ - int cipher; - struct wpa_ie_data ie; - - wpa_printf(MSG_DEBUG, "RSN: Received SMK M5 (Peer " MACSTR ")", - MAC2STR(kde->mac_addr)); - if (kde->rsn_ie == NULL || kde->rsn_ie_len > PEERKEY_MAX_IE_LEN || - wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0) { - wpa_printf(MSG_INFO, "RSN: No RSN IE in SMK M5"); - /* TODO: abort negotiation */ - return -1; - } - - if (os_memcmp(key->key_nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) { - wpa_printf(MSG_INFO, "RSN: Key Nonce in SMK M5 does " - "not match with INonce used in SMK M1"); - return -1; - } - - if (os_memcmp(kde->smk + PMK_LEN, peerkey->inonce, WPA_NONCE_LEN) != 0) - { - wpa_printf(MSG_INFO, "RSN: INonce in SMK KDE does not " - "match with the one used in SMK M1"); - return -1; - } - - os_memcpy(peerkey->rsnie_p, kde->rsn_ie, kde->rsn_ie_len); - peerkey->rsnie_p_len = kde->rsn_ie_len; - os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN); - - cipher = wpa_pick_pairwise_cipher(ie.pairwise_cipher & - sm->allowed_pairwise_cipher, 0); - if (cipher < 0) { - wpa_printf(MSG_INFO, "RSN: SMK Peer STA " MACSTR " selected " - "unacceptable cipher", MAC2STR(kde->mac_addr)); - wpa_supplicant_send_smk_error(sm, src_addr, kde->mac_addr, - STK_MUI_SMK, STK_ERR_CPHR_NS, - ver); - /* TODO: abort negotiation */ - return -1; - } - wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey", - wpa_cipher_txt(cipher)); - peerkey->cipher = cipher; - - return 0; -} - - -static int wpa_supplicant_process_smk_m45( - struct wpa_sm *sm, const unsigned char *src_addr, - const struct wpa_eapol_key *key, size_t extra_len, int ver) -{ - struct wpa_peerkey *peerkey; - struct wpa_eapol_ie_parse kde; - u32 lifetime; - - if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { - wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for " - "the current network"); - return -1; - } - - if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < - 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M4/M5"); - return -1; - } - - if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || - kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN || - kde.smk == NULL || kde.smk_len < PMK_LEN + WPA_NONCE_LEN || - kde.lifetime == NULL || kde.lifetime_len < 4) { - wpa_printf(MSG_INFO, "RSN: No MAC Address, Nonce, SMK, or " - "Lifetime KDE in SMK M4/M5"); - return -1; - } - - for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { - if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == 0 && - os_memcmp(peerkey->initiator ? peerkey->inonce : - peerkey->pnonce, - key->key_nonce, WPA_NONCE_LEN) == 0) - break; - } - if (peerkey == NULL) { - wpa_printf(MSG_INFO, "RSN: No matching SMK handshake found " - "for SMK M4/M5: peer " MACSTR, - MAC2STR(kde.mac_addr)); - return -1; - } - - if (peerkey->initiator) { - if (wpa_supplicant_process_smk_m5(sm, src_addr, key, ver, - peerkey, &kde) < 0) - return -1; - } else { - if (wpa_supplicant_process_smk_m4(peerkey, &kde) < 0) - return -1; - } - - os_memcpy(peerkey->smk, kde.smk, PMK_LEN); - peerkey->smk_complete = 1; - wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", peerkey->smk, PMK_LEN); - lifetime = WPA_GET_BE32(kde.lifetime); - wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime); - if (lifetime > 1000000000) - lifetime = 1000000000; /* avoid overflowing eloop time */ - peerkey->lifetime = lifetime; - eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout, - sm, peerkey); - - if (peerkey->initiator) { - rsn_smkid(peerkey->smk, peerkey->pnonce, peerkey->addr, - peerkey->inonce, sm->own_addr, peerkey->smkid, - peerkey->akmp); - wpa_supplicant_send_stk_1_of_4(sm, peerkey); - } else { - rsn_smkid(peerkey->smk, peerkey->pnonce, sm->own_addr, - peerkey->inonce, peerkey->addr, peerkey->smkid, - peerkey->akmp); - } - wpa_hexdump(MSG_DEBUG, "RSN: SMKID", peerkey->smkid, PMKID_LEN); - - return 0; -} - - -static int wpa_supplicant_process_smk_error( - struct wpa_sm *sm, const unsigned char *src_addr, - const struct wpa_eapol_key *key, size_t extra_len) -{ - struct wpa_eapol_ie_parse kde; - struct rsn_error_kde error; - u8 peer[ETH_ALEN]; - u16 error_type; - - wpa_printf(MSG_DEBUG, "RSN: Received SMK Error"); - - if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { - wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for " - "the current network"); - return -1; - } - - if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) < - 0) { - wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); - return -1; - } - - if (kde.error == NULL || kde.error_len < sizeof(error)) { - wpa_printf(MSG_INFO, "RSN: No Error KDE in SMK Error"); - return -1; - } - - if (kde.mac_addr && kde.mac_addr_len >= ETH_ALEN) - os_memcpy(peer, kde.mac_addr, ETH_ALEN); - else - os_memset(peer, 0, ETH_ALEN); - os_memcpy(&error, kde.error, sizeof(error)); - error_type = be_to_host16(error.error_type); - wpa_msg(sm->ctx->msg_ctx, MSG_INFO, - "RSN: SMK Error KDE received: MUI %d error_type %d peer " - MACSTR, - be_to_host16(error.mui), error_type, - MAC2STR(peer)); - - if (kde.mac_addr && - (error_type == STK_ERR_STA_NR || error_type == STK_ERR_STA_NRSN || - error_type == STK_ERR_CPHR_NS)) { - struct wpa_peerkey *peerkey; - - for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { - if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == - 0) - break; - } - if (peerkey == NULL) { - wpa_printf(MSG_DEBUG, "RSN: No matching SMK handshake " - "found for SMK Error"); - return -1; - } - /* TODO: abort SMK/STK handshake and remove all related keys */ - } - - return 0; -} - - -static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm, - struct wpa_peerkey *peerkey, - const struct wpa_eapol_key *key, - u16 ver, const u8 *key_data, - size_t key_data_len) -{ - struct wpa_eapol_ie_parse ie; - size_t kde_buf_len; - struct wpa_ptk *stk; - u8 buf[8], *kde_buf, *pos; - be32 lifetime; - - wpa_printf(MSG_DEBUG, "RSN: RX message 1 of STK 4-Way Handshake from " - MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); - - os_memset(&ie, 0, sizeof(ie)); - - /* RSN: msg 1/4 should contain SMKID for the selected SMK */ - wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", key_data, key_data_len); - if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0 || - ie.pmkid == NULL) { - wpa_printf(MSG_DEBUG, "RSN: No SMKID in STK 1/4"); - return; - } - if (os_memcmp_const(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) { - wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 1/4", - ie.pmkid, PMKID_LEN); - return; - } - - if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) { - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "RSN: Failed to get random data for PNonce"); - return; - } - wpa_hexdump(MSG_DEBUG, "WPA: Renewed PNonce", - peerkey->pnonce, WPA_NONCE_LEN); - - /* Calculate STK which will be stored as a temporary STK until it has - * been verified when processing message 3/4. */ - stk = &peerkey->tstk; - wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion", - sm->own_addr, peerkey->addr, - peerkey->pnonce, key->key_nonce, - stk, peerkey->akmp, peerkey->cipher); - /* Supplicant: swap tx/rx Mic keys */ - os_memcpy(buf, &stk->tk[16], 8); - os_memcpy(&stk->tk[16], &stk->tk[24], 8); - os_memcpy(&stk->tk[24], buf, 8); - peerkey->tstk_set = 1; - - kde_buf_len = peerkey->rsnie_p_len + - 2 + RSN_SELECTOR_LEN + sizeof(lifetime) + - 2 + RSN_SELECTOR_LEN + PMKID_LEN; - kde_buf = os_malloc(kde_buf_len); - if (kde_buf == NULL) - return; - pos = kde_buf; - pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len); - lifetime = host_to_be32(peerkey->lifetime); - pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, - (u8 *) &lifetime, sizeof(lifetime)); - wpa_add_kde(pos, RSN_KEY_DATA_PMKID, peerkey->smkid, PMKID_LEN); - - if (wpa_supplicant_send_2_of_4(sm, peerkey->addr, key, ver, - peerkey->pnonce, kde_buf, kde_buf_len, - stk)) { - os_free(kde_buf); - return; - } - os_free(kde_buf); - - os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN); -} - - -static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm, - struct wpa_peerkey *peerkey, - struct wpa_eapol_ie_parse *kde) -{ - u32 lifetime; - - if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime)) - return; - - lifetime = WPA_GET_BE32(kde->lifetime); - - if (lifetime >= peerkey->lifetime) { - wpa_printf(MSG_DEBUG, "RSN: Peer used SMK lifetime %u seconds " - "which is larger than or equal to own value %u " - "seconds - ignored", lifetime, peerkey->lifetime); - return; - } - - wpa_printf(MSG_DEBUG, "RSN: Peer used shorter SMK lifetime %u seconds " - "(own was %u seconds) - updated", - lifetime, peerkey->lifetime); - peerkey->lifetime = lifetime; - - eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey); - eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout, - sm, peerkey); -} - - -static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm, - struct wpa_peerkey *peerkey, - const struct wpa_eapol_key *key, - u16 ver, const u8 *key_data, - size_t key_data_len) -{ - struct wpa_eapol_ie_parse kde; - - wpa_printf(MSG_DEBUG, "RSN: RX message 2 of STK 4-Way Handshake from " - MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); - - os_memset(&kde, 0, sizeof(kde)); - - /* RSN: msg 2/4 should contain SMKID for the selected SMK and RSN IE - * from the peer. It may also include Lifetime KDE. */ - wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", key_data, key_data_len); - if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0 || - kde.pmkid == NULL || kde.rsn_ie == NULL) { - wpa_printf(MSG_DEBUG, "RSN: No SMKID or RSN IE in STK 2/4"); - return; - } - - if (os_memcmp_const(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) { - wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 2/4", - kde.pmkid, PMKID_LEN); - return; - } - - if (kde.rsn_ie_len != peerkey->rsnie_p_len || - os_memcmp(kde.rsn_ie, peerkey->rsnie_p, kde.rsn_ie_len) != 0) { - wpa_printf(MSG_INFO, "RSN: Peer RSN IE in SMK and STK " - "handshakes did not match"); - wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in SMK handshake", - peerkey->rsnie_p, peerkey->rsnie_p_len); - wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in STK handshake", - kde.rsn_ie, kde.rsn_ie_len); - return; - } - - wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde); - - wpa_supplicant_send_stk_3_of_4(sm, peerkey); - os_memcpy(peerkey->pnonce, key->key_nonce, WPA_NONCE_LEN); -} - - -static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm, - struct wpa_peerkey *peerkey, - const struct wpa_eapol_key *key, - u16 ver, const u8 *key_data, - size_t key_data_len) -{ - struct wpa_eapol_ie_parse kde; - size_t key_len; - const u8 *_key; - u8 key_buf[32], rsc[6]; - - wpa_printf(MSG_DEBUG, "RSN: RX message 3 of STK 4-Way Handshake from " - MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); - - os_memset(&kde, 0, sizeof(kde)); - - /* RSN: msg 3/4 should contain Initiator RSN IE. It may also include - * Lifetime KDE. */ - wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", key_data, key_data_len); - if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0) { - wpa_printf(MSG_DEBUG, "RSN: Failed to parse key data in " - "STK 3/4"); - return; - } - - if (kde.rsn_ie_len != peerkey->rsnie_i_len || - os_memcmp(kde.rsn_ie, peerkey->rsnie_i, kde.rsn_ie_len) != 0) { - wpa_printf(MSG_INFO, "RSN: Initiator RSN IE in SMK and STK " - "handshakes did not match"); - wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in SMK " - "handshake", - peerkey->rsnie_i, peerkey->rsnie_i_len); - wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in STK " - "handshake", - kde.rsn_ie, kde.rsn_ie_len); - return; - } - - if (os_memcmp(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN) != 0) { - wpa_printf(MSG_WARNING, "RSN: INonce from message 1 of STK " - "4-Way Handshake differs from 3 of STK 4-Way " - "Handshake - drop packet (src=" MACSTR ")", - MAC2STR(peerkey->addr)); - return; - } - - wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde); - - if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver, - WPA_GET_BE16(key->key_info), - &peerkey->stk)) - return; - - _key = peerkey->stk.tk; - if (peerkey->cipher == WPA_CIPHER_TKIP) { - /* Swap Tx/Rx keys for Michael MIC */ - os_memcpy(key_buf, _key, 16); - os_memcpy(key_buf + 16, _key + 24, 8); - os_memcpy(key_buf + 24, _key + 16, 8); - _key = key_buf; - key_len = 32; - } else - key_len = 16; - - os_memset(rsc, 0, 6); - if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1, - rsc, sizeof(rsc), _key, key_len) < 0) { - os_memset(key_buf, 0, sizeof(key_buf)); - wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the " - "driver."); - return; - } - os_memset(key_buf, 0, sizeof(key_buf)); -} - - -static void wpa_supplicant_process_stk_4_of_4(struct wpa_sm *sm, - struct wpa_peerkey *peerkey, - const struct wpa_eapol_key *key, - u16 ver) -{ - u8 rsc[6]; - - wpa_printf(MSG_DEBUG, "RSN: RX message 4 of STK 4-Way Handshake from " - MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); - - os_memset(rsc, 0, 6); - if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1, - rsc, sizeof(rsc), peerkey->stk.tk, - peerkey->cipher == WPA_CIPHER_TKIP ? 32 : 16) < 0) { - wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the " - "driver."); - return; - } -} - - -/** - * peerkey_verify_eapol_key_mic - Verify PeerKey MIC - * @sm: Pointer to WPA state machine data from wpa_sm_init() - * @peerkey: Pointer to the PeerKey data for the peer - * @key: Pointer to the EAPOL-Key frame header - * @ver: Version bits from EAPOL-Key Key Info - * @buf: Pointer to the beginning of EAPOL-Key frame - * @len: Length of the EAPOL-Key frame - * Returns: 0 on success, -1 on failure - */ -int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, - struct wpa_peerkey *peerkey, - struct wpa_eapol_key_192 *key, u16 ver, - const u8 *buf, size_t len) -{ - u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; - size_t mic_len = 16; - int ok = 0; - - if (peerkey->initiator && !peerkey->stk_set) { - wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion", - sm->own_addr, peerkey->addr, - peerkey->inonce, key->key_nonce, - &peerkey->stk, peerkey->akmp, peerkey->cipher); - peerkey->stk_set = 1; - } - - os_memcpy(mic, key->key_mic, mic_len); - if (peerkey->tstk_set) { - os_memset(key->key_mic, 0, mic_len); - wpa_eapol_key_mic(peerkey->tstk.kck, peerkey->tstk.kck_len, - sm->key_mgmt, ver, buf, len, key->key_mic); - if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) { - wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC " - "when using TSTK - ignoring TSTK"); - } else { - ok = 1; - peerkey->tstk_set = 0; - peerkey->stk_set = 1; - os_memcpy(&peerkey->stk, &peerkey->tstk, - sizeof(peerkey->stk)); - os_memset(&peerkey->tstk, 0, sizeof(peerkey->tstk)); - } - } - - if (!ok && peerkey->stk_set) { - os_memset(key->key_mic, 0, mic_len); - wpa_eapol_key_mic(peerkey->stk.kck, peerkey->stk.kck_len, - sm->key_mgmt, ver, buf, len, key->key_mic); - if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) { - wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC " - "- dropping packet"); - return -1; - } - ok = 1; - } - - if (!ok) { - wpa_printf(MSG_WARNING, "RSN: Could not verify EAPOL-Key MIC " - "- dropping packet"); - return -1; - } - - os_memcpy(peerkey->replay_counter, key->replay_counter, - WPA_REPLAY_COUNTER_LEN); - peerkey->replay_counter_set = 1; - return 0; -} - - -/** - * wpa_sm_stkstart - Send EAPOL-Key Request for STK handshake (STK M1) - * @sm: Pointer to WPA state machine data from wpa_sm_init() - * @peer: MAC address of the peer STA - * Returns: 0 on success, or -1 on failure - * - * Send an EAPOL-Key Request to the current authenticator to start STK - * handshake with the peer. - */ -int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) -{ - size_t rlen, kde_len; - struct wpa_eapol_key *req; - int key_info, ver; - u8 bssid[ETH_ALEN], *rbuf, *pos, *count_pos; - u16 count; - struct rsn_ie_hdr *hdr; - struct wpa_peerkey *peerkey; - struct wpa_ie_data ie; - - if (sm->proto != WPA_PROTO_RSN || !sm->ptk_set || !sm->peerkey_enabled) - return -1; - - if (sm->ap_rsn_ie && - wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &ie) == 0 && - !(ie.capabilities & WPA_CAPABILITY_PEERKEY_ENABLED)) { - wpa_printf(MSG_DEBUG, "RSN: Current AP does not support STK"); - return -1; - } - - if (sm->pairwise_cipher != WPA_CIPHER_TKIP) - ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; - else - ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; - - if (wpa_sm_get_bssid(sm, bssid) < 0) { - wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key " - "SMK M1"); - return -1; - } - - /* TODO: find existing entry and if found, use that instead of adding - * a new one */ - peerkey = os_zalloc(sizeof(*peerkey)); - if (peerkey == NULL) - return -1; - peerkey->initiator = 1; - os_memcpy(peerkey->addr, peer, ETH_ALEN); - peerkey->akmp = sm->key_mgmt; - - /* SMK M1: - * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, - * MIC=MIC, DataKDs=(RSNIE_I, MAC_P KDE)) - */ - - hdr = (struct rsn_ie_hdr *) peerkey->rsnie_i; - hdr->elem_id = WLAN_EID_RSN; - WPA_PUT_LE16(hdr->version, RSN_VERSION); - pos = (u8 *) (hdr + 1); - /* Group Suite can be anything for SMK RSN IE; receiver will just - * ignore it. */ - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); - pos += RSN_SELECTOR_LEN; - count_pos = pos; - pos += 2; - - count = rsn_cipher_put_suites(pos, sm->allowed_pairwise_cipher); - pos += count * RSN_SELECTOR_LEN; - WPA_PUT_LE16(count_pos, count); - - hdr->len = (pos - peerkey->rsnie_i) - 2; - peerkey->rsnie_i_len = pos - peerkey->rsnie_i; - wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake", - peerkey->rsnie_i, peerkey->rsnie_i_len); - - kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; - - rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, - sizeof(*req) + kde_len, &rlen, - (void *) &req); - if (rbuf == NULL) { - wpa_supplicant_peerkey_free(sm, peerkey); - return -1; - } - - req->type = EAPOL_KEY_TYPE_RSN; - key_info = WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC | - WPA_KEY_INFO_SECURE | WPA_KEY_INFO_REQUEST | ver; - WPA_PUT_BE16(req->key_info, key_info); - WPA_PUT_BE16(req->key_length, 0); - os_memcpy(req->replay_counter, sm->request_counter, - WPA_REPLAY_COUNTER_LEN); - inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); - - if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) { - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "WPA: Failed to get random data for INonce"); - os_free(rbuf); - wpa_supplicant_peerkey_free(sm, peerkey); - return -1; - } - os_memcpy(req->key_nonce, peerkey->inonce, WPA_NONCE_LEN); - wpa_hexdump(MSG_DEBUG, "WPA: INonce for SMK handshake", - req->key_nonce, WPA_NONCE_LEN); - - WPA_PUT_BE16(req->key_data_length, (u16) kde_len); - pos = (u8 *) (req + 1); - - /* Initiator RSN IE */ - pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len); - /* Peer MAC address KDE */ - wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN); - - wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer " - MACSTR ")", MAC2STR(peer)); - wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid, - ETH_P_EAPOL, rbuf, rlen, req->key_mic); - - peerkey->next = sm->peerkey; - sm->peerkey = peerkey; - - return 0; -} - - -/** - * peerkey_deinit - Free PeerKey values - * @sm: Pointer to WPA state machine data from wpa_sm_init() - */ -void peerkey_deinit(struct wpa_sm *sm) -{ - struct wpa_peerkey *prev, *peerkey = sm->peerkey; - while (peerkey) { - prev = peerkey; - peerkey = peerkey->next; - wpa_supplicant_peerkey_free(sm, prev); - } - sm->peerkey = NULL; -} - - -void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey, - struct wpa_eapol_key *key, u16 key_info, u16 ver, - const u8 *key_data, size_t key_data_len) -{ - if ((key_info & (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) == - (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) { - /* 3/4 STK 4-Way Handshake */ - wpa_supplicant_process_stk_3_of_4(sm, peerkey, key, ver, - key_data, key_data_len); - } else if (key_info & WPA_KEY_INFO_ACK) { - /* 1/4 STK 4-Way Handshake */ - wpa_supplicant_process_stk_1_of_4(sm, peerkey, key, ver, - key_data, key_data_len); - } else if (key_info & WPA_KEY_INFO_SECURE) { - /* 4/4 STK 4-Way Handshake */ - wpa_supplicant_process_stk_4_of_4(sm, peerkey, key, ver); - } else { - /* 2/4 STK 4-Way Handshake */ - wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver, - key_data, key_data_len); - } -} - - -void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr, - struct wpa_eapol_key *key, size_t extra_len, - u16 key_info, u16 ver) -{ - if (key_info & WPA_KEY_INFO_ERROR) { - /* SMK Error */ - wpa_supplicant_process_smk_error(sm, src_addr, key, extra_len); - } else if (key_info & WPA_KEY_INFO_ACK) { - /* SMK M2 */ - wpa_supplicant_process_smk_m2(sm, src_addr, key, extra_len, - ver); - } else { - /* SMK M4 or M5 */ - wpa_supplicant_process_smk_m45(sm, src_addr, key, extra_len, - ver); - } -} - -#endif /* CONFIG_PEERKEY */ diff --git a/src/rsn_supp/peerkey.h b/src/rsn_supp/peerkey.h deleted file mode 100644 index 6ccd948baace5..0000000000000 --- a/src/rsn_supp/peerkey.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * WPA Supplicant - PeerKey for Direct Link Setup (DLS) - * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> - * - * This software may be distributed under the terms of the BSD license. - * See README for more details. - */ - -#ifndef PEERKEY_H -#define PEERKEY_H - -#define PEERKEY_MAX_IE_LEN 80 -struct wpa_peerkey { - struct wpa_peerkey *next; - int initiator; /* whether this end was initator for SMK handshake */ - u8 addr[ETH_ALEN]; /* other end MAC address */ - u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */ - u8 pnonce[WPA_NONCE_LEN]; /* Peer Nonce */ - u8 rsnie_i[PEERKEY_MAX_IE_LEN]; /* Initiator RSN IE */ - size_t rsnie_i_len; - u8 rsnie_p[PEERKEY_MAX_IE_LEN]; /* Peer RSN IE */ - size_t rsnie_p_len; - u8 smk[PMK_LEN]; - int smk_complete; - u8 smkid[PMKID_LEN]; - u32 lifetime; - int cipher; /* Selected cipher (WPA_CIPHER_*) */ - u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; - int replay_counter_set; - int akmp; - - struct wpa_ptk stk, tstk; - int stk_set, tstk_set; -}; - - -#ifdef CONFIG_PEERKEY - -int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, - struct wpa_peerkey *peerkey, - struct wpa_eapol_key_192 *key, u16 ver, - const u8 *buf, size_t len); -void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey, - struct wpa_eapol_key *key, u16 key_info, u16 ver, - const u8 *key_data, size_t key_data_len); -void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr, - struct wpa_eapol_key *key, size_t extra_len, - u16 key_info, u16 ver); -void peerkey_deinit(struct wpa_sm *sm); - -#else /* CONFIG_PEERKEY */ - -static inline int -peerkey_verify_eapol_key_mic(struct wpa_sm *sm, - struct wpa_peerkey *peerkey, - struct wpa_eapol_key *key, u16 ver, - const u8 *buf, size_t len) -{ - return -1; -} - -static inline void -peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey, - struct wpa_eapol_key *key, u16 key_info, u16 ver, - const u8 *key_data, size_t key_data_len) -{ -} - -static inline void -peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr, - struct wpa_eapol_key *key, size_t extra_len, - u16 key_info, u16 ver) -{ -} - -static inline void peerkey_deinit(struct wpa_sm *sm) -{ -} - -#endif /* CONFIG_PEERKEY */ - -#endif /* PEERKEY_H */ diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c index 3d8d12223c261..fdd522087dbc9 100644 --- a/src/rsn_supp/pmksa_cache.c +++ b/src/rsn_supp/pmksa_cache.c @@ -43,7 +43,10 @@ static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, struct rsn_pmksa_cache_entry *entry, enum pmksa_free_reason reason) { - wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid); + wpa_sm_remove_pmkid(pmksa->sm, entry->network_ctx, entry->aa, + entry->pmkid, + entry->fils_cache_id_set ? entry->fils_cache_id : + NULL); pmksa->pmksa_count--; pmksa->free_cb(entry, pmksa->ctx, reason); _pmksa_cache_free_entry(entry); @@ -93,7 +96,7 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa : - pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL); + pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL, 0); if (entry) { sec = pmksa->pmksa->reauth_time - now.sec; if (sec < 0) @@ -116,6 +119,7 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) * @spa: Supplicant address * @network_ctx: Network configuration context for this PMK * @akmp: WPA_KEY_MGMT_* used in key derivation + * @cache_id: Pointer to FILS Cache Identifier or %NULL if not advertised * Returns: Pointer to the added PMKSA cache entry or %NULL on error * * This function create a PMKSA entry for a new PMK and adds it to the PMKSA @@ -126,9 +130,10 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *kck, size_t kck_len, - const u8 *aa, const u8 *spa, void *network_ctx, int akmp) + const u8 *aa, const u8 *spa, void *network_ctx, int akmp, + const u8 *cache_id) { - struct rsn_pmksa_cache_entry *entry, *pos, *prev; + struct rsn_pmksa_cache_entry *entry; struct os_reltime now; if (pmk_len > PMK_LEN_MAX) @@ -149,24 +154,38 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, else if (wpa_key_mgmt_suite_b(akmp)) rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); else - rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, - wpa_key_mgmt_sha256(akmp)); + rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp); os_get_reltime(&now); entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime; entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime * pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100; entry->akmp = akmp; + if (cache_id) { + entry->fils_cache_id_set = 1; + os_memcpy(entry->fils_cache_id, cache_id, FILS_CACHE_ID_LEN); + } os_memcpy(entry->aa, aa, ETH_ALEN); entry->network_ctx = network_ctx; + return pmksa_cache_add_entry(pmksa, entry); +} + + +struct rsn_pmksa_cache_entry * +pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) +{ + struct rsn_pmksa_cache_entry *pos, *prev; + /* Replace an old entry for the same Authenticator (if found) with the * new entry */ pos = pmksa->pmksa; prev = NULL; while (pos) { - if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) { - if (pos->pmk_len == pmk_len && - os_memcmp_const(pos->pmk, pmk, pmk_len) == 0 && + if (os_memcmp(entry->aa, pos->aa, ETH_ALEN) == 0) { + if (pos->pmk_len == entry->pmk_len && + os_memcmp_const(pos->pmk, entry->pmk, + entry->pmk_len) == 0 && os_memcmp_const(pos->pmkid, entry->pmkid, PMKID_LEN) == 0) { wpa_printf(MSG_DEBUG, "WPA: reusing previous " @@ -192,8 +211,8 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, "the current AP and any PMKSA cache entry " "that was based on the old PMK"); if (!pos->opportunistic) - pmksa_cache_flush(pmksa, network_ctx, pos->pmk, - pos->pmk_len); + pmksa_cache_flush(pmksa, entry->network_ctx, + pos->pmk, pos->pmk_len); pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE); break; } @@ -244,8 +263,10 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, } pmksa->pmksa_count++; wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR - " network_ctx=%p", MAC2STR(entry->aa), network_ctx); - wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid); + " network_ctx=%p", MAC2STR(entry->aa), entry->network_ctx); + wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid, + entry->fils_cache_id_set ? entry->fils_cache_id : NULL, + entry->pmk, entry->pmk_len); return entry; } @@ -320,17 +341,20 @@ void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) * @aa: Authenticator address or %NULL to match any * @pmkid: PMKID or %NULL to match any * @network_ctx: Network context or %NULL to match any + * @akmp: Specific AKMP to search for or 0 for any * Returns: Pointer to PMKSA cache entry or %NULL if no match was found */ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, - const void *network_ctx) + const void *network_ctx, + int akmp) { struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; while (entry) { if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) && (pmkid == NULL || os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) && + (!akmp || akmp == entry->akmp) && (network_ctx == NULL || network_ctx == entry->network_ctx)) return entry; entry = entry->next; @@ -345,16 +369,19 @@ pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, const u8 *aa) { struct rsn_pmksa_cache_entry *new_entry; + os_time_t old_expiration = old_entry->expiration; new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len, NULL, NULL, 0, aa, pmksa->sm->own_addr, - old_entry->network_ctx, old_entry->akmp); + old_entry->network_ctx, old_entry->akmp, + old_entry->fils_cache_id_set ? + old_entry->fils_cache_id : NULL); if (new_entry == NULL) return NULL; /* TODO: reorder entries based on expiration time? */ - new_entry->expiration = old_entry->expiration; + new_entry->expiration = old_expiration; new_entry->opportunistic = 1; return new_entry; @@ -366,6 +393,7 @@ pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @network_ctx: Network configuration context * @aa: Authenticator address for the new AP + * @akmp: Specific AKMP to search for or 0 for any * Returns: Pointer to a new PMKSA cache entry or %NULL if not available * * Try to create a new PMKSA cache entry opportunistically by guessing that the @@ -374,7 +402,7 @@ pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, */ struct rsn_pmksa_cache_entry * pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, - const u8 *aa) + const u8 *aa, int akmp) { struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; @@ -382,7 +410,8 @@ pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, if (network_ctx == NULL) return NULL; while (entry) { - if (entry->network_ctx == network_ctx) { + if (entry->network_ctx == network_ctx && + (!akmp || entry->akmp == akmp)) { entry = pmksa_cache_clone_entry(pmksa, entry, aa); if (entry) { wpa_printf(MSG_DEBUG, "RSN: added " @@ -397,6 +426,24 @@ pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx, } +static struct rsn_pmksa_cache_entry * +pmksa_cache_get_fils_cache_id(struct rsn_pmksa_cache *pmksa, + const void *network_ctx, const u8 *cache_id) +{ + struct rsn_pmksa_cache_entry *entry; + + for (entry = pmksa->pmksa; entry; entry = entry->next) { + if (network_ctx == entry->network_ctx && + entry->fils_cache_id_set && + os_memcmp(cache_id, entry->fils_cache_id, + FILS_CACHE_ID_LEN) == 0) + return entry; + } + + return NULL; +} + + /** * pmksa_cache_get_current - Get the current used PMKSA entry * @sm: Pointer to WPA state machine data from wpa_sm_init() @@ -429,33 +476,44 @@ void pmksa_cache_clear_current(struct wpa_sm *sm) * @bssid: BSSID for PMKSA or %NULL if not used * @network_ctx: Network configuration context * @try_opportunistic: Whether to allow opportunistic PMKSA caching + * @fils_cache_id: Pointer to FILS Cache Identifier or %NULL if not used * Returns: 0 if PMKSA was found or -1 if no matching entry was found */ int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, const u8 *bssid, void *network_ctx, - int try_opportunistic) + int try_opportunistic, const u8 *fils_cache_id, + int akmp) { struct rsn_pmksa_cache *pmksa = sm->pmksa; wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p " - "try_opportunistic=%d", network_ctx, try_opportunistic); + "try_opportunistic=%d akmp=0x%x", + network_ctx, try_opportunistic, akmp); if (pmkid) wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID", pmkid, PMKID_LEN); if (bssid) wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR, MAC2STR(bssid)); + if (fils_cache_id) + wpa_printf(MSG_DEBUG, + "RSN: Search for FILS Cache Identifier %02x%02x", + fils_cache_id[0], fils_cache_id[1]); sm->cur_pmksa = NULL; if (pmkid) sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid, - network_ctx); + network_ctx, akmp); if (sm->cur_pmksa == NULL && bssid) sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL, - network_ctx); + network_ctx, akmp); if (sm->cur_pmksa == NULL && try_opportunistic && bssid) sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa, network_ctx, - bssid); + bssid, akmp); + if (sm->cur_pmksa == NULL && fils_cache_id) + sm->cur_pmksa = pmksa_cache_get_fils_cache_id(pmksa, + network_ctx, + fils_cache_id); if (sm->cur_pmksa) { wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID", sm->cur_pmksa->pmkid, PMKID_LEN); @@ -482,11 +540,20 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) char *pos = buf; struct rsn_pmksa_cache_entry *entry; struct os_reltime now; + int cache_id_used = 0; + + for (entry = pmksa->pmksa; entry; entry = entry->next) { + if (entry->fils_cache_id_set) { + cache_id_used = 1; + break; + } + } os_get_reltime(&now); ret = os_snprintf(pos, buf + len - pos, "Index / AA / PMKID / expiration (in seconds) / " - "opportunistic\n"); + "opportunistic%s\n", + cache_id_used ? " / FILS Cache Identifier" : ""); if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; @@ -501,18 +568,36 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) pos += ret; pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, PMKID_LEN); - ret = os_snprintf(pos, buf + len - pos, " %d %d\n", + ret = os_snprintf(pos, buf + len - pos, " %d %d", (int) (entry->expiration - now.sec), entry->opportunistic); if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; + if (entry->fils_cache_id_set) { + ret = os_snprintf(pos, buf + len - pos, " %02x%02x", + entry->fils_cache_id[0], + entry->fils_cache_id[1]); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + } + ret = os_snprintf(pos, buf + len - pos, "\n"); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; entry = entry->next; } return pos - buf; } +struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa) +{ + return pmksa->pmksa; +} + + /** * pmksa_cache_init - Initialize PMKSA cache * @free_cb: Callback function to be called when a PMKSA cache entry is freed diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h index daede6dac7fe6..6c49fa9248fb3 100644 --- a/src/rsn_supp/pmksa_cache.h +++ b/src/rsn_supp/pmksa_cache.h @@ -21,6 +21,14 @@ struct rsn_pmksa_cache_entry { int akmp; /* WPA_KEY_MGMT_* */ u8 aa[ETH_ALEN]; + /* + * If FILS Cache Identifier is included (fils_cache_id_set), this PMKSA + * cache entry is applicable to all BSSs (any BSSID/aa[]) that + * advertise the same FILS Cache Identifier within the same ESS. + */ + u8 fils_cache_id[2]; + unsigned int fils_cache_id_set:1; + os_time_t reauth_time; /** @@ -53,20 +61,27 @@ pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, - const void *network_ctx); + const void *network_ctx, + int akmp); int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); +struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa); struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *kck, size_t kck_len, - const u8 *aa, const u8 *spa, void *network_ctx, int akmp); + const u8 *aa, const u8 *spa, void *network_ctx, int akmp, + const u8 *cache_id); +struct rsn_pmksa_cache_entry * +pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry); struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm); void pmksa_cache_clear_current(struct wpa_sm *sm); int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, const u8 *bssid, void *network_ctx, - int try_opportunistic); + int try_opportunistic, const u8 *fils_cache_id, + int akmp); struct rsn_pmksa_cache_entry * pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, - void *network_ctx, const u8 *aa); + void *network_ctx, const u8 *aa, int akmp); void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, const u8 *pmk, size_t pmk_len); @@ -86,7 +101,7 @@ static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) static inline struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid, - const void *network_ctx) + const void *network_ctx, int akmp) { return NULL; } @@ -104,9 +119,23 @@ static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, } static inline struct rsn_pmksa_cache_entry * +pmksa_cache_head(struct rsn_pmksa_cache *pmksa) +{ + return NULL; +} + +static inline struct rsn_pmksa_cache_entry * +pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) +{ + return NULL; +} + +static inline struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *pmkid, const u8 *kck, size_t kck_len, - const u8 *aa, const u8 *spa, void *network_ctx, int akmp) + const u8 *aa, const u8 *spa, void *network_ctx, int akmp, + const u8 *cache_id) { return NULL; } @@ -118,7 +147,9 @@ static inline void pmksa_cache_clear_current(struct wpa_sm *sm) static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, const u8 *bssid, void *network_ctx, - int try_opportunistic) + int try_opportunistic, + const u8 *fils_cache_id, + int akmp) { return -1; } diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c index 4c9a4fb8b14ca..d0c43f464e27d 100644 --- a/src/rsn_supp/preauth.c +++ b/src/rsn_supp/preauth.c @@ -97,7 +97,7 @@ static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, NULL, 0, sm->preauth_bssid, sm->own_addr, sm->network_ctx, - WPA_KEY_MGMT_IEEE8021X); + WPA_KEY_MGMT_IEEE8021X, NULL); } else { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: failed to get master session key from " @@ -323,7 +323,7 @@ void rsn_preauth_candidate_process(struct wpa_sm *sm) dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates, struct rsn_pmksa_candidate, list) { struct rsn_pmksa_cache_entry *p = NULL; - p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL); + p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL, 0); if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 && (p == NULL || p->opportunistic)) { wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA " @@ -342,7 +342,8 @@ void rsn_preauth_candidate_process(struct wpa_sm *sm) /* Some drivers (e.g., NDIS) expect to get notified about the * PMKIDs again, so report the existing data now. */ if (p) { - wpa_sm_add_pmkid(sm, candidate->bssid, p->pmkid); + wpa_sm_add_pmkid(sm, NULL, candidate->bssid, p->pmkid, + NULL, p->pmk, p->pmk_len); } dl_list_del(&candidate->list); @@ -371,7 +372,7 @@ void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, if (sm->network_ctx && sm->proactive_key_caching) pmksa_cache_get_opportunistic(sm->pmksa, sm->network_ctx, - bssid); + bssid, 0); if (!preauth) { wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without " @@ -482,7 +483,7 @@ void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid, if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie)) return; - pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL); + pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL, 0); if (pmksa && (!pmksa->opportunistic || !(ie.capabilities & WPA_CAPABILITY_PREAUTH))) return; diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c index 9eb973860049c..345b0c84d871c 100644 --- a/src/rsn_supp/tdls.c +++ b/src/rsn_supp/tdls.c @@ -35,6 +35,7 @@ #define TDLS_TESTING_DECLINE_RESP BIT(9) #define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10) #define TDLS_TESTING_WRONG_MIC BIT(11) +#define TDLS_TESTING_DOUBLE_TPK_M2 BIT(12) unsigned int tdls_testing = 0; #endif /* CONFIG_TDLS_TESTING */ @@ -303,10 +304,9 @@ static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code, peer->sm_tmr.peer_capab = peer_capab; peer->sm_tmr.buf_len = msg_len; os_free(peer->sm_tmr.buf); - peer->sm_tmr.buf = os_malloc(msg_len); + peer->sm_tmr.buf = os_memdup(msg, msg_len); if (peer->sm_tmr.buf == NULL) return -1; - os_memcpy(peer->sm_tmr.buf, msg, msg_len); wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered " "(action_code=%u)", action_code); @@ -413,8 +413,9 @@ static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer, size_t len[2]; u8 data[3 * ETH_ALEN]; - /* IEEE Std 802.11z-2010 8.5.9.1: - * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce)) + /* IEEE Std 802.11-2016 12.7.9.2: + * TPK-Key-Input = Hash(min(SNonce, ANonce) || max(SNonce, ANonce)) + * Hash = SHA-256 for TDLS */ len[0] = WPA_NONCE_LEN; len[1] = WPA_NONCE_LEN; @@ -432,11 +433,8 @@ static void wpa_tdls_generate_tpk(struct wpa_tdls_peer *peer, key_input, SHA256_MAC_LEN); /* - * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK", - * min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY) - * TODO: is N_KEY really included in KDF Context and if so, in which - * presentation format (little endian 16-bit?) is it used? It gets - * added by the KDF anyway.. + * TPK = KDF-Hash-Length(TPK-Key-Input, "TDLS PMK", + * min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID) */ if (os_memcmp(own_addr, peer->addr, ETH_ALEN) < 0) { @@ -1220,9 +1218,10 @@ skip_ies: #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_DIFF_BSSID) { + struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos; + wpa_printf(MSG_DEBUG, "TDLS: Testing - use incorrect BSSID in " "Link Identifier"); - struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos; wpa_tdls_linkid(sm, peer, l); l->bssid[5] ^= 0x01; pos += sizeof(*l); @@ -2126,6 +2125,13 @@ skip_add_peer: goto error; } +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_DOUBLE_TPK_M2) { + wpa_printf(MSG_INFO, "TDLS: Testing - Send another TPK M2"); + wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer); + } +#endif /* CONFIG_TDLS_TESTING */ + return 0; error: @@ -2153,11 +2159,11 @@ static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) eloop_register_timeout(lifetime, 0, wpa_tdls_tpk_timeout, sm, peer); #ifdef CONFIG_TDLS_TESTING - if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) { - wpa_printf(MSG_DEBUG, "TDLS: Testing - disable TPK " - "expiration"); - eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); - } + if (tdls_testing & TDLS_TESTING_NO_TPK_EXPIRATION) { + wpa_printf(MSG_DEBUG, + "TDLS: Testing - disable TPK expiration"); + eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); + } #endif /* CONFIG_TDLS_TESTING */ } @@ -2912,14 +2918,14 @@ void wpa_tdls_disassoc(struct wpa_sm *sm) static int wpa_tdls_prohibited(struct ieee802_11_elems *elems) { /* bit 38 - TDLS Prohibited */ - return !!(elems->ext_capab[2 + 4] & 0x40); + return !!(elems->ext_capab[4] & 0x40); } static int wpa_tdls_chan_switch_prohibited(struct ieee802_11_elems *elems) { /* bit 39 - TDLS Channel Switch Prohibited */ - return !!(elems->ext_capab[2 + 4] & 0x80); + return !!(elems->ext_capab[4] & 0x80); } @@ -2932,7 +2938,7 @@ void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len) if (ies == NULL || ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed || - elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) + elems.ext_capab == NULL || elems.ext_capab_len < 5) return; sm->tdls_prohibited = wpa_tdls_prohibited(&elems); @@ -2951,7 +2957,7 @@ void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len) if (ies == NULL || ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed || - elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) + elems.ext_capab == NULL || elems.ext_capab_len < 5) return; if (!sm->tdls_prohibited && wpa_tdls_prohibited(&elems)) { diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c index dcd75272151fb..e0c913074c631 100644 --- a/src/rsn_supp/wpa.c +++ b/src/rsn_supp/wpa.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - WPA state machine and EAPOL-Key processing - * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> * Copyright(c) 2015 Intel Deutschland GmbH * * This software may be distributed under the terms of the BSD license. @@ -10,10 +10,17 @@ #include "includes.h" #include "common.h" +#include "crypto/aes.h" #include "crypto/aes_wrap.h" #include "crypto/crypto.h" #include "crypto/random.h" +#include "crypto/aes_siv.h" +#include "crypto/sha256.h" +#include "crypto/sha384.h" +#include "crypto/sha512.h" #include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "eap_common/eap_defs.h" #include "eapol_supp/eapol_supp_sm.h" #include "wpa.h" #include "eloop.h" @@ -21,7 +28,6 @@ #include "pmksa_cache.h" #include "wpa_i.h" #include "wpa_ie.h" -#include "peerkey.h" static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -30,8 +36,7 @@ static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; /** * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message * @sm: Pointer to WPA state machine data from wpa_sm_init() - * @kck: Key Confirmation Key (KCK, part of PTK) - * @kck_len: KCK length in octets + * @ptk: PTK for Key Confirmation/Encryption Key * @ver: Version field from Key Info * @dest: Destination address for the frame * @proto: Ethertype (usually ETH_P_EAPOL) @@ -40,13 +45,16 @@ static const u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written * Returns: >= 0 on success, < 0 on failure */ -int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, +int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk, int ver, const u8 *dest, u16 proto, u8 *msg, size_t msg_len, u8 *key_mic) { int ret = -1; - size_t mic_len = wpa_mic_len(sm->key_mgmt); + size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); + wpa_printf(MSG_DEBUG, "WPA: Send EAPOL-Key frame to " MACSTR + " ver=%d mic_len=%d key_mgmt=0x%x", + MAC2STR(dest), ver, (int) mic_len, sm->key_mgmt); if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) { /* * Association event was not yet received; try to fetch @@ -64,16 +72,89 @@ int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, MAC2STR(dest)); } } - if (key_mic && - wpa_eapol_key_mic(kck, kck_len, sm->key_mgmt, ver, msg, msg_len, - key_mic)) { - wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, - "WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC", - ver, sm->key_mgmt); + + if (mic_len) { + if (key_mic && (!ptk || !ptk->kck_len)) + goto out; + + if (key_mic && + wpa_eapol_key_mic(ptk->kck, ptk->kck_len, sm->key_mgmt, ver, + msg, msg_len, key_mic)) { + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC", + ver, sm->key_mgmt); + goto out; + } + if (ptk) + wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", + ptk->kck, ptk->kck_len); + wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", + key_mic, mic_len); + } else { +#ifdef CONFIG_FILS + /* AEAD cipher - Key MIC field not used */ + struct ieee802_1x_hdr *s_hdr, *hdr; + struct wpa_eapol_key *s_key, *key; + u8 *buf, *s_key_data, *key_data; + size_t buf_len = msg_len + AES_BLOCK_SIZE; + size_t key_data_len; + u16 eapol_len; + const u8 *aad[1]; + size_t aad_len[1]; + + if (!ptk || !ptk->kek_len) + goto out; + + key_data_len = msg_len - sizeof(struct ieee802_1x_hdr) - + sizeof(struct wpa_eapol_key) - 2; + + buf = os_malloc(buf_len); + if (!buf) + goto out; + + os_memcpy(buf, msg, msg_len); + hdr = (struct ieee802_1x_hdr *) buf; + key = (struct wpa_eapol_key *) (hdr + 1); + key_data = ((u8 *) (key + 1)) + 2; + + /* Update EAPOL header to include AES-SIV overhead */ + eapol_len = be_to_host16(hdr->length); + eapol_len += AES_BLOCK_SIZE; + hdr->length = host_to_be16(eapol_len); + + /* Update Key Data Length field to include AES-SIV overhead */ + WPA_PUT_BE16((u8 *) (key + 1), AES_BLOCK_SIZE + key_data_len); + + s_hdr = (struct ieee802_1x_hdr *) msg; + s_key = (struct wpa_eapol_key *) (s_hdr + 1); + s_key_data = ((u8 *) (s_key + 1)) + 2; + + wpa_hexdump_key(MSG_DEBUG, "WPA: Plaintext Key Data", + s_key_data, key_data_len); + + wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", ptk->kek, ptk->kek_len); + /* AES-SIV AAD from EAPOL protocol version field (inclusive) to + * to Key Data (exclusive). */ + aad[0] = buf; + aad_len[0] = key_data - buf; + if (aes_siv_encrypt(ptk->kek, ptk->kek_len, + s_key_data, key_data_len, + 1, aad, aad_len, key_data) < 0) { + os_free(buf); + goto out; + } + + wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV", + key_data, AES_BLOCK_SIZE + key_data_len); + + os_free(msg); + msg = buf; + msg_len = buf_len; +#else /* CONFIG_FILS */ goto out; +#endif /* CONFIG_FILS */ } - wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len); - wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len); + wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len); ret = wpa_sm_ether_send(sm, dest, proto, msg, msg_len); eapol_sm_notify_tx_eapol_key(sm->eapol); @@ -97,12 +178,10 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) { size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; - struct wpa_eapol_key_192 *reply192; int key_info, ver; - u8 bssid[ETH_ALEN], *rbuf, *key_mic; + u8 bssid[ETH_ALEN], *rbuf, *key_mic, *mic; - if (sm->key_mgmt == WPA_KEY_MGMT_OSEN || - wpa_key_mgmt_suite_b(sm->key_mgmt)) + if (wpa_use_akm_defined(sm->key_mgmt)) ver = WPA_KEY_INFO_TYPE_AKM_DEFINED; else if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt)) @@ -118,20 +197,21 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) return; } - mic_len = wpa_mic_len(sm->key_mgmt); - hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); + mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); + hdrlen = sizeof(*reply) + mic_len + 2; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen, &rlen, (void *) &reply); if (rbuf == NULL) return; - reply192 = (struct wpa_eapol_key_192 *) reply; reply->type = (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info = WPA_KEY_INFO_REQUEST | ver; if (sm->ptk_set) - key_info |= WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; + key_info |= WPA_KEY_INFO_SECURE; + if (sm->ptk_set && mic_len) + key_info |= WPA_KEY_INFO_MIC; if (error) key_info |= WPA_KEY_INFO_ERROR; if (pairwise) @@ -142,21 +222,19 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) WPA_REPLAY_COUNTER_LEN); inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); - if (mic_len == 24) - WPA_PUT_BE16(reply192->key_data_length, 0); - else - WPA_PUT_BE16(reply->key_data_length, 0); + mic = (u8 *) (reply + 1); + WPA_PUT_BE16(mic + mic_len, 0); if (!(key_info & WPA_KEY_INFO_MIC)) key_mic = NULL; else - key_mic = reply192->key_mic; /* same offset in reply */ + key_mic = mic; wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Sending EAPOL-Key Request (error=%d " "pairwise=%d ptk_set=%d len=%lu)", error, pairwise, sm->ptk_set, (unsigned long) rlen); - wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid, - ETH_P_EAPOL, rbuf, rlen, key_mic); + wpa_eapol_key_send(sm, &sm->ptk, ver, bssid, ETH_P_EAPOL, rbuf, rlen, + key_mic); } @@ -190,7 +268,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, * event before receiving this 1/4 message, so try to find a * matching PMKSA cache entry here. */ sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid, - NULL); + NULL, 0); if (sm->cur_pmksa) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: found matching PMKID from PMKSA cache"); @@ -210,11 +288,23 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, eapol_sm_notify_cached(sm->eapol); #ifdef CONFIG_IEEE80211R sm->xxkey_len = 0; +#ifdef CONFIG_SAE + if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE && + sm->pmk_len == PMK_LEN) { + /* Need to allow FT key derivation to proceed with + * PMK from SAE being used as the XXKey in cases where + * the PMKID in msg 1/4 matches the PMKSA entry that was + * just added based on SAE authentication for the + * initial mobility domain association. */ + os_memcpy(sm->xxkey, sm->pmk, sm->pmk_len); + sm->xxkey_len = sm->pmk_len; + } +#endif /* CONFIG_SAE */ #endif /* CONFIG_IEEE80211R */ } else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) { int res, pmk_len; - if (sm->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + if (wpa_key_mgmt_sha384(sm->key_mgmt)) pmk_len = PMK_LEN_SUITE_B_192; else pmk_len = PMK_LEN; @@ -233,14 +323,28 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, u8 buf[2 * PMK_LEN]; if (eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0) { - os_memcpy(sm->xxkey, buf + PMK_LEN, PMK_LEN); - sm->xxkey_len = PMK_LEN; + if (wpa_key_mgmt_sha384(sm->key_mgmt)) { + os_memcpy(sm->xxkey, buf, + SHA384_MAC_LEN); + sm->xxkey_len = SHA384_MAC_LEN; + } else { + os_memcpy(sm->xxkey, buf + PMK_LEN, + PMK_LEN); + sm->xxkey_len = PMK_LEN; + } os_memset(buf, 0, sizeof(buf)); } #endif /* CONFIG_IEEE80211R */ } if (res == 0) { struct rsn_pmksa_cache_entry *sa = NULL; + const u8 *fils_cache_id = NULL; + +#ifdef CONFIG_FILS + if (sm->fils_cache_id_set) + fils_cache_id = sm->fils_cache_id; +#endif /* CONFIG_FILS */ + wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state " "machines", sm->pmk, pmk_len); sm->pmk_len = pmk_len; @@ -253,11 +357,12 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, NULL, 0, src_addr, sm->own_addr, sm->network_ctx, - sm->key_mgmt); + sm->key_mgmt, + fils_cache_id); } if (!sm->cur_pmksa && pmkid && - pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL)) - { + pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL, + 0)) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: the new PMK matches with the " "PMKID"); @@ -341,9 +446,9 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, { size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; - struct wpa_eapol_key_192 *reply192; u8 *rbuf, *key_mic; u8 *rsn_ie_buf = NULL; + u16 key_info; if (wpa_ie == NULL) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - " @@ -383,8 +488,8 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len); - mic_len = wpa_mic_len(sm->key_mgmt); - hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); + mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); + hdrlen = sizeof(*reply) + mic_len + 2; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen + wpa_ie_len, &rlen, (void *) &reply); @@ -392,13 +497,16 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, os_free(rsn_ie_buf); return -1; } - reply192 = (struct wpa_eapol_key_192 *) reply; reply->type = (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; - WPA_PUT_BE16(reply->key_info, - ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC); + key_info = ver | WPA_KEY_INFO_KEY_TYPE; + if (mic_len) + key_info |= WPA_KEY_INFO_MIC; + else + key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; + WPA_PUT_BE16(reply->key_info, key_info); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) WPA_PUT_BE16(reply->key_length, 0); else @@ -408,21 +516,16 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter, WPA_REPLAY_COUNTER_LEN); - key_mic = reply192->key_mic; /* same offset for reply and reply192 */ - if (mic_len == 24) { - WPA_PUT_BE16(reply192->key_data_length, wpa_ie_len); - os_memcpy(reply192 + 1, wpa_ie, wpa_ie_len); - } else { - WPA_PUT_BE16(reply->key_data_length, wpa_ie_len); - os_memcpy(reply + 1, wpa_ie, wpa_ie_len); - } + key_mic = (u8 *) (reply + 1); + WPA_PUT_BE16(key_mic + mic_len, wpa_ie_len); /* Key Data Length */ + os_memcpy(key_mic + mic_len + 2, wpa_ie, wpa_ie_len); /* Key Data */ os_free(rsn_ie_buf); os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4"); - return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, - ETH_P_EAPOL, rbuf, rlen, key_mic); + return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen, + key_mic); } @@ -500,7 +603,8 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, /* Calculate PTK which will be stored as a temporary PTK until it has * been verified when processing message 3/4. */ ptk = &sm->tptk; - wpa_derive_ptk(sm, src_addr, key, ptk); + if (wpa_derive_ptk(sm, src_addr, key, ptk) < 0) + goto failed; if (sm->pairwise_cipher == WPA_CIPHER_TKIP) { u8 buf[8]; /* Supplicant: swap tx/rx Mic keys */ @@ -571,7 +675,9 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, sm, addr, MLME_SETPROTECTION_PROTECT_TYPE_RX_TX, MLME_SETPROTECTION_KEY_TYPE_PAIRWISE); eapol_sm_notify_portValid(sm->eapol, TRUE); - if (wpa_key_mgmt_wpa_psk(sm->key_mgmt)) + if (wpa_key_mgmt_wpa_psk(sm->key_mgmt) || + sm->key_mgmt == WPA_KEY_MGMT_DPP || + sm->key_mgmt == WPA_KEY_MGMT_OWE) eapol_sm_notify_eap_success(sm->eapol, TRUE); /* * Start preauthentication after a short wait to avoid a @@ -638,6 +744,11 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, alg = wpa_cipher_to_alg(sm->pairwise_cipher); keylen = wpa_cipher_key_len(sm->pairwise_cipher); + if (keylen <= 0 || (unsigned int) keylen != sm->ptk.tk_len) { + wpa_printf(MSG_DEBUG, "WPA: TK length mismatch: %d != %lu", + keylen, (long unsigned int) sm->ptk.tk_len); + return -1; + } rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) { @@ -658,6 +769,7 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, /* TK is not needed anymore in supplicant */ os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN); + sm->ptk.tk_len = 0; sm->ptk.installed = 1; if (sm->wpa_ptk_rekey) { @@ -895,7 +1007,7 @@ static int wpa_supplicant_install_igtk(struct wpa_sm *sm, } wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, - "WPA: IGTK keyid %d pn %02x%02x%02x%02x%02x%02x", + "WPA: IGTK keyid %d pn " COMPACT_MACSTR, keyidx, MAC2STR(igtk->pn)); wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", igtk->igtk, len); if (keyidx > 4095) { @@ -1203,22 +1315,24 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, { size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; - struct wpa_eapol_key_192 *reply192; u8 *rbuf, *key_mic; - mic_len = wpa_mic_len(sm->key_mgmt); - hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); + mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); + hdrlen = sizeof(*reply) + mic_len + 2; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen, &rlen, (void *) &reply); if (rbuf == NULL) return -1; - reply192 = (struct wpa_eapol_key_192 *) reply; reply->type = (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info &= WPA_KEY_INFO_SECURE; - key_info |= ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC; + key_info |= ver | WPA_KEY_INFO_KEY_TYPE; + if (mic_len) + key_info |= WPA_KEY_INFO_MIC; + else + key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; WPA_PUT_BE16(reply->key_info, key_info); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) WPA_PUT_BE16(reply->key_length, 0); @@ -1227,15 +1341,12 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, os_memcpy(reply->replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); - key_mic = reply192->key_mic; /* same offset for reply and reply192 */ - if (mic_len == 24) - WPA_PUT_BE16(reply192->key_data_length, 0); - else - WPA_PUT_BE16(reply->key_data_length, 0); + key_mic = (u8 *) (reply + 1); + WPA_PUT_BE16(key_mic + mic_len, 0); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); - return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, - ETH_P_EAPOL, rbuf, rlen, key_mic); + return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen, + key_mic); } @@ -1350,13 +1461,19 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, if (ie.gtk) wpa_sm_set_rekey_offload(sm); - if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt)) { + /* Add PMKSA cache entry for Suite B AKMs here since PMKID can be + * calculated only after KCK has been derived. Though, do not replace an + * existing PMKSA entry after each 4-way handshake (i.e., new KCK/PMKID) + * to avoid unnecessary changes of PMKID while continuing to use the + * same PMK. */ + if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt) && + !sm->cur_pmksa) { struct rsn_pmksa_cache_entry *sa; sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL, sm->ptk.kck, sm->ptk.kck_len, sm->bssid, sm->own_addr, - sm->network_ctx, sm->key_mgmt); + sm->network_ctx, sm->key_mgmt, NULL); if (!sm->cur_pmksa) sm->cur_pmksa = sa; } @@ -1378,7 +1495,8 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, int maxkeylen; struct wpa_eapol_ie_parse ie; - wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen); + wpa_hexdump_key(MSG_DEBUG, "RSN: msg 1/2 key data", + keydata, keydatalen); if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0) return -1; if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { @@ -1512,22 +1630,24 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, { size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; - struct wpa_eapol_key_192 *reply192; u8 *rbuf, *key_mic; - mic_len = wpa_mic_len(sm->key_mgmt); - hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); + mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); + hdrlen = sizeof(*reply) + mic_len + 2; rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, hdrlen, &rlen, (void *) &reply); if (rbuf == NULL) return -1; - reply192 = (struct wpa_eapol_key_192 *) reply; reply->type = (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; key_info &= WPA_KEY_INFO_KEY_INDEX_MASK; - key_info |= ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE; + key_info |= ver | WPA_KEY_INFO_SECURE; + if (mic_len) + key_info |= WPA_KEY_INFO_MIC; + else + key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; WPA_PUT_BE16(reply->key_info, key_info); if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) WPA_PUT_BE16(reply->key_length, 0); @@ -1536,15 +1656,12 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, os_memcpy(reply->replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); - key_mic = reply192->key_mic; /* same offset for reply and reply192 */ - if (mic_len == 24) - WPA_PUT_BE16(reply192->key_data_length, 0); - else - WPA_PUT_BE16(reply->key_data_length, 0); + key_mic = (u8 *) (reply + 1); + WPA_PUT_BE16(key_mic + mic_len, 0); wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); - return wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, - sm->bssid, ETH_P_EAPOL, rbuf, rlen, key_mic); + return wpa_eapol_key_send(sm, &sm->ptk, ver, sm->bssid, ETH_P_EAPOL, + rbuf, rlen, key_mic); } @@ -1559,7 +1676,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, struct wpa_gtk_data gd; const u8 *key_rsc; - if (!sm->msg_3_of_4_ok) { + if (!sm->msg_3_of_4_ok && !wpa_fils_is_completed(sm)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group Key Handshake started prior to completion of 4-way handshake"); goto failed; @@ -1620,20 +1737,21 @@ failed: static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, - struct wpa_eapol_key_192 *key, + struct wpa_eapol_key *key, u16 ver, const u8 *buf, size_t len) { u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; int ok = 0; - size_t mic_len = wpa_mic_len(sm->key_mgmt); + size_t mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); - os_memcpy(mic, key->key_mic, mic_len); + os_memcpy(mic, key + 1, mic_len); if (sm->tptk_set) { - os_memset(key->key_mic, 0, mic_len); - wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, sm->key_mgmt, - ver, buf, len, key->key_mic); - if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) { + os_memset(key + 1, 0, mic_len); + if (wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, + sm->key_mgmt, + ver, buf, len, (u8 *) (key + 1)) < 0 || + os_memcmp_const(mic, key + 1, mic_len) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid EAPOL-Key MIC " "when using TPTK - ignoring TPTK"); @@ -1643,14 +1761,23 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, sm->ptk_set = 1; os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk)); os_memset(&sm->tptk, 0, sizeof(sm->tptk)); + /* + * This assures the same TPTK in sm->tptk can never be + * copied twice to sm->ptk as the new PTK. In + * combination with the installed flag in the wpa_ptk + * struct, this assures the same PTK is only installed + * once. + */ + sm->renew_snonce = 1; } } if (!ok && sm->ptk_set) { - os_memset(key->key_mic, 0, mic_len); - wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, sm->key_mgmt, - ver, buf, len, key->key_mic); - if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) { + os_memset(key + 1, 0, mic_len); + if (wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, + sm->key_mgmt, + ver, buf, len, (u8 *) (key + 1)) < 0 || + os_memcmp_const(mic, key + 1, mic_len) != 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Invalid EAPOL-Key MIC - " "dropping packet"); @@ -1675,7 +1802,8 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, /* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, - struct wpa_eapol_key *key, u16 ver, + struct wpa_eapol_key *key, + size_t mic_len, u16 ver, u8 *key_data, size_t *key_data_len) { wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data", @@ -1696,6 +1824,8 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, return -1; #else /* CONFIG_NO_RC4 */ u8 ek[32]; + + wpa_printf(MSG_DEBUG, "WPA: Decrypt Key Data using RC4"); os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len); if (rc4_skip(ek, 32, 256, key_data, *key_data_len)) { @@ -1708,9 +1838,12 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, #endif /* CONFIG_NO_RC4 */ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || ver == WPA_KEY_INFO_TYPE_AES_128_CMAC || - sm->key_mgmt == WPA_KEY_MGMT_OSEN || - wpa_key_mgmt_suite_b(sm->key_mgmt)) { + wpa_use_aes_key_wrap(sm->key_mgmt)) { u8 *buf; + + wpa_printf(MSG_DEBUG, + "WPA: Decrypt Key Data using AES-UNWRAP (KEK length %u)", + (unsigned int) sm->ptk.kek_len); if (*key_data_len < 8 || *key_data_len % 8) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported AES-WRAP len %u", @@ -1734,7 +1867,7 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, } os_memcpy(key_data, buf, *key_data_len); bin_clear_free(buf, *key_data_len); - WPA_PUT_BE16(key->key_data_length, *key_data_len); + WPA_PUT_BE16(((u8 *) (key + 1)) + mic_len, *key_data_len); } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported key_info type %d", ver); @@ -1797,6 +1930,76 @@ static void wpa_eapol_key_dump(struct wpa_sm *sm, } +#ifdef CONFIG_FILS +static int wpa_supp_aead_decrypt(struct wpa_sm *sm, u8 *buf, size_t buf_len, + size_t *key_data_len) +{ + struct wpa_ptk *ptk; + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + u8 *pos, *tmp; + const u8 *aad[1]; + size_t aad_len[1]; + + if (*key_data_len < AES_BLOCK_SIZE) { + wpa_printf(MSG_INFO, "No room for AES-SIV data in the frame"); + return -1; + } + + if (sm->tptk_set) + ptk = &sm->tptk; + else if (sm->ptk_set) + ptk = &sm->ptk; + else + return -1; + + hdr = (struct ieee802_1x_hdr *) buf; + key = (struct wpa_eapol_key *) (hdr + 1); + pos = (u8 *) (key + 1); + pos += 2; /* Pointing at the Encrypted Key Data field */ + + tmp = os_malloc(*key_data_len); + if (!tmp) + return -1; + + /* AES-SIV AAD from EAPOL protocol version field (inclusive) to + * to Key Data (exclusive). */ + aad[0] = buf; + aad_len[0] = pos - buf; + if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, *key_data_len, + 1, aad, aad_len, tmp) < 0) { + wpa_printf(MSG_INFO, "Invalid AES-SIV data in the frame"); + bin_clear_free(tmp, *key_data_len); + return -1; + } + + /* AEAD decryption and validation completed successfully */ + (*key_data_len) -= AES_BLOCK_SIZE; + wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data", + tmp, *key_data_len); + + /* Replace Key Data field with the decrypted version */ + os_memcpy(pos, tmp, *key_data_len); + pos -= 2; /* Key Data Length field */ + WPA_PUT_BE16(pos, *key_data_len); + bin_clear_free(tmp, *key_data_len); + + if (sm->tptk_set) { + sm->tptk_set = 0; + sm->ptk_set = 1; + os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk)); + os_memset(&sm->tptk, 0, sizeof(sm->tptk)); + } + + os_memcpy(sm->rx_replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + sm->rx_replay_counter_set = 1; + + return 0; +} +#endif /* CONFIG_FILS */ + + /** * wpa_sm_rx_eapol - Process received WPA EAPOL frames * @sm: Pointer to WPA state machine data from wpa_sm_init() @@ -1819,20 +2022,18 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, size_t plen, data_len, key_data_len; const struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; - struct wpa_eapol_key_192 *key192; u16 key_info, ver; u8 *tmp = NULL; int ret = -1; - struct wpa_peerkey *peerkey = NULL; - u8 *key_data; + u8 *mic, *key_data; size_t mic_len, keyhdrlen; #ifdef CONFIG_IEEE80211R sm->ft_completed = 0; #endif /* CONFIG_IEEE80211R */ - mic_len = wpa_mic_len(sm->key_mgmt); - keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key); + mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len); + keyhdrlen = sizeof(*key) + mic_len + 2; if (len < sizeof(*hdr) + keyhdrlen) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, @@ -1879,17 +2080,12 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, * Make a copy of the frame since we need to modify the buffer during * MAC validation and Key Data decryption. */ - tmp = os_malloc(data_len); + tmp = os_memdup(buf, data_len); if (tmp == NULL) goto out; - os_memcpy(tmp, buf, data_len); key = (struct wpa_eapol_key *) (tmp + sizeof(struct ieee802_1x_hdr)); - key192 = (struct wpa_eapol_key_192 *) - (tmp + sizeof(struct ieee802_1x_hdr)); - if (mic_len == 24) - key_data = (u8 *) (key192 + 1); - else - key_data = (u8 *) (key + 1); + mic = (u8 *) (key + 1); + key_data = mic + mic_len + 2; if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN) { @@ -1900,11 +2096,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, goto out; } - if (mic_len == 24) - key_data_len = WPA_GET_BE16(key192->key_data_length); - else - key_data_len = WPA_GET_BE16(key->key_data_length); - wpa_eapol_key_dump(sm, key, key_data_len, key192->key_mic, mic_len); + key_data_len = WPA_GET_BE16(mic + mic_len); + wpa_eapol_key_dump(sm, key, key_data_len, mic, mic_len); if (key_data_len > plen - keyhdrlen) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key " @@ -1922,23 +2115,14 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES && - !wpa_key_mgmt_suite_b(sm->key_mgmt) && - sm->key_mgmt != WPA_KEY_MGMT_OSEN) { + !wpa_use_akm_defined(sm->key_mgmt)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor version %d", ver); goto out; } - if (sm->key_mgmt == WPA_KEY_MGMT_OSEN && - ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { - wpa_msg(sm->ctx->msg_ctx, MSG_INFO, - "OSEN: Unsupported EAPOL-Key descriptor version %d", - ver); - goto out; - } - - if (wpa_key_mgmt_suite_b(sm->key_mgmt) && + if (wpa_use_akm_defined(sm->key_mgmt) && ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)", @@ -1949,7 +2133,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) { /* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */ - if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { + if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && + !wpa_use_akm_defined(sm->key_mgmt)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "FT: AP did not use AES-128-CMAC"); goto out; @@ -1959,8 +2144,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, #ifdef CONFIG_IEEE80211W if (wpa_key_mgmt_sha256(sm->key_mgmt)) { if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && - sm->key_mgmt != WPA_KEY_MGMT_OSEN && - !wpa_key_mgmt_suite_b(sm->key_mgmt)) { + !wpa_use_akm_defined(sm->key_mgmt)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: AP did not use the " "negotiated AES-128-CMAC"); @@ -1969,7 +2153,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, } else #endif /* CONFIG_IEEE80211W */ if (sm->pairwise_cipher == WPA_CIPHER_CCMP && - !wpa_key_mgmt_suite_b(sm->key_mgmt) && + !wpa_use_akm_defined(sm->key_mgmt) && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: CCMP is used, but EAPOL-Key " @@ -1989,7 +2173,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, } else goto out; } else if (sm->pairwise_cipher == WPA_CIPHER_GCMP && - !wpa_key_mgmt_suite_b(sm->key_mgmt) && + !wpa_use_akm_defined(sm->key_mgmt) && ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: GCMP is used, but EAPOL-Key " @@ -1997,44 +2181,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, goto out; } -#ifdef CONFIG_PEERKEY - for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { - if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0) - break; - } - - if (!(key_info & WPA_KEY_INFO_SMK_MESSAGE) && peerkey) { - if (!peerkey->initiator && peerkey->replay_counter_set && - os_memcmp(key->replay_counter, peerkey->replay_counter, - WPA_REPLAY_COUNTER_LEN) <= 0) { - wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "RSN: EAPOL-Key Replay Counter did not " - "increase (STK) - dropping packet"); - goto out; - } else if (peerkey->initiator) { - u8 _tmp[WPA_REPLAY_COUNTER_LEN]; - os_memcpy(_tmp, key->replay_counter, - WPA_REPLAY_COUNTER_LEN); - inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN); - if (os_memcmp(_tmp, peerkey->replay_counter, - WPA_REPLAY_COUNTER_LEN) != 0) { - wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, - "RSN: EAPOL-Key Replay " - "Counter did not match (STK) - " - "dropping packet"); - goto out; - } - } - } - - if (peerkey && peerkey->initiator && (key_info & WPA_KEY_INFO_ACK)) { - wpa_msg(sm->ctx->msg_ctx, MSG_INFO, - "RSN: Ack bit in key_info from STK peer"); - goto out; - } -#endif /* CONFIG_PEERKEY */ - - if (!peerkey && sm->rx_replay_counter_set && + if (sm->rx_replay_counter_set && os_memcmp(key->replay_counter, sm->rx_replay_counter, WPA_REPLAY_COUNTER_LEN) <= 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -2043,11 +2190,13 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, goto out; } - if (!(key_info & (WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE)) -#ifdef CONFIG_PEERKEY - && (peerkey == NULL || !peerkey->initiator) -#endif /* CONFIG_PEERKEY */ - ) { + if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Unsupported SMK bit in key_info"); + goto out; + } + + if (!(key_info & WPA_KEY_INFO_ACK)) { wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: No Ack bit in key_info"); goto out; @@ -2059,19 +2208,19 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, goto out; } - if ((key_info & WPA_KEY_INFO_MIC) && !peerkey && - wpa_supplicant_verify_eapol_key_mic(sm, key192, ver, tmp, data_len)) + if ((key_info & WPA_KEY_INFO_MIC) && + wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len)) goto out; -#ifdef CONFIG_PEERKEY - if ((key_info & WPA_KEY_INFO_MIC) && peerkey && - peerkey_verify_eapol_key_mic(sm, peerkey, key192, ver, tmp, - data_len)) - goto out; -#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_FILS + if (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + if (wpa_supp_aead_decrypt(sm, tmp, data_len, &key_data_len)) + goto out; + } +#endif /* CONFIG_FILS */ if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) && - (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && mic_len) { /* * Only decrypt the Key Data field if the frame's authenticity * was verified. When using AES-SIV (FILS), the MIC flag is not @@ -2083,7 +2232,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, "WPA: Ignore EAPOL-Key with encrypted but unauthenticated data"); goto out; } - if (wpa_supplicant_decrypt_key_data(sm, key, ver, key_data, + if (wpa_supplicant_decrypt_key_data(sm, key, mic_len, + ver, key_data, &key_data_len)) goto out; } @@ -2095,11 +2245,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, "non-zero key index"); goto out; } - if (peerkey) { - /* PeerKey 4-Way Handshake */ - peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver, - key_data, key_data_len); - } else if (key_info & WPA_KEY_INFO_MIC) { + if (key_info & (WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ENCR_KEY_DATA)) { /* 3/4 4-Way Handshake */ wpa_supplicant_process_3_of_4(sm, key, ver, key_data, key_data_len); @@ -2109,19 +2256,16 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, ver, key_data, key_data_len); } - } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { - /* PeerKey SMK Handshake */ - peerkey_rx_eapol_smk(sm, src_addr, key, key_data_len, key_info, - ver); } else { - if (key_info & WPA_KEY_INFO_MIC) { + if ((mic_len && (key_info & WPA_KEY_INFO_MIC)) || + (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA))) { /* 1/2 Group Key Handshake */ wpa_supplicant_process_1_of_2(sm, src_addr, key, key_data, key_data_len, ver); } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "WPA: EAPOL-Key (Group) without Mic bit - " + "WPA: EAPOL-Key (Group) without Mic/Encr bit - " "dropped"); } } @@ -2296,6 +2440,7 @@ static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, } if (deauth) { + sm->pmk_len = 0; os_memset(sm->pmk, 0, sizeof(sm->pmk)); wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); } @@ -2353,13 +2498,21 @@ void wpa_sm_deinit(struct wpa_sm *sm) os_free(sm->ap_rsn_ie); wpa_sm_drop_sa(sm); os_free(sm->ctx); - peerkey_deinit(sm); #ifdef CONFIG_IEEE80211R os_free(sm->assoc_resp_ies); #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_TESTING_OPTIONS wpabuf_free(sm->test_assoc_ie); #endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_FILS_SK_PFS + crypto_ecdh_deinit(sm->fils_ecdh); +#endif /* CONFIG_FILS_SK_PFS */ +#ifdef CONFIG_FILS + wpabuf_free(sm->fils_ft_ies); +#endif /* CONFIG_FILS */ +#ifdef CONFIG_OWE + crypto_ecdh_deinit(sm->owe_ecdh); +#endif /* CONFIG_OWE */ os_free(sm); } @@ -2403,6 +2556,16 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) clear_keys = 0; } #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_FILS + if (sm->fils_completed) { + /* + * Clear portValid to kick EAPOL state machine to re-enter + * AUTHENTICATED state to get the EAPOL port Authorized. + */ + wpa_supplicant_key_neg_complete(sm, sm->bssid, 1); + clear_keys = 0; + } +#endif /* CONFIG_FILS */ if (clear_keys) { /* @@ -2443,7 +2606,6 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) { eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL); eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); - peerkey_deinit(sm); rsn_preauth_deinit(sm); pmksa_cache_clear_current(sm); if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE) @@ -2451,6 +2613,9 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) #ifdef CONFIG_TDLS wpa_tdls_disassoc(sm); #endif /* CONFIG_TDLS */ +#ifdef CONFIG_FILS + sm->fils_completed = 0; +#endif /* CONFIG_FILS */ #ifdef CONFIG_IEEE80211R sm->ft_reassoc_completed = 0; #endif /* CONFIG_IEEE80211R */ @@ -2459,6 +2624,7 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) wpa_sm_drop_sa(sm); sm->msg_3_of_4_ok = 0; + os_memset(sm->bssid, 0, ETH_ALEN); } @@ -2478,6 +2644,8 @@ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, if (sm == NULL) return; + wpa_hexdump_key(MSG_DEBUG, "WPA: Set PMK based on external data", + pmk, pmk_len); sm->pmk_len = pmk_len; os_memcpy(sm->pmk, pmk, pmk_len); @@ -2490,7 +2658,7 @@ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, if (bssid) { pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0, bssid, sm->own_addr, - sm->network_ctx, sm->key_mgmt); + sm->network_ctx, sm->key_mgmt, NULL); } } @@ -2508,11 +2676,15 @@ void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm) return; if (sm->cur_pmksa) { + wpa_hexdump_key(MSG_DEBUG, + "WPA: Set PMK based on current PMKSA", + sm->cur_pmksa->pmk, sm->cur_pmksa->pmk_len); sm->pmk_len = sm->cur_pmksa->pmk_len; os_memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len); } else { - sm->pmk_len = PMK_LEN; - os_memset(sm->pmk, 0, PMK_LEN); + wpa_printf(MSG_DEBUG, "WPA: No current PMKSA - clear PMK"); + sm->pmk_len = 0; + os_memset(sm->pmk, 0, PMK_LEN_MAX); } } @@ -2560,7 +2732,6 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) if (config) { sm->network_ctx = config->network_ctx; - sm->peerkey_enabled = config->peerkey_enabled; sm->allowed_pairwise_cipher = config->allowed_pairwise_cipher; sm->proactive_key_caching = config->proactive_key_caching; sm->eap_workaround = config->eap_workaround; @@ -2573,9 +2744,17 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) sm->wpa_ptk_rekey = config->wpa_ptk_rekey; sm->p2p = config->p2p; sm->wpa_rsc_relaxation = config->wpa_rsc_relaxation; +#ifdef CONFIG_FILS + if (config->fils_cache_id) { + sm->fils_cache_id_set = 1; + os_memcpy(sm->fils_cache_id, config->fils_cache_id, + FILS_CACHE_ID_LEN); + } else { + sm->fils_cache_id_set = 0; + } +#endif /* CONFIG_FILS */ } else { sm->network_ctx = NULL; - sm->peerkey_enabled = 0; sm->allowed_pairwise_cipher = 0; sm->proactive_key_caching = 0; sm->eap_workaround = 0; @@ -2728,9 +2907,12 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, >= 0 && rsn.capabilities & (WPA_CAPABILITY_MFPR | WPA_CAPABILITY_MFPC)) { - ret = os_snprintf(pos, end - pos, "pmf=%d\n", + ret = os_snprintf(pos, end - pos, "pmf=%d\n" + "mgmt_group_cipher=%s\n", (rsn.capabilities & - WPA_CAPABILITY_MFPR) ? 2 : 1); + WPA_CAPABILITY_MFPR) ? 2 : 1, + wpa_cipher_txt( + sm->mgmt_group_cipher)); if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -2796,12 +2978,15 @@ int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie, * the correct version of the IE even if PMKSA caching is * aborted (which would remove PMKID from IE generation). */ - sm->assoc_wpa_ie = os_malloc(*wpa_ie_len); + sm->assoc_wpa_ie = os_memdup(wpa_ie, *wpa_ie_len); if (sm->assoc_wpa_ie == NULL) return -1; - os_memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len); sm->assoc_wpa_ie_len = *wpa_ie_len; + } else { + wpa_hexdump(MSG_DEBUG, + "WPA: Leave previously set WPA IE default", + sm->assoc_wpa_ie, sm->assoc_wpa_ie_len); } return 0; @@ -2832,11 +3017,10 @@ int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) sm->assoc_wpa_ie_len = 0; } else { wpa_hexdump(MSG_DEBUG, "WPA: set own WPA/RSN IE", ie, len); - sm->assoc_wpa_ie = os_malloc(len); + sm->assoc_wpa_ie = os_memdup(ie, len); if (sm->assoc_wpa_ie == NULL) return -1; - os_memcpy(sm->assoc_wpa_ie, ie, len); sm->assoc_wpa_ie_len = len; } @@ -2867,11 +3051,10 @@ int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len) sm->ap_wpa_ie_len = 0; } else { wpa_hexdump(MSG_DEBUG, "WPA: set AP WPA IE", ie, len); - sm->ap_wpa_ie = os_malloc(len); + sm->ap_wpa_ie = os_memdup(ie, len); if (sm->ap_wpa_ie == NULL) return -1; - os_memcpy(sm->ap_wpa_ie, ie, len); sm->ap_wpa_ie_len = len; } @@ -2902,11 +3085,10 @@ int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len) sm->ap_rsn_ie_len = 0; } else { wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len); - sm->ap_rsn_ie = os_malloc(len); + sm->ap_rsn_ie = os_memdup(ie, len); if (sm->ap_rsn_ie == NULL) return -1; - os_memcpy(sm->ap_rsn_ie, ie, len); sm->ap_rsn_ie_len = len; } @@ -2945,11 +3127,43 @@ int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) } +struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_head(struct wpa_sm *sm) +{ + return pmksa_cache_head(sm->pmksa); +} + + +struct rsn_pmksa_cache_entry * +wpa_sm_pmksa_cache_add_entry(struct wpa_sm *sm, + struct rsn_pmksa_cache_entry * entry) +{ + return pmksa_cache_add_entry(sm->pmksa, entry); +} + + +void wpa_sm_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, + const u8 *pmkid, const u8 *bssid, + const u8 *fils_cache_id) +{ + sm->cur_pmksa = pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0, + bssid, sm->own_addr, sm->network_ctx, + sm->key_mgmt, fils_cache_id); +} + + +int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid, + const void *network_ctx) +{ + return pmksa_cache_get(sm->pmksa, bssid, NULL, network_ctx, 0) != NULL; +} + + void wpa_sm_drop_sa(struct wpa_sm *sm) { wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK"); sm->ptk_set = 0; sm->tptk_set = 0; + sm->pmk_len = 0; os_memset(sm->pmk, 0, sizeof(sm->pmk)); os_memset(&sm->ptk, 0, sizeof(sm->ptk)); os_memset(&sm->tptk, 0, sizeof(sm->tptk)); @@ -2961,8 +3175,11 @@ void wpa_sm_drop_sa(struct wpa_sm *sm) #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_IEEE80211R os_memset(sm->xxkey, 0, sizeof(sm->xxkey)); + sm->xxkey_len = 0; os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0)); + sm->pmk_r0_len = 0; os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1)); + sm->pmk_r1_len = 0; #endif /* CONFIG_IEEE80211R */ } @@ -3047,27 +3264,6 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) #endif /* CONFIG_WNM */ -#ifdef CONFIG_PEERKEY -int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, - const u8 *buf, size_t len) -{ - struct wpa_peerkey *peerkey; - - for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { - if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0) - break; - } - - if (!peerkey) - return 0; - - wpa_sm_rx_eapol(sm, src_addr, buf, len); - - return 1; -} -#endif /* CONFIG_PEERKEY */ - - #ifdef CONFIG_P2P int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf) @@ -3112,9 +3308,1130 @@ void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, #ifdef CONFIG_TESTING_OPTIONS + void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf) { wpabuf_free(sm->test_assoc_ie); sm->test_assoc_ie = buf; } + + +const u8 * wpa_sm_get_anonce(struct wpa_sm *sm) +{ + return sm->anonce; +} + #endif /* CONFIG_TESTING_OPTIONS */ + + +unsigned int wpa_sm_get_key_mgmt(struct wpa_sm *sm) +{ + return sm->key_mgmt; +} + + +#ifdef CONFIG_FILS + +struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md) +{ + struct wpabuf *buf = NULL; + struct wpabuf *erp_msg; + struct wpabuf *pub = NULL; + + erp_msg = eapol_sm_build_erp_reauth_start(sm->eapol); + if (!erp_msg && !sm->cur_pmksa) { + wpa_printf(MSG_DEBUG, + "FILS: Neither ERP EAP-Initiate/Re-auth nor PMKSA cache entry is available - skip FILS"); + goto fail; + } + + wpa_printf(MSG_DEBUG, "FILS: Try to use FILS (erp=%d pmksa_cache=%d)", + erp_msg != NULL, sm->cur_pmksa != NULL); + + sm->fils_completed = 0; + + if (!sm->assoc_wpa_ie) { + wpa_printf(MSG_INFO, "FILS: No own RSN IE set for FILS"); + goto fail; + } + + if (random_get_bytes(sm->fils_nonce, FILS_NONCE_LEN) < 0 || + random_get_bytes(sm->fils_session, FILS_SESSION_LEN) < 0) + goto fail; + + wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Nonce", + sm->fils_nonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Session", + sm->fils_session, FILS_SESSION_LEN); + +#ifdef CONFIG_FILS_SK_PFS + sm->fils_dh_group = dh_group; + if (dh_group) { + crypto_ecdh_deinit(sm->fils_ecdh); + sm->fils_ecdh = crypto_ecdh_init(dh_group); + if (!sm->fils_ecdh) { + wpa_printf(MSG_INFO, + "FILS: Could not initialize ECDH with group %d", + dh_group); + goto fail; + } + pub = crypto_ecdh_get_pubkey(sm->fils_ecdh, 1); + if (!pub) + goto fail; + wpa_hexdump_buf(MSG_DEBUG, "FILS: Element (DH public key)", + pub); + sm->fils_dh_elem_len = wpabuf_len(pub); + } +#endif /* CONFIG_FILS_SK_PFS */ + + buf = wpabuf_alloc(1000 + sm->assoc_wpa_ie_len + + (pub ? wpabuf_len(pub) : 0)); + if (!buf) + goto fail; + + /* Fields following the Authentication algorithm number field */ + + /* Authentication Transaction seq# */ + wpabuf_put_le16(buf, 1); + + /* Status Code */ + wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); + + /* TODO: FILS PK */ +#ifdef CONFIG_FILS_SK_PFS + if (dh_group) { + /* Finite Cyclic Group */ + wpabuf_put_le16(buf, dh_group); + /* Element */ + wpabuf_put_buf(buf, pub); + } +#endif /* CONFIG_FILS_SK_PFS */ + + /* RSNE */ + wpa_hexdump(MSG_DEBUG, "FILS: RSNE in FILS Authentication frame", + sm->assoc_wpa_ie, sm->assoc_wpa_ie_len); + wpabuf_put_data(buf, sm->assoc_wpa_ie, sm->assoc_wpa_ie_len); + + if (md) { + /* MDE when using FILS for FT initial association */ + struct rsn_mdie *mdie; + + wpabuf_put_u8(buf, WLAN_EID_MOBILITY_DOMAIN); + wpabuf_put_u8(buf, sizeof(*mdie)); + mdie = wpabuf_put(buf, sizeof(*mdie)); + os_memcpy(mdie->mobility_domain, md, MOBILITY_DOMAIN_ID_LEN); + mdie->ft_capab = 0; + } + + /* FILS Nonce */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE); + wpabuf_put_data(buf, sm->fils_nonce, FILS_NONCE_LEN); + + /* FILS Session */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION); + wpabuf_put_data(buf, sm->fils_session, FILS_SESSION_LEN); + + /* FILS Wrapped Data */ + sm->fils_erp_pmkid_set = 0; + if (erp_msg) { + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(buf, 1 + wpabuf_len(erp_msg)); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_WRAPPED_DATA); + wpabuf_put_buf(buf, erp_msg); + /* Calculate pending PMKID here so that we do not need to + * maintain a copy of the EAP-Initiate/Reauth message. */ + if (fils_pmkid_erp(sm->key_mgmt, wpabuf_head(erp_msg), + wpabuf_len(erp_msg), + sm->fils_erp_pmkid) == 0) + sm->fils_erp_pmkid_set = 1; + } + + wpa_hexdump_buf(MSG_DEBUG, "RSN: FILS fields for Authentication frame", + buf); + +fail: + wpabuf_free(erp_msg); + wpabuf_free(pub); + return buf; +} + + +int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data, + size_t len) +{ + const u8 *pos, *end; + struct ieee802_11_elems elems; + struct wpa_ie_data rsn; + int pmkid_match = 0; + u8 ick[FILS_ICK_MAX_LEN]; + size_t ick_len; + int res; + struct wpabuf *dh_ss = NULL; + const u8 *g_sta = NULL; + size_t g_sta_len = 0; + const u8 *g_ap = NULL; + size_t g_ap_len = 0; + struct wpabuf *pub = NULL; + + os_memcpy(sm->bssid, bssid, ETH_ALEN); + + wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields", + data, len); + pos = data; + end = data + len; + + /* TODO: FILS PK */ +#ifdef CONFIG_FILS_SK_PFS + if (sm->fils_dh_group) { + u16 group; + + /* Using FILS PFS */ + + /* Finite Cyclic Group */ + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, + "FILS: No room for Finite Cyclic Group"); + goto fail; + } + group = WPA_GET_LE16(pos); + pos += 2; + if (group != sm->fils_dh_group) { + wpa_printf(MSG_DEBUG, + "FILS: Unexpected change in Finite Cyclic Group: %u (expected %u)", + group, sm->fils_dh_group); + goto fail; + } + + /* Element */ + if ((size_t) (end - pos) < sm->fils_dh_elem_len) { + wpa_printf(MSG_DEBUG, "FILS: No room for Element"); + goto fail; + } + + if (!sm->fils_ecdh) { + wpa_printf(MSG_DEBUG, "FILS: No ECDH state available"); + goto fail; + } + dh_ss = crypto_ecdh_set_peerkey(sm->fils_ecdh, 1, pos, + sm->fils_dh_elem_len); + if (!dh_ss) { + wpa_printf(MSG_DEBUG, "FILS: ECDH operation failed"); + goto fail; + } + wpa_hexdump_buf_key(MSG_DEBUG, "FILS: DH_SS", dh_ss); + g_ap = pos; + g_ap_len = sm->fils_dh_elem_len; + pos += sm->fils_dh_elem_len; + } +#endif /* CONFIG_FILS_SK_PFS */ + + wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos); + if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) { + wpa_printf(MSG_DEBUG, "FILS: Could not parse elements"); + goto fail; + } + + /* RSNE */ + wpa_hexdump(MSG_DEBUG, "FILS: RSN element", elems.rsn_ie, + elems.rsn_ie_len); + if (!elems.rsn_ie || + wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, + &rsn) < 0) { + wpa_printf(MSG_DEBUG, "FILS: No RSN element"); + goto fail; + } + + if (!elems.fils_nonce) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field"); + goto fail; + } + os_memcpy(sm->fils_anonce, elems.fils_nonce, FILS_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: ANonce", sm->fils_anonce, FILS_NONCE_LEN); + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt)) { + struct wpa_ft_ies parse; + + if (!elems.mdie || !elems.ftie) { + wpa_printf(MSG_DEBUG, "FILS+FT: No MDE or FTE"); + goto fail; + } + + if (wpa_ft_parse_ies(pos, end - pos, &parse, + wpa_key_mgmt_sha384(sm->key_mgmt)) < 0) { + wpa_printf(MSG_DEBUG, "FILS+FT: Failed to parse IEs"); + goto fail; + } + + if (!parse.r0kh_id) { + wpa_printf(MSG_DEBUG, + "FILS+FT: No R0KH-ID subelem in FTE"); + goto fail; + } + os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len); + sm->r0kh_id_len = parse.r0kh_id_len; + wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID", + sm->r0kh_id, sm->r0kh_id_len); + + if (!parse.r1kh_id) { + wpa_printf(MSG_DEBUG, + "FILS+FT: No R1KH-ID subelem in FTE"); + goto fail; + } + os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN); + wpa_hexdump(MSG_DEBUG, "FILS+FT: R1KH-ID", + sm->r1kh_id, FT_R1KH_ID_LEN); + + /* TODO: Check MDE and FTE payload */ + + wpabuf_free(sm->fils_ft_ies); + sm->fils_ft_ies = wpabuf_alloc(2 + elems.mdie_len + + 2 + elems.ftie_len); + if (!sm->fils_ft_ies) + goto fail; + wpabuf_put_data(sm->fils_ft_ies, elems.mdie - 2, + 2 + elems.mdie_len); + wpabuf_put_data(sm->fils_ft_ies, elems.ftie - 2, + 2 + elems.ftie_len); + } else { + wpabuf_free(sm->fils_ft_ies); + sm->fils_ft_ies = NULL; + } +#endif /* CONFIG_IEEE80211R */ + + /* PMKID List */ + if (rsn.pmkid && rsn.num_pmkid > 0) { + wpa_hexdump(MSG_DEBUG, "FILS: PMKID List", + rsn.pmkid, rsn.num_pmkid * PMKID_LEN); + + if (rsn.num_pmkid != 1) { + wpa_printf(MSG_DEBUG, "FILS: Invalid PMKID selection"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "FILS: PMKID", rsn.pmkid, PMKID_LEN); + if (os_memcmp(sm->cur_pmksa->pmkid, rsn.pmkid, PMKID_LEN) != 0) + { + wpa_printf(MSG_DEBUG, "FILS: PMKID mismatch"); + wpa_hexdump(MSG_DEBUG, "FILS: Expected PMKID", + sm->cur_pmksa->pmkid, PMKID_LEN); + goto fail; + } + wpa_printf(MSG_DEBUG, + "FILS: Matching PMKID - continue using PMKSA caching"); + pmkid_match = 1; + } + if (!pmkid_match && sm->cur_pmksa) { + wpa_printf(MSG_DEBUG, + "FILS: No PMKID match - cannot use cached PMKSA entry"); + sm->cur_pmksa = NULL; + } + + /* FILS Session */ + if (!elems.fils_session) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Session element"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session, + FILS_SESSION_LEN); + if (os_memcmp(sm->fils_session, elems.fils_session, FILS_SESSION_LEN) + != 0) { + wpa_printf(MSG_DEBUG, "FILS: Session mismatch"); + wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session", + sm->fils_session, FILS_SESSION_LEN); + goto fail; + } + + /* FILS Wrapped Data */ + if (!sm->cur_pmksa && elems.fils_wrapped_data) { + u8 rmsk[ERP_MAX_KEY_LEN]; + size_t rmsk_len; + + wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data", + elems.fils_wrapped_data, + elems.fils_wrapped_data_len); + eapol_sm_process_erp_finish(sm->eapol, elems.fils_wrapped_data, + elems.fils_wrapped_data_len); + if (eapol_sm_failed(sm->eapol)) + goto fail; + + rmsk_len = ERP_MAX_KEY_LEN; + res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len); + if (res == PMK_LEN) { + rmsk_len = PMK_LEN; + res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len); + } + if (res) + goto fail; + + res = fils_rmsk_to_pmk(sm->key_mgmt, rmsk, rmsk_len, + sm->fils_nonce, sm->fils_anonce, + dh_ss ? wpabuf_head(dh_ss) : NULL, + dh_ss ? wpabuf_len(dh_ss) : 0, + sm->pmk, &sm->pmk_len); + os_memset(rmsk, 0, sizeof(rmsk)); + + /* Don't use DHss in PTK derivation if PMKSA caching is not + * used. */ + wpabuf_clear_free(dh_ss); + dh_ss = NULL; + + if (res) + goto fail; + + if (!sm->fils_erp_pmkid_set) { + wpa_printf(MSG_DEBUG, "FILS: PMKID not available"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "FILS: PMKID", sm->fils_erp_pmkid, + PMKID_LEN); + wpa_printf(MSG_DEBUG, "FILS: ERP processing succeeded - add PMKSA cache entry for the result"); + sm->cur_pmksa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, + sm->fils_erp_pmkid, NULL, 0, + sm->bssid, sm->own_addr, + sm->network_ctx, sm->key_mgmt, + NULL); + } + + if (!sm->cur_pmksa) { + wpa_printf(MSG_DEBUG, + "FILS: No remaining options to continue FILS authentication"); + goto fail; + } + + if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid, + sm->fils_nonce, sm->fils_anonce, + dh_ss ? wpabuf_head(dh_ss) : NULL, + dh_ss ? wpabuf_len(dh_ss) : 0, + &sm->ptk, ick, &ick_len, + sm->key_mgmt, sm->pairwise_cipher, + sm->fils_ft, &sm->fils_ft_len) < 0) { + wpa_printf(MSG_DEBUG, "FILS: Failed to derive PTK"); + goto fail; + } + + wpabuf_clear_free(dh_ss); + dh_ss = NULL; + + sm->ptk_set = 1; + sm->tptk_set = 0; + os_memset(&sm->tptk, 0, sizeof(sm->tptk)); + +#ifdef CONFIG_FILS_SK_PFS + if (sm->fils_dh_group) { + if (!sm->fils_ecdh) { + wpa_printf(MSG_INFO, "FILS: ECDH not initialized"); + goto fail; + } + pub = crypto_ecdh_get_pubkey(sm->fils_ecdh, 1); + if (!pub) + goto fail; + wpa_hexdump_buf(MSG_DEBUG, "FILS: gSTA", pub); + g_sta = wpabuf_head(pub); + g_sta_len = wpabuf_len(pub); + if (!g_ap) { + wpa_printf(MSG_INFO, "FILS: gAP not available"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "FILS: gAP", g_ap, g_ap_len); + } +#endif /* CONFIG_FILS_SK_PFS */ + + res = fils_key_auth_sk(ick, ick_len, sm->fils_nonce, + sm->fils_anonce, sm->own_addr, sm->bssid, + g_sta, g_sta_len, g_ap, g_ap_len, + sm->key_mgmt, sm->fils_key_auth_sta, + sm->fils_key_auth_ap, + &sm->fils_key_auth_len); + wpabuf_free(pub); + os_memset(ick, 0, sizeof(ick)); + return res; +fail: + wpabuf_free(pub); + wpabuf_clear_free(dh_ss); + return -1; +} + + +#ifdef CONFIG_IEEE80211R +static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf) +{ + struct rsn_ie_hdr *rsnie; + u16 capab; + u8 *pos; + int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt); + + /* RSNIE[PMKR0Name/PMKR1Name] */ + rsnie = wpabuf_put(buf, sizeof(*rsnie)); + rsnie->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(rsnie->version, RSN_VERSION); + + /* Group Suite Selector */ + if (!wpa_cipher_valid_group(sm->group_cipher)) { + wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)", + sm->group_cipher); + return -1; + } + pos = wpabuf_put(buf, RSN_SELECTOR_LEN); + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + sm->group_cipher)); + + /* Pairwise Suite Count */ + wpabuf_put_le16(buf, 1); + + /* Pairwise Suite List */ + if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { + wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)", + sm->pairwise_cipher); + return -1; + } + pos = wpabuf_put(buf, RSN_SELECTOR_LEN); + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + sm->pairwise_cipher)); + + /* Authenticated Key Management Suite Count */ + wpabuf_put_le16(buf, 1); + + /* Authenticated Key Management Suite List */ + pos = wpabuf_put(buf, RSN_SELECTOR_LEN); + if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256); + else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384); + else { + wpa_printf(MSG_WARNING, + "FILS+FT: Invalid key management type (%d)", + sm->key_mgmt); + return -1; + } + + /* RSN Capabilities */ + capab = 0; +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) + capab |= WPA_CAPABILITY_MFPC; +#endif /* CONFIG_IEEE80211W */ + wpabuf_put_le16(buf, capab); + + /* PMKID Count */ + wpabuf_put_le16(buf, 1); + + /* PMKID List [PMKR1Name] */ + wpa_hexdump_key(MSG_DEBUG, "FILS+FT: XXKey (FILS-FT)", + sm->fils_ft, sm->fils_ft_len); + wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: SSID", sm->ssid, sm->ssid_len); + wpa_hexdump(MSG_DEBUG, "FILS+FT: MDID", + sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN); + wpa_hexdump_ascii(MSG_DEBUG, "FILS+FT: R0KH-ID", + sm->r0kh_id, sm->r0kh_id_len); + if (wpa_derive_pmk_r0(sm->fils_ft, sm->fils_ft_len, sm->ssid, + sm->ssid_len, sm->mobility_domain, + sm->r0kh_id, sm->r0kh_id_len, sm->own_addr, + sm->pmk_r0, sm->pmk_r0_name, use_sha384) < 0) { + wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMK-R0"); + return -1; + } + sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN; + wpa_hexdump_key(MSG_DEBUG, "FILS+FT: PMK-R0", + sm->pmk_r0, sm->pmk_r0_len); + wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR0Name", + sm->pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_printf(MSG_DEBUG, "FILS+FT: R1KH-ID: " MACSTR, + MAC2STR(sm->r1kh_id)); + pos = wpabuf_put(buf, WPA_PMK_NAME_LEN); + if (wpa_derive_pmk_r1_name(sm->pmk_r0_name, sm->r1kh_id, sm->own_addr, + pos, use_sha384) < 0) { + wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", pos, WPA_PMK_NAME_LEN); + +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { + /* Management Group Cipher Suite */ + pos = wpabuf_put(buf, RSN_SELECTOR_LEN); + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + } +#endif /* CONFIG_IEEE80211W */ + + rsnie->len = ((u8 *) wpabuf_put(buf, 0) - (u8 *) rsnie) - 2; + return 0; +} +#endif /* CONFIG_IEEE80211R */ + + +struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek, + size_t *kek_len, const u8 **snonce, + const u8 **anonce, + const struct wpabuf **hlp, + unsigned int num_hlp) +{ + struct wpabuf *buf; + size_t len; + unsigned int i; + + len = 1000; +#ifdef CONFIG_IEEE80211R + if (sm->fils_ft_ies) + len += wpabuf_len(sm->fils_ft_ies); + if (wpa_key_mgmt_ft(sm->key_mgmt)) + len += 256; +#endif /* CONFIG_IEEE80211R */ + for (i = 0; hlp && i < num_hlp; i++) + len += 10 + wpabuf_len(hlp[i]); + buf = wpabuf_alloc(len); + if (!buf) + return NULL; + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->fils_ft_ies) { + /* MDE and FTE when using FILS+FT */ + wpabuf_put_buf(buf, sm->fils_ft_ies); + /* RSNE with PMKR1Name in PMKID field */ + if (fils_ft_build_assoc_req_rsne(sm, buf) < 0) { + wpabuf_free(buf); + return NULL; + } + } +#endif /* CONFIG_IEEE80211R */ + + /* FILS Session */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION); + wpabuf_put_data(buf, sm->fils_session, FILS_SESSION_LEN); + + /* Everything after FILS Session element gets encrypted in the driver + * with KEK. The buffer returned from here is the plaintext version. */ + + /* TODO: FILS Public Key */ + + /* FILS Key Confirm */ + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ + wpabuf_put_u8(buf, 1 + sm->fils_key_auth_len); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_KEY_CONFIRM); + wpabuf_put_data(buf, sm->fils_key_auth_sta, sm->fils_key_auth_len); + + /* FILS HLP Container */ + for (i = 0; hlp && i < num_hlp; i++) { + const u8 *pos = wpabuf_head(hlp[i]); + size_t left = wpabuf_len(hlp[i]); + + wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */ + if (left <= 254) + len = 1 + left; + else + len = 255; + wpabuf_put_u8(buf, len); /* Length */ + /* Element ID Extension */ + wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_HLP_CONTAINER); + /* Destination MAC Address, Source MAC Address, HLP Packet. + * HLP Packet is in MSDU format (i.e., included the LLC/SNAP + * header when LPD is used). */ + wpabuf_put_data(buf, pos, len - 1); + pos += len - 1; + left -= len - 1; + while (left) { + wpabuf_put_u8(buf, WLAN_EID_FRAGMENT); + len = left > 255 ? 255 : left; + wpabuf_put_u8(buf, len); + wpabuf_put_data(buf, pos, len); + pos += len; + left -= len; + } + } + + /* TODO: FILS IP Address Assignment */ + + wpa_hexdump_buf(MSG_DEBUG, "FILS: Association Request plaintext", buf); + + *kek = sm->ptk.kek; + *kek_len = sm->ptk.kek_len; + wpa_hexdump_key(MSG_DEBUG, "FILS: KEK for AEAD", *kek, *kek_len); + *snonce = sm->fils_nonce; + wpa_hexdump(MSG_DEBUG, "FILS: SNonce for AEAD AAD", + *snonce, FILS_NONCE_LEN); + *anonce = sm->fils_anonce; + wpa_hexdump(MSG_DEBUG, "FILS: ANonce for AEAD AAD", + *anonce, FILS_NONCE_LEN); + + return buf; +} + + +static void fils_process_hlp_resp(struct wpa_sm *sm, const u8 *resp, size_t len) +{ + const u8 *pos, *end; + + wpa_hexdump(MSG_MSGDUMP, "FILS: HLP response", resp, len); + if (len < 2 * ETH_ALEN) + return; + pos = resp + 2 * ETH_ALEN; + end = resp + len; + if (end - pos >= 6 && + os_memcmp(pos, "\xaa\xaa\x03\x00\x00\x00", 6) == 0) + pos += 6; /* Remove SNAP/LLC header */ + wpa_sm_fils_hlp_rx(sm, resp, resp + ETH_ALEN, pos, end - pos); +} + + +static void fils_process_hlp_container(struct wpa_sm *sm, const u8 *pos, + size_t len) +{ + const u8 *end = pos + len; + u8 *tmp, *tmp_pos; + + /* Check if there are any FILS HLP Container elements */ + while (end - pos >= 2) { + if (2 + pos[1] > end - pos) + return; + if (pos[0] == WLAN_EID_EXTENSION && + pos[1] >= 1 + 2 * ETH_ALEN && + pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER) + break; + pos += 2 + pos[1]; + } + if (end - pos < 2) + return; /* No FILS HLP Container elements */ + + tmp = os_malloc(end - pos); + if (!tmp) + return; + + while (end - pos >= 2) { + if (2 + pos[1] > end - pos || + pos[0] != WLAN_EID_EXTENSION || + pos[1] < 1 + 2 * ETH_ALEN || + pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER) + break; + tmp_pos = tmp; + os_memcpy(tmp_pos, pos + 3, pos[1] - 1); + tmp_pos += pos[1] - 1; + pos += 2 + pos[1]; + + /* Add possible fragments */ + while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT && + 2 + pos[1] <= end - pos) { + os_memcpy(tmp_pos, pos + 2, pos[1]); + tmp_pos += pos[1]; + pos += 2 + pos[1]; + } + + fils_process_hlp_resp(sm, tmp, tmp_pos - tmp); + } + + os_free(tmp); +} + + +int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + const u8 *end, *ie_start; + struct ieee802_11_elems elems; + int keylen, rsclen; + enum wpa_alg alg; + struct wpa_gtk_data gd; + int maxkeylen; + struct wpa_eapol_ie_parse kde; + + if (!sm || !sm->ptk_set) { + wpa_printf(MSG_DEBUG, "FILS: No KEK available"); + return -1; + } + + if (!wpa_key_mgmt_fils(sm->key_mgmt)) { + wpa_printf(MSG_DEBUG, "FILS: Not a FILS AKM"); + return -1; + } + + if (sm->fils_completed) { + wpa_printf(MSG_DEBUG, + "FILS: Association has already been completed for this FILS authentication - ignore unexpected retransmission"); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "FILS: (Re)Association Response frame", + resp, len); + + mgmt = (const struct ieee80211_mgmt *) resp; + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_resp)) + return -1; + + end = resp + len; + /* Same offset for Association Response and Reassociation Response */ + ie_start = mgmt->u.assoc_resp.variable; + + if (ieee802_11_parse_elems(ie_start, end - ie_start, &elems, 1) == + ParseFailed) { + wpa_printf(MSG_DEBUG, + "FILS: Failed to parse decrypted elements"); + goto fail; + } + + if (!elems.fils_session) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Session element"); + return -1; + } + if (os_memcmp(elems.fils_session, sm->fils_session, + FILS_SESSION_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FILS: FILS Session mismatch"); + wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session", + elems.fils_session, FILS_SESSION_LEN); + wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session", + sm->fils_session, FILS_SESSION_LEN); + } + + /* TODO: FILS Public Key */ + + if (!elems.fils_key_confirm) { + wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element"); + goto fail; + } + if (elems.fils_key_confirm_len != sm->fils_key_auth_len) { + wpa_printf(MSG_DEBUG, + "FILS: Unexpected Key-Auth length %d (expected %d)", + elems.fils_key_confirm_len, + (int) sm->fils_key_auth_len); + goto fail; + } + if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_ap, + sm->fils_key_auth_len) != 0) { + wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch"); + wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth", + elems.fils_key_confirm, + elems.fils_key_confirm_len); + wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth", + sm->fils_key_auth_ap, sm->fils_key_auth_len); + goto fail; + } + + /* Key Delivery */ + if (!elems.key_delivery) { + wpa_printf(MSG_DEBUG, "FILS: No Key Delivery element"); + goto fail; + } + + /* Parse GTK and set the key to the driver */ + os_memset(&gd, 0, sizeof(gd)); + if (wpa_supplicant_parse_ies(elems.key_delivery + WPA_KEY_RSC_LEN, + elems.key_delivery_len - WPA_KEY_RSC_LEN, + &kde) < 0) { + wpa_printf(MSG_DEBUG, "FILS: Failed to parse KDEs"); + goto fail; + } + if (!kde.gtk) { + wpa_printf(MSG_DEBUG, "FILS: No GTK KDE"); + goto fail; + } + maxkeylen = gd.gtk_len = kde.gtk_len - 2; + if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, + gd.gtk_len, maxkeylen, + &gd.key_rsc_len, &gd.alg)) + goto fail; + + wpa_hexdump_key(MSG_DEBUG, "FILS: Received GTK", kde.gtk, kde.gtk_len); + gd.keyidx = kde.gtk[0] & 0x3; + gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm, + !!(kde.gtk[0] & BIT(2))); + if (kde.gtk_len - 2 > sizeof(gd.gtk)) { + wpa_printf(MSG_DEBUG, "FILS: Too long GTK in GTK KDE (len=%lu)", + (unsigned long) kde.gtk_len - 2); + goto fail; + } + os_memcpy(gd.gtk, kde.gtk + 2, kde.gtk_len - 2); + + wpa_printf(MSG_DEBUG, "FILS: Set GTK to driver"); + if (wpa_supplicant_install_gtk(sm, &gd, elems.key_delivery, 0) < 0) { + wpa_printf(MSG_DEBUG, "FILS: Failed to set GTK"); + goto fail; + } + + if (ieee80211w_set_keys(sm, &kde) < 0) { + wpa_printf(MSG_DEBUG, "FILS: Failed to set IGTK"); + goto fail; + } + + alg = wpa_cipher_to_alg(sm->pairwise_cipher); + keylen = wpa_cipher_key_len(sm->pairwise_cipher); + if (keylen <= 0 || (unsigned int) keylen != sm->ptk.tk_len) { + wpa_printf(MSG_DEBUG, "FILS: TK length mismatch: %u != %lu", + keylen, (long unsigned int) sm->ptk.tk_len); + goto fail; + } + rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher); + wpa_hexdump_key(MSG_DEBUG, "FILS: Set TK to driver", + sm->ptk.tk, keylen); + if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, null_rsc, rsclen, + sm->ptk.tk, keylen) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "FILS: Failed to set PTK to the driver (alg=%d keylen=%d bssid=" + MACSTR ")", + alg, keylen, MAC2STR(sm->bssid)); + goto fail; + } + + /* TODO: TK could be cleared after auth frame exchange now that driver + * takes care of association frame encryption/decryption. */ + /* TK is not needed anymore in supplicant */ + os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN); + sm->ptk.tk_len = 0; + sm->ptk.installed = 1; + + /* FILS HLP Container */ + fils_process_hlp_container(sm, ie_start, end - ie_start); + + /* TODO: FILS IP Address Assignment */ + + wpa_printf(MSG_DEBUG, "FILS: Auth+Assoc completed successfully"); + sm->fils_completed = 1; + + return 0; +fail: + return -1; +} + + +void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set) +{ + if (sm) + sm->fils_completed = !!set; +} + +#endif /* CONFIG_FILS */ + + +int wpa_fils_is_completed(struct wpa_sm *sm) +{ +#ifdef CONFIG_FILS + return sm && sm->fils_completed; +#else /* CONFIG_FILS */ + return 0; +#endif /* CONFIG_FILS */ +} + + +#ifdef CONFIG_OWE + +struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm, u16 group) +{ + struct wpabuf *ie = NULL, *pub = NULL; + size_t prime_len; + + if (group == 19) + prime_len = 32; + else if (group == 20) + prime_len = 48; + else if (group == 21) + prime_len = 66; + else + return NULL; + + crypto_ecdh_deinit(sm->owe_ecdh); + sm->owe_ecdh = crypto_ecdh_init(group); + if (!sm->owe_ecdh) + goto fail; + sm->owe_group = group; + pub = crypto_ecdh_get_pubkey(sm->owe_ecdh, 0); + pub = wpabuf_zeropad(pub, prime_len); + if (!pub) + goto fail; + + ie = wpabuf_alloc(5 + wpabuf_len(pub)); + if (!ie) + goto fail; + wpabuf_put_u8(ie, WLAN_EID_EXTENSION); + wpabuf_put_u8(ie, 1 + 2 + wpabuf_len(pub)); + wpabuf_put_u8(ie, WLAN_EID_EXT_OWE_DH_PARAM); + wpabuf_put_le16(ie, group); + wpabuf_put_buf(ie, pub); + wpabuf_free(pub); + wpa_hexdump_buf(MSG_DEBUG, "OWE: Diffie-Hellman Parameter element", + ie); + + return ie; +fail: + wpabuf_free(pub); + crypto_ecdh_deinit(sm->owe_ecdh); + sm->owe_ecdh = NULL; + return NULL; +} + + +int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid, + const u8 *resp_ies, size_t resp_ies_len) +{ + struct ieee802_11_elems elems; + u16 group; + struct wpabuf *secret, *pub, *hkey; + int res; + u8 prk[SHA512_MAC_LEN], pmkid[SHA512_MAC_LEN]; + const char *info = "OWE Key Generation"; + const u8 *addr[2]; + size_t len[2]; + size_t hash_len, prime_len; + struct wpa_ie_data data; + + if (!resp_ies || + ieee802_11_parse_elems(resp_ies, resp_ies_len, &elems, 1) == + ParseFailed) { + wpa_printf(MSG_INFO, + "OWE: Could not parse Association Response frame elements"); + return -1; + } + + if (sm->cur_pmksa && elems.rsn_ie && + wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, 2 + elems.rsn_ie_len, + &data) == 0 && + data.num_pmkid == 1 && data.pmkid && + os_memcmp(sm->cur_pmksa->pmkid, data.pmkid, PMKID_LEN) == 0) { + wpa_printf(MSG_DEBUG, "OWE: Use PMKSA caching"); + wpa_sm_set_pmk_from_pmksa(sm); + return 0; + } + + if (!elems.owe_dh) { + wpa_printf(MSG_INFO, + "OWE: No Diffie-Hellman Parameter element found in Association Response frame"); + return -1; + } + + group = WPA_GET_LE16(elems.owe_dh); + if (group != sm->owe_group) { + wpa_printf(MSG_INFO, + "OWE: Unexpected Diffie-Hellman group in response: %u", + group); + return -1; + } + + if (!sm->owe_ecdh) { + wpa_printf(MSG_INFO, "OWE: No ECDH state available"); + return -1; + } + + if (group == 19) + prime_len = 32; + else if (group == 20) + prime_len = 48; + else if (group == 21) + prime_len = 66; + else + return -1; + + secret = crypto_ecdh_set_peerkey(sm->owe_ecdh, 0, + elems.owe_dh + 2, + elems.owe_dh_len - 2); + secret = wpabuf_zeropad(secret, prime_len); + if (!secret) { + wpa_printf(MSG_DEBUG, "OWE: Invalid peer DH public key"); + return -1; + } + wpa_hexdump_buf_key(MSG_DEBUG, "OWE: DH shared secret", secret); + + /* prk = HKDF-extract(C | A | group, z) */ + + pub = crypto_ecdh_get_pubkey(sm->owe_ecdh, 0); + if (!pub) { + wpabuf_clear_free(secret); + return -1; + } + + /* PMKID = Truncate-128(Hash(C | A)) */ + addr[0] = wpabuf_head(pub); + len[0] = wpabuf_len(pub); + addr[1] = elems.owe_dh + 2; + len[1] = elems.owe_dh_len - 2; + if (group == 19) { + res = sha256_vector(2, addr, len, pmkid); + hash_len = SHA256_MAC_LEN; + } else if (group == 20) { + res = sha384_vector(2, addr, len, pmkid); + hash_len = SHA384_MAC_LEN; + } else if (group == 21) { + res = sha512_vector(2, addr, len, pmkid); + hash_len = SHA512_MAC_LEN; + } else { + res = -1; + hash_len = 0; + } + pub = wpabuf_zeropad(pub, prime_len); + if (res < 0 || !pub) { + wpabuf_free(pub); + wpabuf_clear_free(secret); + return -1; + } + + hkey = wpabuf_alloc(wpabuf_len(pub) + elems.owe_dh_len - 2 + 2); + if (!hkey) { + wpabuf_free(pub); + wpabuf_clear_free(secret); + return -1; + } + + wpabuf_put_buf(hkey, pub); /* C */ + wpabuf_free(pub); + wpabuf_put_data(hkey, elems.owe_dh + 2, elems.owe_dh_len - 2); /* A */ + wpabuf_put_le16(hkey, sm->owe_group); /* group */ + if (group == 19) + res = hmac_sha256(wpabuf_head(hkey), wpabuf_len(hkey), + wpabuf_head(secret), wpabuf_len(secret), prk); + else if (group == 20) + res = hmac_sha384(wpabuf_head(hkey), wpabuf_len(hkey), + wpabuf_head(secret), wpabuf_len(secret), prk); + else if (group == 21) + res = hmac_sha512(wpabuf_head(hkey), wpabuf_len(hkey), + wpabuf_head(secret), wpabuf_len(secret), prk); + wpabuf_clear_free(hkey); + wpabuf_clear_free(secret); + if (res < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "OWE: prk", prk, hash_len); + + /* PMK = HKDF-expand(prk, "OWE Key Generation", n) */ + + if (group == 19) + res = hmac_sha256_kdf(prk, hash_len, NULL, (const u8 *) info, + os_strlen(info), sm->pmk, hash_len); + else if (group == 20) + res = hmac_sha384_kdf(prk, hash_len, NULL, (const u8 *) info, + os_strlen(info), sm->pmk, hash_len); + else if (group == 21) + res = hmac_sha512_kdf(prk, hash_len, NULL, (const u8 *) info, + os_strlen(info), sm->pmk, hash_len); + os_memset(prk, 0, SHA512_MAC_LEN); + if (res < 0) { + sm->pmk_len = 0; + return -1; + } + sm->pmk_len = hash_len; + + wpa_hexdump_key(MSG_DEBUG, "OWE: PMK", sm->pmk, sm->pmk_len); + wpa_hexdump(MSG_DEBUG, "OWE: PMKID", pmkid, PMKID_LEN); + pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, pmkid, NULL, 0, + bssid, sm->own_addr, sm->network_ctx, sm->key_mgmt, + NULL); + + return 0; +} + +#endif /* CONFIG_OWE */ + + +void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id) +{ +#ifdef CONFIG_FILS + if (sm && fils_cache_id) { + sm->fils_cache_id_set = 1; + os_memcpy(sm->fils_cache_id, fils_cache_id, FILS_CACHE_ID_LEN); + } +#endif /* CONFIG_FILS */ +} diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h index 0b7477f31bc7a..21f4b17815e93 100644 --- a/src/rsn_supp/wpa.h +++ b/src/rsn_supp/wpa.h @@ -25,7 +25,7 @@ struct wpa_sm_ctx { void (*set_state)(void *ctx, enum wpa_states state); enum wpa_states (*get_state)(void *ctx); - void (*deauthenticate)(void * ctx, int reason_code); + void (*deauthenticate)(void * ctx, int reason_code); int (*set_key)(void *ctx, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, @@ -38,8 +38,11 @@ struct wpa_sm_ctx { void (*cancel_auth_timeout)(void *ctx); u8 * (*alloc_eapol)(void *ctx, u8 type, const void *data, u16 data_len, size_t *msg_len, void **data_pos); - int (*add_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid); - int (*remove_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid); + int (*add_pmkid)(void *ctx, void *network_ctx, const u8 *bssid, + const u8 *pmkid, const u8 *fils_cache_id, + const u8 *pmk, size_t pmk_len); + int (*remove_pmkid)(void *ctx, void *network_ctx, const u8 *bssid, + const u8 *pmkid, const u8 *fils_cache_id); void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob); const struct wpa_config_blob * (*get_config_blob)(void *ctx, const char *name); @@ -77,6 +80,8 @@ struct wpa_sm_ctx { const u8 *kck, size_t kck_len, const u8 *replay_ctr); int (*key_mgmt_set_pmk)(void *ctx, const u8 *pmk, size_t pmk_len); + void (*fils_hlp_rx)(void *ctx, const u8 *dst, const u8 *src, + const u8 *pkt, size_t pkt_len); }; @@ -95,7 +100,6 @@ enum wpa_sm_conf_params { struct rsn_supp_config { void *network_ctx; - int peerkey_enabled; int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */ int proactive_key_caching; int eap_workaround; @@ -105,6 +109,7 @@ struct rsn_supp_config { int wpa_ptk_rekey; int p2p; int wpa_rsc_relaxation; + const u8 *fils_cache_id; }; #ifndef CONFIG_NO_WPA @@ -147,6 +152,15 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len); int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data); int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len); +struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_head(struct wpa_sm *sm); +struct rsn_pmksa_cache_entry * +wpa_sm_pmksa_cache_add_entry(struct wpa_sm *sm, + struct rsn_pmksa_cache_entry * entry); +void wpa_sm_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, + const u8 *pmkid, const u8 *bssid, + const u8 *fils_cache_id); +int wpa_sm_pmksa_exists(struct wpa_sm *sm, const u8 *bssid, + const void *network_ctx); void wpa_sm_drop_sa(struct wpa_sm *sm); int wpa_sm_has_ptk(struct wpa_sm *sm); @@ -160,6 +174,7 @@ void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter); void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck, size_t ptk_kck_len, const u8 *ptk_kek, size_t ptk_kek_len); +int wpa_fils_is_completed(struct wpa_sm *sm); #else /* CONFIG_NO_WPA */ @@ -327,29 +342,20 @@ static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck, { } -#endif /* CONFIG_NO_WPA */ - -#ifdef CONFIG_PEERKEY -int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer); -int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, - const u8 *buf, size_t len); -#else /* CONFIG_PEERKEY */ -static inline int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) -{ - return -1; -} - -static inline int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, - const u8 *buf, size_t len) +static inline int wpa_fils_is_completed(struct wpa_sm *sm) { return 0; } -#endif /* CONFIG_PEERKEY */ + +#endif /* CONFIG_NO_WPA */ #ifdef CONFIG_IEEE80211R int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len); int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie); +int wpa_ft_add_mdie(struct wpa_sm *sm, u8 *ies, size_t ies_len, + const u8 *mdie); +const u8 * wpa_sm_get_ft_md(struct wpa_sm *sm); int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, int ft_action, const u8 *target_ap, const u8 *ric_ies, size_t ric_ies_len); @@ -374,6 +380,12 @@ static inline int wpa_ft_prepare_auth_request(struct wpa_sm *sm, return 0; } +static inline int wpa_ft_add_mdie(struct wpa_sm *sm, u8 *ies, size_t ies_len, + const u8 *mdie) +{ + return 0; +} + static inline int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, int ft_action, const u8 *target_ap) @@ -425,5 +437,24 @@ extern unsigned int tdls_testing; int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf); void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf); +const u8 * wpa_sm_get_anonce(struct wpa_sm *sm); +unsigned int wpa_sm_get_key_mgmt(struct wpa_sm *sm); + +struct wpabuf * fils_build_auth(struct wpa_sm *sm, int dh_group, const u8 *md); +int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data, + size_t len); +struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek, + size_t *kek_len, const u8 **snonce, + const u8 **anonce, + const struct wpabuf **hlp, + unsigned int num_hlp); +int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len); + +struct wpabuf * owe_build_assoc_req(struct wpa_sm *sm, u16 group); +int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid, + const u8 *resp_ies, size_t resp_ies_len); + +void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set); +void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id); #endif /* WPA_H */ diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c index d45bb4585e50a..b8d60e3208d09 100644 --- a/src/rsn_supp/wpa_ft.c +++ b/src/rsn_supp/wpa_ft.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - IEEE 802.11r - Fast BSS Transition - * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2018, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,7 @@ #include "common.h" #include "crypto/aes_wrap.h" +#include "crypto/sha384.h" #include "crypto/random.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" @@ -23,6 +24,7 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, { u8 ptk_name[WPA_PMK_NAME_LEN]; const u8 *anonce = key->key_nonce; + int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt); if (sm->xxkey_len == 0) { wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " @@ -30,21 +32,26 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, return -1; } - wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid, - sm->ssid_len, sm->mobility_domain, - sm->r0kh_id, sm->r0kh_id_len, sm->own_addr, - sm->pmk_r0, sm->pmk_r0_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, PMK_LEN); + sm->pmk_r0_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN; + if (wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid, + sm->ssid_len, sm->mobility_domain, + sm->r0kh_id, sm->r0kh_id_len, sm->own_addr, + sm->pmk_r0, sm->pmk_r0_name, use_sha384) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, sm->pmk_r0_len); wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", sm->pmk_r0_name, WPA_PMK_NAME_LEN); - wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id, - sm->own_addr, sm->pmk_r1, sm->pmk_r1_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN); + sm->pmk_r1_len = sm->pmk_r0_len; + if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name, + sm->r1kh_id, sm->own_addr, sm->pmk_r1, + sm->pmk_r1_name) < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, sm->pmk_r1_len); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); - return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr, - sm->bssid, sm->pmk_r1_name, ptk, ptk_name, - sm->key_mgmt, sm->pairwise_cipher); + return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce, anonce, + sm->own_addr, sm->bssid, sm->pmk_r1_name, ptk, + ptk_name, sm->key_mgmt, sm->pairwise_cipher); } @@ -58,11 +65,13 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) { struct wpa_ft_ies ft; + int use_sha384; if (sm == NULL) return 0; - if (wpa_ft_parse_ies(ies, ies_len, &ft) < 0) + use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt); + if (wpa_ft_parse_ies(ies, ies_len, &ft, use_sha384) < 0) return -1; if (ft.mdie && ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) @@ -146,16 +155,17 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, const u8 *ap_mdie) { size_t buf_len; - u8 *buf, *pos, *ftie_len, *ftie_pos; + u8 *buf, *pos, *ftie_len, *ftie_pos, *fte_mic, *elem_count; struct rsn_mdie *mdie; - struct rsn_ftie *ftie; struct rsn_ie_hdr *rsnie; u16 capab; + int mdie_len; sm->ft_completed = 0; sm->ft_reassoc_completed = 0; - buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + + buf_len = 2 + sizeof(struct rsn_mdie) + 2 + + sizeof(struct rsn_ftie_sha384) + 2 + sm->r0kh_id_len + ric_ies_len + 100; buf = os_zalloc(buf_len); if (buf == NULL) @@ -201,10 +211,20 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, /* Authenticated Key Management Suite List */ if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); +#ifdef CONFIG_SHA384 + else if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X_SHA384) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384); +#endif /* CONFIG_SHA384 */ else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK) RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); else if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE) RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); +#ifdef CONFIG_FILS + else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256); + else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384); +#endif /* CONFIG_FILS */ else { wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)", sm->key_mgmt); @@ -216,7 +236,10 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, /* RSN Capabilities */ capab = 0; #ifdef CONFIG_IEEE80211W - if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) + if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC || + sm->mgmt_group_cipher == WPA_CIPHER_BIP_GMAC_128 || + sm->mgmt_group_cipher == WPA_CIPHER_BIP_GMAC_256 || + sm->mgmt_group_cipher == WPA_CIPHER_BIP_CMAC_256) capab |= WPA_CAPABILITY_MFPC; #endif /* CONFIG_IEEE80211W */ WPA_PUT_LE16(pos, capab); @@ -231,34 +254,63 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, pos += WPA_PMK_NAME_LEN; #ifdef CONFIG_IEEE80211W - if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { - /* Management Group Cipher Suite */ + /* Management Group Cipher Suite */ + switch (sm->mgmt_group_cipher) { + case WPA_CIPHER_AES_128_CMAC: RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); pos += RSN_SELECTOR_LEN; + break; + case WPA_CIPHER_BIP_GMAC_128: + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_128); + pos += RSN_SELECTOR_LEN; + break; + case WPA_CIPHER_BIP_GMAC_256: + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_256); + pos += RSN_SELECTOR_LEN; + break; + case WPA_CIPHER_BIP_CMAC_256: + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_CMAC_256); + pos += RSN_SELECTOR_LEN; + break; } #endif /* CONFIG_IEEE80211W */ rsnie->len = (pos - (u8 *) rsnie) - 2; /* MDIE */ - *pos++ = WLAN_EID_MOBILITY_DOMAIN; - *pos++ = sizeof(*mdie); - mdie = (struct rsn_mdie *) pos; - pos += sizeof(*mdie); - os_memcpy(mdie->mobility_domain, sm->mobility_domain, - MOBILITY_DOMAIN_ID_LEN); - mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] : - sm->mdie_ft_capab; + mdie_len = wpa_ft_add_mdie(sm, pos, buf_len - (pos - buf), ap_mdie); + if (mdie_len <= 0) { + os_free(buf); + return NULL; + } + mdie = (struct rsn_mdie *) (pos + 2); + pos += mdie_len; /* FTIE[SNonce, [R1KH-ID,] R0KH-ID ] */ ftie_pos = pos; *pos++ = WLAN_EID_FAST_BSS_TRANSITION; ftie_len = pos++; - ftie = (struct rsn_ftie *) pos; - pos += sizeof(*ftie); - os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN); - if (anonce) - os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN); + if (wpa_key_mgmt_sha384(sm->key_mgmt)) { + struct rsn_ftie_sha384 *ftie; + + ftie = (struct rsn_ftie_sha384 *) pos; + fte_mic = ftie->mic; + elem_count = &ftie->mic_control[1]; + pos += sizeof(*ftie); + os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN); + if (anonce) + os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN); + } else { + struct rsn_ftie *ftie; + + ftie = (struct rsn_ftie *) pos; + fte_mic = ftie->mic; + elem_count = &ftie->mic_control[1]; + pos += sizeof(*ftie); + os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN); + if (anonce) + os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN); + } if (kck) { /* R1KH-ID sub-element in third FT message */ *pos++ = FTIE_SUBELEM_R1KH_ID; @@ -292,13 +344,12 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, * RIC-Request (if present) */ /* Information element count */ - ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies, - ric_ies_len); + *elem_count = 3 + ieee802_11_ie_count(ric_ies, ric_ies_len); if (wpa_ft_mic(kck, kck_len, sm->own_addr, target_ap, 5, ((u8 *) mdie) - 2, 2 + sizeof(*mdie), ftie_pos, 2 + *ftie_len, (u8 *) rsnie, 2 + rsnie->len, ric_ies, - ric_ies_len, ftie->mic) < 0) { + ric_ies_len, fte_mic) < 0) { wpa_printf(MSG_INFO, "FT: Failed to calculate MIC"); os_free(buf); return NULL; @@ -367,6 +418,37 @@ int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie) } +int wpa_ft_add_mdie(struct wpa_sm *sm, u8 *buf, size_t buf_len, + const u8 *ap_mdie) +{ + u8 *pos = buf; + struct rsn_mdie *mdie; + + if (buf_len < 2 + sizeof(*mdie)) { + wpa_printf(MSG_INFO, + "FT: Failed to add MDIE: short buffer, length=%zu", + buf_len); + return 0; + } + + *pos++ = WLAN_EID_MOBILITY_DOMAIN; + *pos++ = sizeof(*mdie); + mdie = (struct rsn_mdie *) pos; + os_memcpy(mdie->mobility_domain, sm->mobility_domain, + MOBILITY_DOMAIN_ID_LEN); + mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] : + sm->mdie_ft_capab; + + return 2 + sizeof(*mdie); +} + + +const u8 * wpa_sm_get_ft_md(struct wpa_sm *sm) +{ + return sm->mobility_domain; +} + + int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, int ft_action, const u8 *target_ap, const u8 *ric_ies, size_t ric_ies_len) @@ -375,10 +457,13 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, size_t ft_ies_len; struct wpa_ft_ies parse; struct rsn_mdie *mdie; - struct rsn_ftie *ftie; u8 ptk_name[WPA_PMK_NAME_LEN]; int ret; const u8 *bssid; + const u8 *kck; + size_t kck_len; + int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt); + const u8 *anonce, *snonce; wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len); @@ -404,7 +489,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, return -1; } - if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); return -1; } @@ -417,16 +502,34 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, return -1; } - ftie = (struct rsn_ftie *) parse.ftie; - if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { - wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); - return -1; + if (use_sha384) { + struct rsn_ftie_sha384 *ftie; + + ftie = (struct rsn_ftie_sha384 *) parse.ftie; + if (!ftie || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return -1; + } + + anonce = ftie->anonce; + snonce = ftie->snonce; + } else { + struct rsn_ftie *ftie; + + ftie = (struct rsn_ftie *) parse.ftie; + if (!ftie || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return -1; + } + + anonce = ftie->anonce; + snonce = ftie->snonce; } - if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) { + if (os_memcmp(snonce, sm->snonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", - ftie->snonce, WPA_NONCE_LEN); + snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", sm->snonce, WPA_NONCE_LEN); return -1; @@ -465,23 +568,34 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN); - wpa_hexdump(MSG_DEBUG, "FT: ANonce", ftie->anonce, WPA_NONCE_LEN); - os_memcpy(sm->anonce, ftie->anonce, WPA_NONCE_LEN); - wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id, - sm->own_addr, sm->pmk_r1, sm->pmk_r1_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: ANonce", anonce, WPA_NONCE_LEN); + os_memcpy(sm->anonce, anonce, WPA_NONCE_LEN); + if (wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_len, sm->pmk_r0_name, + sm->r1kh_id, sm->own_addr, sm->pmk_r1, + sm->pmk_r1_name) < 0) + return -1; + sm->pmk_r1_len = sm->pmk_r0_len; + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, sm->pmk_r1_len); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); bssid = target_ap; - if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, - sm->own_addr, bssid, sm->pmk_r1_name, &sm->ptk, - ptk_name, sm->key_mgmt, sm->pairwise_cipher) < 0) + if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len, sm->snonce, + anonce, sm->own_addr, bssid, + sm->pmk_r1_name, &sm->ptk, ptk_name, sm->key_mgmt, + sm->pairwise_cipher) < 0) return -1; - ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce, + if (wpa_key_mgmt_fils(sm->key_mgmt)) { + kck = sm->ptk.kck2; + kck_len = sm->ptk.kck2_len; + } else { + kck = sm->ptk.kck; + kck_len = sm->ptk.kck_len; + } + ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, anonce, sm->pmk_r1_name, - sm->ptk.kck, sm->ptk.kck_len, bssid, + kck, kck_len, bssid, ric_ies, ric_ies_len, parse.mdie ? parse.mdie - 2 : NULL); if (ft_ies) { @@ -544,6 +658,16 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, int keyidx; enum wpa_alg alg; size_t gtk_len, keylen, rsc_len; + const u8 *kek; + size_t kek_len; + + if (wpa_key_mgmt_fils(sm->key_mgmt)) { + kek = sm->ptk.kek2; + kek_len = sm->ptk.kek2_len; + } else { + kek = sm->ptk.kek; + kek_len = sm->ptk.kek_len; + } if (gtk_elem == NULL) { wpa_printf(MSG_DEBUG, "FT: No GTK included in FTIE"); @@ -560,8 +684,7 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, return -1; } gtk_len = gtk_elem_len - 19; - if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, gtk_len / 8, gtk_elem + 11, - gtk)) { + if (aes_unwrap(kek, kek_len, gtk_len / 8, gtk_elem + 11, gtk)) { wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " "decrypt GTK"); return -1; @@ -615,10 +738,24 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, size_t igtk_elem_len) { - u8 igtk[WPA_IGTK_LEN]; + u8 igtk[WPA_IGTK_MAX_LEN]; + size_t igtk_len; u16 keyidx; + const u8 *kek; + size_t kek_len; - if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) + if (wpa_key_mgmt_fils(sm->key_mgmt)) { + kek = sm->ptk.kek2; + kek_len = sm->ptk.kek2_len; + } else { + kek = sm->ptk.kek; + kek_len = sm->ptk.kek_len; + } + + if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC && + sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_128 && + sm->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_256 && + sm->mgmt_group_cipher != WPA_CIPHER_BIP_CMAC_256) return 0; if (igtk_elem == NULL) { @@ -629,19 +766,19 @@ static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, wpa_hexdump_key(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp", igtk_elem, igtk_elem_len); - if (igtk_elem_len != 2 + 6 + 1 + WPA_IGTK_LEN + 8) { + igtk_len = wpa_cipher_key_len(sm->mgmt_group_cipher); + if (igtk_elem_len != 2 + 6 + 1 + igtk_len + 8) { wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem " "length %lu", (unsigned long) igtk_elem_len); return -1; } - if (igtk_elem[8] != WPA_IGTK_LEN) { + if (igtk_elem[8] != igtk_len) { wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem Key Length " "%d", igtk_elem[8]); return -1; } - if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, WPA_IGTK_LEN / 8, - igtk_elem + 9, igtk)) { + if (aes_unwrap(kek, kek_len, igtk_len / 8, igtk_elem + 9, igtk)) { wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " "decrypt IGTK"); return -1; @@ -652,13 +789,16 @@ static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, keyidx = WPA_GET_LE16(igtk_elem); wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk, - WPA_IGTK_LEN); - if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0, - igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) < 0) { + igtk_len); + if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher), + broadcast_ether_addr, keyidx, 0, + igtk_elem + 2, 6, igtk, igtk_len) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the " "driver."); + os_memset(igtk, 0, sizeof(igtk)); return -1; } + os_memset(igtk, 0, sizeof(igtk)); return 0; } @@ -670,9 +810,13 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, { struct wpa_ft_ies parse; struct rsn_mdie *mdie; - struct rsn_ftie *ftie; unsigned int count; u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; + const u8 *kck; + size_t kck_len; + int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt); + const u8 *anonce, *snonce, *fte_mic; + u8 fte_elem_count; wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); @@ -687,7 +831,7 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, return 0; } - if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); return -1; } @@ -700,25 +844,47 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, return -1; } - ftie = (struct rsn_ftie *) parse.ftie; - if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { - wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); - return -1; + if (use_sha384) { + struct rsn_ftie_sha384 *ftie; + + ftie = (struct rsn_ftie_sha384 *) parse.ftie; + if (!ftie || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return -1; + } + + anonce = ftie->anonce; + snonce = ftie->snonce; + fte_elem_count = ftie->mic_control[1]; + fte_mic = ftie->mic; + } else { + struct rsn_ftie *ftie; + + ftie = (struct rsn_ftie *) parse.ftie; + if (!ftie || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return -1; + } + + anonce = ftie->anonce; + snonce = ftie->snonce; + fte_elem_count = ftie->mic_control[1]; + fte_mic = ftie->mic; } - if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) { + if (os_memcmp(snonce, sm->snonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", - ftie->snonce, WPA_NONCE_LEN); + snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", sm->snonce, WPA_NONCE_LEN); return -1; } - if (os_memcmp(ftie->anonce, sm->anonce, WPA_NONCE_LEN) != 0) { + if (os_memcmp(anonce, sm->anonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received ANonce", - ftie->anonce, WPA_NONCE_LEN); + anonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce", sm->anonce, WPA_NONCE_LEN); return -1; @@ -763,14 +929,22 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, count = 3; if (parse.ric) count += ieee802_11_ie_count(parse.ric, parse.ric_len); - if (ftie->mic_control[1] != count) { + if (fte_elem_count != count) { wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " "Control: received %u expected %u", - ftie->mic_control[1], count); + fte_elem_count, count); return -1; } - if (wpa_ft_mic(sm->ptk.kck, sm->ptk.kck_len, sm->own_addr, src_addr, 6, + if (wpa_key_mgmt_fils(sm->key_mgmt)) { + kck = sm->ptk.kck2; + kck_len = sm->ptk.kck2_len; + } else { + kck = sm->ptk.kck; + kck_len = sm->ptk.kck_len; + } + + if (wpa_ft_mic(kck, kck_len, sm->own_addr, src_addr, 6, parse.mdie - 2, parse.mdie_len + 2, parse.ftie - 2, parse.ftie_len + 2, parse.rsn - 2, parse.rsn_len + 2, @@ -780,9 +954,9 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, return -1; } - if (os_memcmp_const(mic, ftie->mic, 16) != 0) { + if (os_memcmp_const(mic, fte_mic, 16) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); - wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16); + wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", fte_mic, 16); wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); return -1; } diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h index 56f88dcdd8991..b94b17a85a3a6 100644 --- a/src/rsn_supp/wpa_i.h +++ b/src/rsn_supp/wpa_i.h @@ -1,6 +1,6 @@ /* * Internal WPA/RSN supplicant state machine definitions - * Copyright (c) 2004-2015, 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. @@ -11,7 +11,6 @@ #include "utils/list.h" -struct wpa_peerkey; struct wpa_tdls_peer; struct wpa_eapol_key; @@ -57,7 +56,6 @@ struct wpa_sm { int fast_reauth; /* whether EAP fast re-authentication is enabled */ void *network_ctx; - int peerkey_enabled; int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */ int proactive_key_caching; int eap_workaround; @@ -94,9 +92,6 @@ struct wpa_sm { u8 *ap_wpa_ie, *ap_rsn_ie; size_t ap_wpa_ie_len, ap_rsn_ie_len; -#ifdef CONFIG_PEERKEY - struct wpa_peerkey *peerkey; -#endif /* CONFIG_PEERKEY */ #ifdef CONFIG_TDLS struct wpa_tdls_peer *tdls; int tdls_prohibited; @@ -117,11 +112,14 @@ struct wpa_sm { #endif /* CONFIG_TDLS */ #ifdef CONFIG_IEEE80211R - u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */ + u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the + * first 384 bits of MSK */ size_t xxkey_len; - u8 pmk_r0[PMK_LEN]; + u8 pmk_r0[PMK_LEN_MAX]; + size_t pmk_r0_len; u8 pmk_r0_name[WPA_PMK_NAME_LEN]; - u8 pmk_r1[PMK_LEN]; + u8 pmk_r1[PMK_LEN_MAX]; + size_t pmk_r1_len; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; @@ -144,6 +142,31 @@ struct wpa_sm { #ifdef CONFIG_TESTING_OPTIONS struct wpabuf *test_assoc_ie; #endif /* CONFIG_TESTING_OPTIONS */ + +#ifdef CONFIG_FILS + u8 fils_nonce[FILS_NONCE_LEN]; + u8 fils_session[FILS_SESSION_LEN]; + u8 fils_anonce[FILS_NONCE_LEN]; + u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN]; + u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN]; + size_t fils_key_auth_len; + unsigned int fils_completed:1; + unsigned int fils_erp_pmkid_set:1; + unsigned int fils_cache_id_set:1; + u8 fils_erp_pmkid[PMKID_LEN]; + u8 fils_cache_id[FILS_CACHE_ID_LEN]; + struct crypto_ecdh *fils_ecdh; + int fils_dh_group; + size_t fils_dh_elem_len; + struct wpabuf *fils_ft_ies; + u8 fils_ft[FILS_FT_MAX_LEN]; + size_t fils_ft_len; +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE + struct crypto_ecdh *owe_ecdh; + u16 owe_group; +#endif /* CONFIG_OWE */ }; @@ -215,18 +238,23 @@ static inline u8 * wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type, msg_len, data_pos); } -static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, const u8 *bssid, - const u8 *pmkid) +static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, void *network_ctx, + const u8 *bssid, const u8 *pmkid, + const u8 *cache_id, const u8 *pmk, + size_t pmk_len) { WPA_ASSERT(sm->ctx->add_pmkid); - return sm->ctx->add_pmkid(sm->ctx->ctx, bssid, pmkid); + return sm->ctx->add_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid, + cache_id, pmk, pmk_len); } -static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, const u8 *bssid, - const u8 *pmkid) +static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, void *network_ctx, + const u8 *bssid, const u8 *pmkid, + const u8 *cache_id) { WPA_ASSERT(sm->ctx->remove_pmkid); - return sm->ctx->remove_pmkid(sm->ctx->ctx, bssid, pmkid); + return sm->ctx->remove_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid, + cache_id); } static inline int wpa_sm_mlme_setprotection(struct wpa_sm *sm, const u8 *addr, @@ -359,7 +387,16 @@ static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm, return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len); } -int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, +static inline void wpa_sm_fils_hlp_rx(struct wpa_sm *sm, + const u8 *dst, const u8 *src, + const u8 *pkt, size_t pkt_len) +{ + if (sm->ctx->fils_hlp_rx) + sm->ctx->fils_hlp_rx(sm->ctx->ctx, dst, src, pkt, pkt_len); +} + + +int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk, int ver, const u8 *dest, u16 proto, u8 *msg, size_t msg_len, u8 *key_mic); int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c index c44844ec583bf..a3410d15447a4 100644 --- a/src/rsn_supp/wpa_ie.c +++ b/src/rsn_supp/wpa_ie.c @@ -1,6 +1,6 @@ /* * wpa_supplicant - WPA/RSN IE and KDE processing - * 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. @@ -161,6 +161,10 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, #ifdef CONFIG_IEEE80211R } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); +#ifdef CONFIG_SHA384 + } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384); +#endif /* CONFIG_SHA384 */ } else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); #endif /* CONFIG_IEEE80211R */ @@ -180,6 +184,30 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192); } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B) { RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B); +#ifdef CONFIG_FILS + } else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256); + } else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384); +#ifdef CONFIG_IEEE80211R + } else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256); + } else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384); +#endif /* CONFIG_IEEE80211R */ +#endif /* CONFIG_FILS */ +#ifdef CONFIG_OWE + } else if (key_mgmt & WPA_KEY_MGMT_OWE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE); +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + } else if (key_mgmt & WPA_KEY_MGMT_DPP) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP); +#endif /* CONFIG_DPP */ +#ifdef CONFIG_HS20 + } else if (key_mgmt & WPA_KEY_MGMT_OSEN) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN); +#endif /* CONFIG_HS20 */ } else { wpa_printf(MSG_WARNING, "Invalid key management type (%d).", key_mgmt); @@ -405,44 +433,6 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, return 0; } -#ifdef CONFIG_PEERKEY - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) { - ie->smk = pos + 2 + RSN_SELECTOR_LEN; - ie->smk_len = pos[1] - RSN_SELECTOR_LEN; - wpa_hexdump_key(MSG_DEBUG, "WPA: SMK in EAPOL-Key", - pos, pos[1] + 2); - return 0; - } - - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) { - ie->nonce = pos + 2 + RSN_SELECTOR_LEN; - ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; - wpa_hexdump(MSG_DEBUG, "WPA: Nonce in EAPOL-Key", - pos, pos[1] + 2); - return 0; - } - - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) { - ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; - ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; - wpa_hexdump(MSG_DEBUG, "WPA: Lifetime in EAPOL-Key", - pos, pos[1] + 2); - return 0; - } - - if (pos[1] > RSN_SELECTOR_LEN + 2 && - RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) { - ie->error = pos + 2 + RSN_SELECTOR_LEN; - ie->error_len = pos[1] - RSN_SELECTOR_LEN; - wpa_hexdump(MSG_DEBUG, "WPA: Error in EAPOL-Key", - pos, pos[1] + 2); - return 0; - } -#endif /* CONFIG_PEERKEY */ - #ifdef CONFIG_IEEE80211W if (pos[1] > RSN_SELECTOR_LEN + 2 && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h index fe95af0abc519..0e72af56029eb 100644 --- a/src/rsn_supp/wpa_ie.h +++ b/src/rsn_supp/wpa_ie.h @@ -21,16 +21,6 @@ struct wpa_eapol_ie_parse { size_t gtk_len; const u8 *mac_addr; size_t mac_addr_len; -#ifdef CONFIG_PEERKEY - const u8 *smk; - size_t smk_len; - const u8 *nonce; - size_t nonce_len; - const u8 *lifetime; - size_t lifetime_len; - const u8 *error; - size_t error_len; -#endif /* CONFIG_PEERKEY */ #ifdef CONFIG_IEEE80211W const u8 *igtk; size_t igtk_len; diff --git a/src/tls/libtommath.c b/src/tls/libtommath.c index 8bc824f20dcdc..4f7a14823d725 100644 --- a/src/tls/libtommath.c +++ b/src/tls/libtommath.c @@ -116,7 +116,7 @@ typedef int mp_err; #define MP_PREC 32 /* default digits of precision */ #else #define MP_PREC 8 /* default digits of precision */ - #endif + #endif #endif /* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */ @@ -274,8 +274,8 @@ static int s_mp_add (mp_int * a, mp_int * b, mp_int * c) *tmpc++ &= MP_MASK; } - /* now copy higher words if any, that is in A+B - * if A or B has more digits add those in + /* now copy higher words if any, that is in A+B + * if A or B has more digits add those in */ if (min != max) { for (; i < max; i++) { @@ -499,29 +499,29 @@ static int mp_mul (mp_int * a, mp_int * b, mp_int * c) #ifdef BN_MP_TOOM_MUL_C if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) { res = mp_toom_mul(a, b, c); - } else + } else #endif #ifdef BN_MP_KARATSUBA_MUL_C /* use Karatsuba? */ if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) { res = mp_karatsuba_mul (a, b, c); - } else + } else #endif { /* can we use the fast multiplier? * - * The fast multiplier can be used if the output will - * have less than MP_WARRAY digits and the number of + * The fast multiplier can be used if the output will + * have less than MP_WARRAY digits and the number of * digits won't affect carry propagation */ #ifdef BN_FAST_S_MP_MUL_DIGS_C int digs = a->used + b->used + 1; if ((digs < MP_WARRAY) && - MIN(a->used, b->used) <= + MIN(a->used, b->used) <= (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { res = fast_s_mp_mul_digs (a, b, c, digs); - } else + } else #endif #ifdef BN_S_MP_MUL_DIGS_C res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */ @@ -629,7 +629,7 @@ static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) err = mp_exptmod(&tmpG, &tmpX, P, Y); mp_clear_multi(&tmpG, &tmpX, NULL); return err; -#else +#else #error mp_exptmod would always fail /* no invmod */ return MP_VAL; @@ -658,7 +658,7 @@ static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y) dr = mp_reduce_is_2k(P) << 1; } #endif - + /* if the modulus is odd or dr != 0 use the montgomery method */ #ifdef BN_MP_EXPTMOD_FAST_C if (mp_isodd (P) == 1 || dr != 0) { @@ -693,7 +693,7 @@ static int mp_cmp (mp_int * a, mp_int * b) return MP_GT; } } - + /* compare digits */ if (a->sign == MP_NEG) { /* if negative compare opposite direction */ @@ -779,7 +779,7 @@ static int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c) } /* init temps */ - if ((res = mp_init_multi(&x, &y, &u, &v, + if ((res = mp_init_multi(&x, &y, &u, &v, &A, &B, &C, &D, NULL)) != MP_OKAY) { return res; } @@ -906,14 +906,14 @@ top: goto LBL_ERR; } } - + /* too big */ while (mp_cmp_mag(&C, b) != MP_LT) { if ((res = mp_sub(&C, b, &C)) != MP_OKAY) { goto LBL_ERR; } } - + /* C is now the inverse */ mp_exch (&C, c); res = MP_OKAY; @@ -933,7 +933,7 @@ static int mp_cmp_mag (mp_int * a, mp_int * b) if (a->used > b->used) { return MP_GT; } - + if (a->used < b->used) { return MP_LT; } @@ -1199,8 +1199,8 @@ static void mp_rshd (mp_int * a, int b) /* top [offset into digits] */ top = a->dp + b; - /* this is implemented as a sliding window where - * the window is b-digits long and digits from + /* this is implemented as a sliding window where + * the window is b-digits long and digits from * the top of the window are copied to the bottom * * e.g. @@ -1218,13 +1218,13 @@ static void mp_rshd (mp_int * a, int b) *bottom++ = 0; } } - + /* remove excess digits */ a->used -= b; } -/* swap the elements of two integers, for cases where you can't simply swap the +/* swap the elements of two integers, for cases where you can't simply swap the * mp_int pointers around */ static void mp_exch (mp_int * a, mp_int * b) @@ -1237,7 +1237,7 @@ static void mp_exch (mp_int * a, mp_int * b) } -/* trim unused digits +/* trim unused digits * * This is used to ensure that leading zero digits are * trimed and the leading "used" digit will be non-zero @@ -1298,7 +1298,7 @@ static int mp_grow (mp_int * a, int size) #ifdef BN_MP_ABS_C -/* b = |a| +/* b = |a| * * Simple function copies the input and fixes the sign to positive */ @@ -1434,7 +1434,7 @@ static int mp_mul_2d (mp_int * a, int b, mp_int * c) /* set the carry to the carry bits of the current word */ r = rr; } - + /* set final carry */ if (r != 0) { c->dp[(c->used)++] = r; @@ -1446,7 +1446,7 @@ static int mp_mul_2d (mp_int * a, int b, mp_int * c) #ifdef BN_MP_INIT_MULTI_C -static int mp_init_multi(mp_int *mp, ...) +static int mp_init_multi(mp_int *mp, ...) { mp_err res = MP_OKAY; /* Assume ok until proven otherwise */ int n = 0; /* Number of ok inits */ @@ -1460,11 +1460,11 @@ static int mp_init_multi(mp_int *mp, ...) succeeded in init-ing, then return error. */ va_list clean_args; - + /* end the current list */ va_end(args); - - /* now start cleaning up */ + + /* now start cleaning up */ cur_arg = mp; va_start(clean_args, mp); while (n--) { @@ -1484,7 +1484,7 @@ static int mp_init_multi(mp_int *mp, ...) #ifdef BN_MP_CLEAR_MULTI_C -static void mp_clear_multi(mp_int *mp, ...) +static void mp_clear_multi(mp_int *mp, ...) { mp_int* next_mp = mp; va_list args; @@ -1558,7 +1558,7 @@ static int mp_count_bits (mp_int * a) /* get number of digits and add that */ r = (a->used - 1) * DIGIT_BIT; - + /* take the last digit and count the bits in it */ q = a->dp[a->used - 1]; while (q > ((mp_digit) 0)) { @@ -1628,7 +1628,7 @@ static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) } return res; } - + /* init our temps */ if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) { return res; @@ -1638,7 +1638,7 @@ static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d) mp_set(&tq, 1); n = mp_count_bits(a) - mp_count_bits(b); if (((res = mp_abs(a, &ta)) != MP_OKAY) || - ((res = mp_abs(b, &tb)) != MP_OKAY) || + ((res = mp_abs(b, &tb)) != MP_OKAY) || ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) || ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) { goto LBL_ERR; @@ -1675,17 +1675,17 @@ LBL_ERR: #else -/* integer signed division. +/* integer signed division. * c*b + d == a [e.g. a/b, c=quotient, d=remainder] * HAC pp.598 Algorithm 14.20 * - * Note that the description in HAC is horribly - * incomplete. For example, it doesn't consider - * the case where digits are removed from 'x' in - * the inner loop. It also doesn't consider the + * Note that the description in HAC is horribly + * incomplete. For example, it doesn't consider + * the case where digits are removed from 'x' in + * the inner loop. It also doesn't consider the * case that y has fewer than three digits, etc.. * - * The overall algorithm is as described as + * The overall algorithm is as described as * 14.20 from HAC but fixed to treat these cases. */ static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) @@ -1775,7 +1775,7 @@ static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) continue; } - /* step 3.1 if xi == yt then set q{i-t-1} to b-1, + /* step 3.1 if xi == yt then set q{i-t-1} to b-1, * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */ if (x.dp[i] == y.dp[t]) { q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1); @@ -1789,10 +1789,10 @@ static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK)); } - /* while (q{i-t-1} * (yt * b + y{t-1})) > - xi * b**2 + xi-1 * b + xi-2 - - do q{i-t-1} -= 1; + /* while (q{i-t-1} * (yt * b + y{t-1})) > + xi * b**2 + xi-1 * b + xi-2 + + do q{i-t-1} -= 1; */ q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK; do { @@ -1843,10 +1843,10 @@ static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d) } } - /* now q is the quotient and x is the remainder - * [which we have to normalize] + /* now q is the quotient and x is the remainder + * [which we have to normalize] */ - + /* get sign before writing to c */ x.sign = x.used == 0 ? MP_ZPOS : a->sign; @@ -1914,7 +1914,7 @@ static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int red /* init M array */ /* init first cell */ if ((err = mp_init(&M[1])) != MP_OKAY) { - return err; + return err; } /* now init the second half of the array */ @@ -1932,7 +1932,7 @@ static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int red if ((err = mp_init (&mu)) != MP_OKAY) { goto LBL_M; } - + if (redmode == 0) { if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) { goto LBL_MU; @@ -1943,22 +1943,22 @@ static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int red goto LBL_MU; } redux = mp_reduce_2k_l; - } + } /* create M table * - * The M table contains powers of the base, + * The M table contains powers of the base, * e.g. M[x] = G**x mod P * - * The first half of the table is not + * The first half of the table is not * computed though accept for M[0] and M[1] */ if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) { goto LBL_MU; } - /* compute the value at M[1<<(winsize-1)] by squaring - * M[1] (winsize-1) times + /* compute the value at M[1<<(winsize-1)] by squaring + * M[1] (winsize-1) times */ if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) { goto LBL_MU; @@ -1966,7 +1966,7 @@ static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int red for (x = 0; x < (winsize - 1); x++) { /* square it */ - if ((err = mp_sqr (&M[1 << (winsize - 1)], + if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) { goto LBL_MU; } @@ -2117,18 +2117,18 @@ static int mp_sqr (mp_int * a, mp_int * b) if (a->used >= TOOM_SQR_CUTOFF) { res = mp_toom_sqr(a, b); /* Karatsuba? */ - } else + } else #endif #ifdef BN_MP_KARATSUBA_SQR_C if (a->used >= KARATSUBA_SQR_CUTOFF) { res = mp_karatsuba_sqr (a, b); - } else + } else #endif { #ifdef BN_FAST_S_MP_SQR_C /* can we use the fast comba multiplier? */ - if ((a->used * 2 + 1) < MP_WARRAY && - a->used < + if ((a->used * 2 + 1) < MP_WARRAY && + a->used < (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) { res = fast_s_mp_sqr (a, b); } else @@ -2145,7 +2145,7 @@ if (a->used >= KARATSUBA_SQR_CUTOFF) { } -/* reduces a modulo n where n is of the form 2**p - d +/* reduces a modulo n where n is of the form 2**p - d This differs from reduce_2k since "d" can be larger than a single digit. */ @@ -2153,33 +2153,33 @@ static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d) { mp_int q; int p, res; - + if ((res = mp_init(&q)) != MP_OKAY) { return res; } - - p = mp_count_bits(n); + + p = mp_count_bits(n); top: /* q = a/2**p, a = a mod 2**p */ if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) { goto ERR; } - + /* q = q * d */ - if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { + if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { goto ERR; } - + /* a = a + q */ if ((res = s_mp_add(a, &q, a)) != MP_OKAY) { goto ERR; } - + if (mp_cmp_mag(a, n) != MP_LT) { s_mp_sub(a, n, a); goto top; } - + ERR: mp_clear(&q); return res; @@ -2191,26 +2191,26 @@ static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d) { int res; mp_int tmp; - + if ((res = mp_init(&tmp)) != MP_OKAY) { return res; } - + if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) { goto ERR; } - + if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) { goto ERR; } - + ERR: mp_clear(&tmp); return res; } -/* computes a = 2**b +/* computes a = 2**b * * Simple algorithm which zeroes the int, grows it then just sets one bit * as required. @@ -2243,7 +2243,7 @@ static int mp_2expt (mp_int * a, int b) static int mp_reduce_setup (mp_int * a, mp_int * b) { int res; - + if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) { return res; } @@ -2251,7 +2251,7 @@ static int mp_reduce_setup (mp_int * a, mp_int * b) } -/* reduces x mod m, assumes 0 < x < m**2, mu is +/* reduces x mod m, assumes 0 < x < m**2, mu is * precomputed via mp_reduce_setup. * From HAC pp.604 Algorithm 14.42 */ @@ -2266,7 +2266,7 @@ static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu) } /* q1 = x / b**(k-1) */ - mp_rshd (&q, um - 1); + mp_rshd (&q, um - 1); /* according to HAC this optimization is ok */ if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) { @@ -2282,8 +2282,8 @@ static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu) if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) { goto CLEANUP; } -#else - { +#else + { #error mp_reduce would always fail res = MP_VAL; goto CLEANUP; @@ -2292,7 +2292,7 @@ static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu) } /* q3 = q2 / b**(k+1) */ - mp_rshd (&q, um + 1); + mp_rshd (&q, um + 1); /* x = x mod b**(k+1), quick (no division) */ if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) { @@ -2326,7 +2326,7 @@ static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu) goto CLEANUP; } } - + CLEANUP: mp_clear (&q); @@ -2335,7 +2335,7 @@ CLEANUP: /* multiplies |a| * |b| and only computes up to digs digits of result - * HAC pp. 595, Algorithm 14.12 Modified so you can control how + * HAC pp. 595, Algorithm 14.12 Modified so you can control how * many digits of output are created. */ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) @@ -2349,7 +2349,7 @@ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) #ifdef BN_FAST_S_MP_MUL_DIGS_C /* can we use the fast multiplier? */ if (((digs) < MP_WARRAY) && - MIN (a->used, b->used) < + MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { return fast_s_mp_mul_digs (a, b, c, digs); } @@ -2372,10 +2372,10 @@ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) /* setup some aliases */ /* copy of the digit from a used within the nested loop */ tmpx = a->dp[ix]; - + /* an alias for the destination shifted ix places */ tmpt = t.dp + ix; - + /* an alias for the digits of b */ tmpy = b->dp; @@ -2409,15 +2409,15 @@ static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) #ifdef BN_FAST_S_MP_MUL_DIGS_C /* Fast (comba) multiplier * - * This is the fast column-array [comba] multiplier. It is - * designed to compute the columns of the product first - * then handle the carries afterwards. This has the effect + * This is the fast column-array [comba] multiplier. It is + * designed to compute the columns of the product first + * then handle the carries afterwards. This has the effect * of making the nested loops that compute the columns very * simple and schedulable on super-scalar processors. * - * This has been modified to produce a variable number of - * digits of output so if say only a half-product is required - * you don't have to compute the upper half (a feature + * This has been modified to produce a variable number of + * digits of output so if say only a half-product is required + * you don't have to compute the upper half (a feature * required for fast Barrett reduction). * * Based on Algorithm 14.12 on pp.595 of HAC. @@ -2441,7 +2441,7 @@ static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) /* clear the carry */ _W = 0; - for (ix = 0; ix < pa; ix++) { + for (ix = 0; ix < pa; ix++) { int tx, ty; int iy; mp_digit *tmpx, *tmpy; @@ -2454,7 +2454,7 @@ static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs) tmpx = a->dp + tx; tmpy = b->dp + ty; - /* this is the number of times the loop will iterrate, essentially + /* this is the number of times the loop will iterrate, essentially while (tx++ < a->used && ty-- >= 0) { ... } */ iy = MIN(a->used-tx, ty+1); @@ -2501,8 +2501,8 @@ static int mp_init_size (mp_int * a, int size) int x; /* pad size so there are always extra digits */ - size += (MP_PREC * 2) - (size % MP_PREC); - + size += (MP_PREC * 2) - (size % MP_PREC); + /* alloc mem */ a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size); if (a->dp == NULL) { @@ -2556,7 +2556,7 @@ static int s_mp_sqr (mp_int * a, mp_int * b) /* alias for where to store the results */ tmpt = t.dp + (2*ix + 1); - + for (iy = ix + 1; iy < pa; iy++) { /* first calculate the product */ r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]); @@ -2863,24 +2863,24 @@ static int mp_mul_2(mp_int * a, mp_int * b) /* alias for source */ tmpa = a->dp; - + /* alias for dest */ tmpb = b->dp; /* carry */ r = 0; for (x = 0; x < a->used; x++) { - - /* get what will be the *next* carry bit from the - * MSB of the current digit + + /* get what will be the *next* carry bit from the + * MSB of the current digit */ rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1)); - + /* now shift up this digit, add in the carry [from the previous] */ *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK; - - /* copy the carry that would be from the source - * digit into the next iteration + + /* copy the carry that would be from the source + * digit into the next iteration */ r = rr; } @@ -2892,8 +2892,8 @@ static int mp_mul_2(mp_int * a, mp_int * b) ++(b->used); } - /* now zero any excess digits on the destination - * that we didn't write to + /* now zero any excess digits on the destination + * that we didn't write to */ tmpb = b->dp + b->used; for (x = b->used; x < oldused; x++) { @@ -3011,7 +3011,7 @@ static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int /* determine and setup reduction code */ if (redmode == 0) { -#ifdef BN_MP_MONTGOMERY_SETUP_C +#ifdef BN_MP_MONTGOMERY_SETUP_C /* now setup montgomery */ if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) { goto LBL_M; @@ -3026,7 +3026,7 @@ static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int if (((P->used * 2 + 1) < MP_WARRAY) && P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) { redux = fast_mp_montgomery_reduce; - } else + } else #endif { #ifdef BN_MP_MONTGOMERY_REDUCE_C @@ -3077,7 +3077,7 @@ static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) { goto LBL_RES; } -#else +#else err = MP_VAL; goto LBL_RES; #endif @@ -3245,10 +3245,10 @@ LBL_M: #ifdef BN_FAST_S_MP_SQR_C /* the jist of squaring... - * you do like mult except the offset of the tmpx [one that - * starts closer to zero] can't equal the offset of tmpy. + * you do like mult except the offset of the tmpx [one that + * starts closer to zero] can't equal the offset of tmpy. * So basically you set up iy like before then you min it with - * (ty-tx) so that it never happens. You double all those + * (ty-tx) so that it never happens. You double all those * you add in the inner loop After that loop you do the squares and add them in. @@ -3270,7 +3270,7 @@ static int fast_s_mp_sqr (mp_int * a, mp_int * b) /* number of output digits to produce */ W1 = 0; - for (ix = 0; ix < pa; ix++) { + for (ix = 0; ix < pa; ix++) { int tx, ty, iy; mp_word _W; mp_digit *tmpy; @@ -3291,7 +3291,7 @@ static int fast_s_mp_sqr (mp_int * a, mp_int * b) */ iy = MIN(a->used-tx, ty+1); - /* now for squaring tx can never equal ty + /* now for squaring tx can never equal ty * we halve the distance since they approach at a rate of 2x * and we have to round because odd cases need to be executed */ diff --git a/src/tls/rsa.c b/src/tls/rsa.c index 0b7b530bc37c2..3525eb9919db3 100644 --- a/src/tls/rsa.c +++ b/src/tls/rsa.c @@ -80,7 +80,7 @@ crypto_rsa_import_public_key(const u8 *buf, size_t len) * PKCS #1, 7.1: * RSAPublicKey ::= SEQUENCE { * modulus INTEGER, -- n - * publicExponent INTEGER -- e + * publicExponent INTEGER -- e * } */ diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c index 9bc0d211f48dd..76e19746b0209 100644 --- a/src/tls/tlsv1_client.c +++ b/src/tls/tlsv1_client.c @@ -264,7 +264,7 @@ failed: * @in_data: Pointer to plaintext data to be encrypted * @in_len: Input buffer length * @out_data: Pointer to output buffer (encrypted TLS data) - * @out_len: Maximum out_data length + * @out_len: Maximum out_data length * Returns: Number of bytes written to out_data, -1 on failure * * This function is used after TLS handshake has been completed successfully to diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c index 244c3cb060821..e66f1a98896de 100644 --- a/src/tls/tlsv1_client_read.c +++ b/src/tls/tlsv1_client_read.c @@ -685,10 +685,9 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, pos, conn->dh_p_len); goto fail; } - conn->dh_p = os_malloc(conn->dh_p_len); + conn->dh_p = os_memdup(pos, conn->dh_p_len); if (conn->dh_p == NULL) goto fail; - os_memcpy(conn->dh_p, pos, conn->dh_p_len); pos += conn->dh_p_len; wpa_hexdump(MSG_DEBUG, "TLSv1: DH p (prime)", conn->dh_p, conn->dh_p_len); @@ -700,10 +699,9 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, if (val == 0 || val > (size_t) (end - pos)) goto fail; conn->dh_g_len = val; - conn->dh_g = os_malloc(conn->dh_g_len); + conn->dh_g = os_memdup(pos, conn->dh_g_len); if (conn->dh_g == NULL) goto fail; - os_memcpy(conn->dh_g, pos, conn->dh_g_len); pos += conn->dh_g_len; wpa_hexdump(MSG_DEBUG, "TLSv1: DH g (generator)", conn->dh_g, conn->dh_g_len); @@ -717,10 +715,9 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn, if (val == 0 || val > (size_t) (end - pos)) goto fail; conn->dh_ys_len = val; - conn->dh_ys = os_malloc(conn->dh_ys_len); + conn->dh_ys = os_memdup(pos, conn->dh_ys_len); if (conn->dh_ys == NULL) goto fail; - os_memcpy(conn->dh_ys, pos, conn->dh_ys_len); pos += conn->dh_ys_len; wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)", conn->dh_ys, conn->dh_ys_len); diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c index 6b28417e499c7..e178915a454d7 100644 --- a/src/tls/tlsv1_common.c +++ b/src/tls/tlsv1_common.c @@ -21,7 +21,7 @@ * RFC 2246 Section 9: Mandatory to implement TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA * Add support for commonly used cipher suites; don't bother with exportable * suites. - */ + */ static const struct tls_cipher_suite tls_cipher_suites[] = { { TLS_NULL_WITH_NULL_NULL, TLS_KEY_X_NULL, TLS_CIPHER_NULL, @@ -482,21 +482,21 @@ int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk, os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01" "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0) { - wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-256"); + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-256"); decrypted = buf + 19; buflen -= 19; } else if (buflen >= 19 + 48 && os_memcmp(buf, "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01" "\x65\x03\x04\x02\x02\x05\x00\x04\x30", 19) == 0) { - wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-384"); + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-384"); decrypted = buf + 19; buflen -= 19; } else if (buflen >= 19 + 64 && os_memcmp(buf, "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01" "\x65\x03\x04\x02\x03\x05\x00\x04\x40", 19) == 0) { - wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-512"); + wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-512"); decrypted = buf + 19; buflen -= 19; diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c index 52c1ae0143da3..842e5dd728c78 100644 --- a/src/tls/tlsv1_cred.c +++ b/src/tls/tlsv1_cred.c @@ -1166,10 +1166,9 @@ static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred, if (hdr.length == 0) return -1; os_free(cred->dh_p); - cred->dh_p = os_malloc(hdr.length); + cred->dh_p = os_memdup(hdr.payload, hdr.length); if (cred->dh_p == NULL) return -1; - os_memcpy(cred->dh_p, hdr.payload, hdr.length); cred->dh_p_len = hdr.length; pos = hdr.payload + hdr.length; @@ -1188,10 +1187,9 @@ static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred, if (hdr.length == 0) return -1; os_free(cred->dh_g); - cred->dh_g = os_malloc(hdr.length); + cred->dh_g = os_memdup(hdr.payload, hdr.length); if (cred->dh_g == NULL) return -1; - os_memcpy(cred->dh_g, hdr.payload, hdr.length); cred->dh_g_len = hdr.length; return 0; diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c index ba47337bcbb11..5406969040959 100644 --- a/src/tls/tlsv1_server.c +++ b/src/tls/tlsv1_server.c @@ -216,7 +216,7 @@ failed: * @in_data: Pointer to plaintext data to be encrypted * @in_len: Input buffer length * @out_data: Pointer to output buffer (encrypted TLS data) - * @out_len: Maximum out_data length + * @out_len: Maximum out_data length * Returns: Number of bytes written to out_data, -1 on failure * * This function is used after TLS handshake has been completed successfully to diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c index 75f222c4f2495..f80c9a358bf52 100644 --- a/src/tls/x509v3.c +++ b/src/tls/x509v3.c @@ -274,13 +274,12 @@ static int x509_parse_public_key(const u8 *buf, size_t len, */ } os_free(cert->public_key); - cert->public_key = os_malloc(hdr.length - 1); + cert->public_key = os_memdup(pos + 1, hdr.length - 1); if (cert->public_key == NULL) { wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " "public key"); return -1; } - os_memcpy(cert->public_key, pos + 1, hdr.length - 1); cert->public_key_len = hdr.length - 1; wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey", cert->public_key, cert->public_key_len); @@ -925,10 +924,9 @@ static int x509_parse_alt_name_ip(struct x509_name *name, /* iPAddress OCTET STRING */ wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len); os_free(name->ip); - name->ip = os_malloc(len); + name->ip = os_memdup(pos, len); if (name->ip == NULL) return -1; - os_memcpy(name->ip, pos, len); name->ip_len = len; return 0; } @@ -1700,14 +1698,13 @@ struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) return NULL; } os_free(cert->sign_value); - cert->sign_value = os_malloc(hdr.length - 1); + cert->sign_value = os_memdup(pos + 1, hdr.length - 1); if (cert->sign_value == NULL) { wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " "signatureValue"); x509_certificate_free(cert); return NULL; } - os_memcpy(cert->sign_value, pos + 1, hdr.length - 1); cert->sign_value_len = hdr.length - 1; wpa_hexdump(MSG_MSGDUMP, "X509: signature", cert->sign_value, cert->sign_value_len); @@ -2039,7 +2036,7 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted, for (cert = chain, idx = 0; cert; cert = cert->next, idx++) { cert->issuer_trusted = 0; - x509_name_string(&cert->subject, buf, sizeof(buf)); + x509_name_string(&cert->subject, buf, sizeof(buf)); wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf); if (chain_trusted) @@ -2063,11 +2060,11 @@ int x509_certificate_chain_validate(struct x509_certificate *trusted, wpa_printf(MSG_DEBUG, "X509: Certificate " "chain issuer name mismatch"); x509_name_string(&cert->issuer, buf, - sizeof(buf)); + sizeof(buf)); wpa_printf(MSG_DEBUG, "X509: cert issuer: %s", buf); x509_name_string(&cert->next->subject, buf, - sizeof(buf)); + sizeof(buf)); wpa_printf(MSG_DEBUG, "X509: next cert " "subject: %s", buf); *reason = X509_VALIDATE_CERTIFICATE_UNKNOWN; diff --git a/src/utils/Makefile b/src/utils/Makefile index 8aad813cfc858..52efc5321fca1 100644 --- a/src/utils/Makefile +++ b/src/utils/Makefile @@ -17,6 +17,7 @@ LIB_OBJS= \ base64.o \ bitfield.o \ common.o \ + crc32.o \ ip_addr.o \ radiotap.o \ trace.o \ diff --git a/src/utils/base64.c b/src/utils/base64.c index d44f290e56849..8eb4ba127d480 100644 --- a/src/utils/base64.c +++ b/src/utils/base64.c @@ -13,21 +13,14 @@ static const unsigned char base64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const unsigned char base64_url_table[65] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; -/** - * base64_encode - Base64 encode - * @src: Data to be encoded - * @len: Length of the data to be encoded - * @out_len: Pointer to output length variable, or %NULL if not used - * Returns: Allocated buffer of out_len bytes of encoded data, - * or %NULL on failure - * - * Caller is responsible for freeing the returned buffer. Returned buffer is - * nul terminated to make it easier to use as a C string. The nul terminator is - * not included in out_len. - */ -unsigned char * base64_encode(const unsigned char *src, size_t len, - size_t *out_len) + +static unsigned char * base64_gen_encode(const unsigned char *src, size_t len, + size_t *out_len, + const unsigned char *table, + int add_pad) { unsigned char *out, *pos; const unsigned char *end, *in; @@ -35,7 +28,8 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, int line_len; olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */ - olen += olen / 72; /* line feeds */ + if (add_pad) + olen += olen / 72; /* line feeds */ olen++; /* nul termination */ if (olen < len) return NULL; /* integer overflow */ @@ -48,35 +42,35 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, pos = out; line_len = 0; while (end - in >= 3) { - *pos++ = base64_table[(in[0] >> 2) & 0x3f]; - *pos++ = base64_table[(((in[0] & 0x03) << 4) | - (in[1] >> 4)) & 0x3f]; - *pos++ = base64_table[(((in[1] & 0x0f) << 2) | - (in[2] >> 6)) & 0x3f]; - *pos++ = base64_table[in[2] & 0x3f]; + *pos++ = table[(in[0] >> 2) & 0x3f]; + *pos++ = table[(((in[0] & 0x03) << 4) | (in[1] >> 4)) & 0x3f]; + *pos++ = table[(((in[1] & 0x0f) << 2) | (in[2] >> 6)) & 0x3f]; + *pos++ = table[in[2] & 0x3f]; in += 3; line_len += 4; - if (line_len >= 72) { + if (add_pad && line_len >= 72) { *pos++ = '\n'; line_len = 0; } } if (end - in) { - *pos++ = base64_table[(in[0] >> 2) & 0x3f]; + *pos++ = table[(in[0] >> 2) & 0x3f]; if (end - in == 1) { - *pos++ = base64_table[((in[0] & 0x03) << 4) & 0x3f]; - *pos++ = '='; + *pos++ = table[((in[0] & 0x03) << 4) & 0x3f]; + if (add_pad) + *pos++ = '='; } else { - *pos++ = base64_table[(((in[0] & 0x03) << 4) | - (in[1] >> 4)) & 0x3f]; - *pos++ = base64_table[((in[1] & 0x0f) << 2) & 0x3f]; + *pos++ = table[(((in[0] & 0x03) << 4) | + (in[1] >> 4)) & 0x3f]; + *pos++ = table[((in[1] & 0x0f) << 2) & 0x3f]; } - *pos++ = '='; + if (add_pad) + *pos++ = '='; line_len += 4; } - if (line_len) + if (add_pad && line_len) *pos++ = '\n'; *pos = '\0'; @@ -86,26 +80,18 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, } -/** - * base64_decode - Base64 decode - * @src: Data to be decoded - * @len: Length of the data to be decoded - * @out_len: Pointer to output length variable - * Returns: Allocated buffer of out_len bytes of decoded data, - * or %NULL on failure - * - * Caller is responsible for freeing the returned buffer. - */ -unsigned char * base64_decode(const unsigned char *src, size_t len, - size_t *out_len) +static unsigned char * base64_gen_decode(const unsigned char *src, size_t len, + size_t *out_len, + const unsigned char *table) { unsigned char dtable[256], *out, *pos, block[4], tmp; size_t i, count, olen; int pad = 0; + size_t extra_pad; os_memset(dtable, 0x80, 256); for (i = 0; i < sizeof(base64_table) - 1; i++) - dtable[base64_table[i]] = (unsigned char) i; + dtable[table[i]] = (unsigned char) i; dtable['='] = 0; count = 0; @@ -114,21 +100,28 @@ unsigned char * base64_decode(const unsigned char *src, size_t len, count++; } - if (count == 0 || count % 4) + if (count == 0) return NULL; + extra_pad = (4 - count % 4) % 4; - olen = count / 4 * 3; + olen = (count + extra_pad) / 4 * 3; pos = out = os_malloc(olen); if (out == NULL) return NULL; count = 0; - for (i = 0; i < len; i++) { - tmp = dtable[src[i]]; + for (i = 0; i < len + extra_pad; i++) { + unsigned char val; + + if (i >= len) + val = '='; + else + val = src[i]; + tmp = dtable[val]; if (tmp == 0x80) continue; - if (src[i] == '=') + if (val == '=') pad++; block[count] = tmp; count++; @@ -155,3 +148,53 @@ unsigned char * base64_decode(const unsigned char *src, size_t len, *out_len = pos - out; return out; } + + +/** + * base64_encode - Base64 encode + * @src: Data to be encoded + * @len: Length of the data to be encoded + * @out_len: Pointer to output length variable, or %NULL if not used + * Returns: Allocated buffer of out_len bytes of encoded data, + * or %NULL on failure + * + * Caller is responsible for freeing the returned buffer. Returned buffer is + * nul terminated to make it easier to use as a C string. The nul terminator is + * not included in out_len. + */ +unsigned char * base64_encode(const unsigned char *src, size_t len, + size_t *out_len) +{ + return base64_gen_encode(src, len, out_len, base64_table, 1); +} + + +unsigned char * base64_url_encode(const unsigned char *src, size_t len, + size_t *out_len, int add_pad) +{ + return base64_gen_encode(src, len, out_len, base64_url_table, add_pad); +} + + +/** + * base64_decode - Base64 decode + * @src: Data to be decoded + * @len: Length of the data to be decoded + * @out_len: Pointer to output length variable + * Returns: Allocated buffer of out_len bytes of decoded data, + * or %NULL on failure + * + * Caller is responsible for freeing the returned buffer. + */ +unsigned char * base64_decode(const unsigned char *src, size_t len, + size_t *out_len) +{ + return base64_gen_decode(src, len, out_len, base64_table); +} + + +unsigned char * base64_url_decode(const unsigned char *src, size_t len, + size_t *out_len) +{ + return base64_gen_decode(src, len, out_len, base64_url_table); +} diff --git a/src/utils/base64.h b/src/utils/base64.h index aa21fd0fc1b76..5a72c3ebf522f 100644 --- a/src/utils/base64.h +++ b/src/utils/base64.h @@ -13,5 +13,9 @@ unsigned char * base64_encode(const unsigned char *src, size_t len, size_t *out_len); unsigned char * base64_decode(const unsigned char *src, size_t len, size_t *out_len); +unsigned char * base64_url_encode(const unsigned char *src, size_t len, + size_t *out_len, int add_pad); +unsigned char * base64_url_decode(const unsigned char *src, size_t len, + size_t *out_len); #endif /* BASE64_H */ diff --git a/src/utils/browser-wpadebug.c b/src/utils/browser-wpadebug.c index 59ba4d1e02d86..dfb4b67977f87 100644 --- a/src/utils/browser-wpadebug.c +++ b/src/utils/browser-wpadebug.c @@ -52,7 +52,7 @@ static void http_req(void *ctx, struct http_request *req) eloop_terminate(); return; } - wpabuf_put_str(resp, "User input completed"); + wpabuf_put_str(resp, "HTTP/1.1\r\n\r\nUser input completed"); if (done) { eloop_cancel_timeout(browser_timeout, NULL, NULL); @@ -97,6 +97,7 @@ int hs20_web_browser(const char *url) if (pid == 0) { /* run the external command in the child process */ char *argv[14]; + char *envp[] = { "PATH=/system/bin:/vendor/bin", NULL }; argv[0] = "browser-wpadebug"; argv[1] = "start"; @@ -113,8 +114,8 @@ int hs20_web_browser(const char *url) argv[12] = "-3"; /* USER_CURRENT_OR_SELF */ argv[13] = NULL; - execv("/system/bin/am", argv); - wpa_printf(MSG_ERROR, "execv: %s", strerror(errno)); + execve("/system/bin/am", argv, envp); + wpa_printf(MSG_ERROR, "execve: %s", strerror(errno)); exit(0); return -1; } diff --git a/src/utils/common.c b/src/utils/common.c index 04a533a05902f..1eb33705bef34 100644 --- a/src/utils/common.c +++ b/src/utils/common.c @@ -1200,3 +1200,24 @@ int str_starts(const char *str, const char *start) { return os_strncmp(str, start, os_strlen(start)) == 0; } + + +/** + * rssi_to_rcpi - Convert RSSI to RCPI + * @rssi: RSSI to convert + * Returns: RCPI corresponding to the given RSSI value, or 255 if not available. + * + * 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. + */ +u8 rssi_to_rcpi(int rssi) +{ + if (!rssi) + return 255; /* not available */ + if (rssi < -110) + return 0; + if (rssi > 0) + return 220; + return (rssi + 110) * 2; +} diff --git a/src/utils/common.h b/src/utils/common.h index 77856774d215c..f824d001aeab8 100644 --- a/src/utils/common.h +++ b/src/utils/common.h @@ -53,6 +53,15 @@ static inline unsigned int bswap_32(unsigned int v) } #endif /* __APPLE__ */ +#ifdef __rtems__ +#include <rtems/endian.h> +#define __BYTE_ORDER BYTE_ORDER +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#define __BIG_ENDIAN BIG_ENDIAN +#define bswap_16 CPU_swap_u16 +#define bswap_32 CPU_swap_u32 +#endif /* __rtems__ */ + #ifdef CONFIG_NATIVE_WINDOWS #include <winsock.h> @@ -141,6 +150,7 @@ static inline unsigned int wpa_swap_32(unsigned int v) #define host_to_le32(n) (n) #define be_to_host32(n) wpa_swap_32(n) #define host_to_be32(n) wpa_swap_32(n) +#define host_to_le64(n) (n) #define WPA_BYTE_SWAP_DEFINED @@ -331,6 +341,9 @@ static inline void WPA_PUT_LE64(u8 *a, u64 val) #ifndef ETH_P_RRB #define ETH_P_RRB 0x890D #endif /* ETH_P_RRB */ +#ifndef ETH_P_OUI +#define ETH_P_OUI 0x88B7 +#endif /* ETH_P_OUI */ #ifdef __GNUC__ @@ -423,6 +436,7 @@ void perror(const char *s); #define __bitwise __attribute__((bitwise)) #else #define __force +#undef __bitwise #define __bitwise #endif @@ -552,6 +566,7 @@ int is_ctrl_char(char c); int str_starts(const char *str, const char *start); +u8 rssi_to_rcpi(int rssi); /* * gcc 4.4 ends up generating strict-aliasing warnings about some very common diff --git a/src/utils/crc32.c b/src/utils/crc32.c new file mode 100644 index 0000000000000..12d9e2a7008ed --- /dev/null +++ b/src/utils/crc32.c @@ -0,0 +1,85 @@ +/* + * 32-bit CRC for FCS calculation + * Copyright (c) 2010, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/crc32.h" + +/* + * IEEE 802.11 FCS CRC32 + * G(x) = x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + + * x^5 + x^4 + x^2 + x + 1 + */ +static const u32 crc32_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, + 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, + 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, + 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, + 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, + 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, + 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, + 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, + 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, + 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, + 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, + 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, + 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, + 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, + 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, + 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, + 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, + 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, + 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, + 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, + 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, + 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, + 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, + 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, + 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, + 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, + 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, + 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, + 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, + 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, + 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, + 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, + 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, + 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, + 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, + 0x2d02ef8d +}; + + +u32 crc32(const u8 *frame, size_t frame_len) +{ + size_t i; + u32 crc; + + crc = 0xFFFFFFFF; + for (i = 0; i < frame_len; i++) + crc = crc32_table[(crc ^ frame[i]) & 0xff] ^ (crc >> 8); + + return ~crc; +} diff --git a/src/utils/crc32.h b/src/utils/crc32.h new file mode 100644 index 0000000000000..dc31399beb638 --- /dev/null +++ b/src/utils/crc32.h @@ -0,0 +1,14 @@ +/* + * 32-bit CRC for FCS calculation + * Copyright (c) 2010, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef CRC32_H +#define CRC32_H + +u32 crc32(const u8 *frame, size_t frame_len); + +#endif /* CRC32_H */ diff --git a/src/utils/eloop.h b/src/utils/eloop.h index 97af16f0130aa..04ee6d1837b63 100644 --- a/src/utils/eloop.h +++ b/src/utils/eloop.h @@ -45,16 +45,16 @@ typedef void (*eloop_sock_handler)(int sock, void *eloop_ctx, void *sock_ctx); /** * eloop_event_handler - eloop generic event callback type * @eloop_ctx: Registered callback context data (eloop_data) - * @sock_ctx: Registered callback context data (user_data) + * @user_ctx: Registered callback context data (user_data) */ -typedef void (*eloop_event_handler)(void *eloop_data, void *user_ctx); +typedef void (*eloop_event_handler)(void *eloop_ctx, void *user_ctx); /** * eloop_timeout_handler - eloop timeout event callback type * @eloop_ctx: Registered callback context data (eloop_data) - * @sock_ctx: Registered callback context data (user_data) + * @user_ctx: Registered callback context data (user_data) */ -typedef void (*eloop_timeout_handler)(void *eloop_data, void *user_ctx); +typedef void (*eloop_timeout_handler)(void *eloop_ctx, void *user_ctx); /** * eloop_signal_handler - eloop signal event callback type diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c index a06aae8d9b9d0..58519ea8d2489 100644 --- a/src/utils/http_curl.c +++ b/src/utils/http_curl.c @@ -486,12 +486,11 @@ static void add_logo(struct http_ctx *ctx, struct http_cert *hcert, return; n->hash_len = ASN1_STRING_length(hash->hashValue); - n->hash = os_malloc(n->hash_len); + n->hash = os_memdup(ASN1_STRING_data(hash->hashValue), n->hash_len); if (n->hash == NULL) { os_free(n->alg_oid); return; } - os_memcpy(n->hash, ASN1_STRING_data(hash->hashValue), n->hash_len); len = ASN1_STRING_length(uri); n->uri = os_malloc(len + 1); @@ -987,7 +986,7 @@ static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx) ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); - ssl_ctx = ssl->ctx; + ssl_ctx = SSL_get_SSL_CTX(ssl); ctx = SSL_CTX_get_app_data(ssl_ctx); wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify, preverify_ok: %d", @@ -1095,7 +1094,7 @@ static int ocsp_resp_cb(SSL *s, void *arg) { struct http_ctx *ctx = arg; const unsigned char *p; - int len, status, reason; + int len, status, reason, res; OCSP_RESPONSE *rsp; OCSP_BASICRESP *basic; OCSP_CERTID *id; @@ -1200,17 +1199,36 @@ static int ocsp_resp_cb(SSL *s, void *arg) return 0; } - id = OCSP_cert_to_id(NULL, ctx->peer_cert, ctx->peer_issuer); + id = OCSP_cert_to_id(EVP_sha256(), ctx->peer_cert, ctx->peer_issuer); if (!id) { - wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier"); + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not create OCSP certificate identifier (SHA256)"); OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(rsp); ctx->last_err = "Could not create OCSP certificate identifier"; return 0; } - if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, - &this_update, &next_update)) { + res = OCSP_resp_find_status(basic, id, &status, &reason, &produced_at, + &this_update, &next_update); + if (!res) { + id = OCSP_cert_to_id(NULL, ctx->peer_cert, ctx->peer_issuer); + if (!id) { + wpa_printf(MSG_DEBUG, + "OpenSSL: Could not create OCSP certificate identifier (SHA1)"); + OCSP_BASICRESP_free(basic); + OCSP_RESPONSE_free(rsp); + ctx->last_err = + "Could not create OCSP certificate identifier"; + return 0; + } + + res = OCSP_resp_find_status(basic, id, &status, &reason, + &produced_at, &this_update, + &next_update); + } + + if (!res) { wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s", (ctx->ocsp == MANDATORY_OCSP) ? "" : " (OCSP not required)"); diff --git a/src/utils/json.c b/src/utils/json.c new file mode 100644 index 0000000000000..b9130d3a65ab0 --- /dev/null +++ b/src/utils/json.c @@ -0,0 +1,569 @@ +/* + * JavaScript Object Notation (JSON) parser (RFC7159) + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "base64.h" +#include "json.h" + +#define JSON_MAX_DEPTH 10 +#define JSON_MAX_TOKENS 500 + + +void json_escape_string(char *txt, size_t maxlen, const char *data, size_t len) +{ + char *end = txt + maxlen; + size_t i; + + for (i = 0; i < len; i++) { + if (txt + 4 >= end) + break; + + switch (data[i]) { + case '\"': + *txt++ = '\\'; + *txt++ = '\"'; + break; + case '\\': + *txt++ = '\\'; + *txt++ = '\\'; + break; + case '\n': + *txt++ = '\\'; + *txt++ = 'n'; + break; + case '\r': + *txt++ = '\\'; + *txt++ = 'r'; + break; + case '\t': + *txt++ = '\\'; + *txt++ = 't'; + break; + default: + if (data[i] >= 32 && data[i] <= 126) { + *txt++ = data[i]; + } else { + txt += os_snprintf(txt, end - txt, "\\u%04x", + data[i]); + } + break; + } + } + + *txt = '\0'; +} + + +static char * json_parse_string(const char **json_pos, const char *end) +{ + const char *pos = *json_pos; + char *str, *spos, *s_end; + size_t max_len, buf_len; + u8 bin[2]; + + pos++; /* skip starting quote */ + + max_len = end - pos + 1; + buf_len = max_len > 10 ? 10 : max_len; + str = os_malloc(buf_len); + if (!str) + return NULL; + spos = str; + s_end = str + buf_len; + + for (; pos < end; pos++) { + if (buf_len < max_len && s_end - spos < 3) { + char *tmp; + int idx; + + idx = spos - str; + buf_len *= 2; + if (buf_len > max_len) + buf_len = max_len; + tmp = os_realloc(str, buf_len); + if (!tmp) + goto fail; + str = tmp; + spos = str + idx; + s_end = str + buf_len; + } + + switch (*pos) { + case '\"': /* end string */ + *spos = '\0'; + /* caller will move to the next position */ + *json_pos = pos; + return str; + case '\\': + pos++; + switch (*pos) { + case '"': + case '\\': + case '/': + *spos++ = *pos; + break; + case 'n': + *spos++ = '\n'; + break; + case 'r': + *spos++ = '\r'; + break; + case 't': + *spos++ = '\t'; + break; + case 'u': + if (end - pos < 5 || + hexstr2bin(pos + 1, bin, 2) < 0 || + bin[1] == 0x00) { + wpa_printf(MSG_DEBUG, + "JSON: Invalid \\u escape"); + goto fail; + } + if (bin[0] == 0x00) { + *spos++ = bin[1]; + } else { + *spos++ = bin[0]; + *spos++ = bin[1]; + } + pos += 4; + break; + default: + wpa_printf(MSG_DEBUG, + "JSON: Unknown escape '%c'", *pos); + goto fail; + } + break; + default: + *spos++ = *pos; + break; + } + } + +fail: + os_free(str); + return NULL; +} + + +static int json_parse_number(const char **json_pos, const char *end, + int *ret_val) +{ + const char *pos = *json_pos; + size_t len; + char *str; + + for (; pos < end; pos++) { + if (*pos != '-' && (*pos < '0' || *pos > '9')) { + pos--; + break; + } + } + if (pos < *json_pos) + return -1; + len = pos - *json_pos + 1; + str = os_malloc(len + 1); + if (!str) + return -1; + os_memcpy(str, *json_pos, len); + str[len] = '\0'; + + *ret_val = atoi(str); + os_free(str); + *json_pos = pos; + return 0; +} + + +static int json_check_tree_state(struct json_token *token) +{ + if (!token) + return 0; + if (json_check_tree_state(token->child) < 0 || + json_check_tree_state(token->sibling) < 0) + return -1; + if (token->state != JSON_COMPLETED) { + wpa_printf(MSG_DEBUG, + "JSON: Unexpected token state %d (name=%s type=%d)", + token->state, token->name ? token->name : "N/A", + token->type); + return -1; + } + return 0; +} + + +static struct json_token * json_alloc_token(unsigned int *tokens) +{ + (*tokens)++; + if (*tokens > JSON_MAX_TOKENS) { + wpa_printf(MSG_DEBUG, "JSON: Maximum token limit exceeded"); + return NULL; + } + return os_zalloc(sizeof(struct json_token)); +} + + +struct json_token * json_parse(const char *data, size_t data_len) +{ + struct json_token *root = NULL, *curr_token = NULL, *token = NULL; + const char *pos, *end; + char *str; + int num; + unsigned int depth = 0; + unsigned int tokens = 0; + + pos = data; + end = data + data_len; + + for (; pos < end; pos++) { + switch (*pos) { + case '[': /* start array */ + case '{': /* start object */ + if (!curr_token) { + token = json_alloc_token(&tokens); + if (!token) + goto fail; + if (!root) + root = token; + } else if (curr_token->state == JSON_WAITING_VALUE) { + token = curr_token; + } else if (curr_token->parent && + curr_token->parent->type == JSON_ARRAY && + curr_token->parent->state == JSON_STARTED && + curr_token->state == JSON_EMPTY) { + token = curr_token; + } else { + wpa_printf(MSG_DEBUG, + "JSON: Invalid state for start array/object"); + goto fail; + } + depth++; + if (depth > JSON_MAX_DEPTH) { + wpa_printf(MSG_DEBUG, + "JSON: Max depth exceeded"); + goto fail; + } + token->type = *pos == '[' ? JSON_ARRAY : JSON_OBJECT; + token->state = JSON_STARTED; + token->child = json_alloc_token(&tokens); + if (!token->child) + goto fail; + curr_token = token->child; + curr_token->parent = token; + curr_token->state = JSON_EMPTY; + break; + case ']': /* end array */ + case '}': /* end object */ + if (!curr_token || !curr_token->parent || + curr_token->parent->state != JSON_STARTED) { + wpa_printf(MSG_DEBUG, + "JSON: Invalid state for end array/object"); + goto fail; + } + depth--; + curr_token = curr_token->parent; + if ((*pos == ']' && + curr_token->type != JSON_ARRAY) || + (*pos == '}' && + curr_token->type != JSON_OBJECT)) { + wpa_printf(MSG_DEBUG, + "JSON: Array/Object mismatch"); + goto fail; + } + if (curr_token->child->state == JSON_EMPTY && + !curr_token->child->child && + !curr_token->child->sibling) { + /* Remove pending child token since the + * array/object was empty. */ + json_free(curr_token->child); + curr_token->child = NULL; + } + curr_token->state = JSON_COMPLETED; + break; + case '\"': /* string */ + str = json_parse_string(&pos, end); + if (!str) + goto fail; + if (!curr_token) { + token = json_alloc_token(&tokens); + if (!token) + goto fail; + token->type = JSON_STRING; + token->string = str; + token->state = JSON_COMPLETED; + } else if (curr_token->parent && + curr_token->parent->type == JSON_ARRAY && + curr_token->parent->state == JSON_STARTED && + curr_token->state == JSON_EMPTY) { + curr_token->string = str; + curr_token->state = JSON_COMPLETED; + curr_token->type = JSON_STRING; + wpa_printf(MSG_MSGDUMP, + "JSON: String value: '%s'", + curr_token->string); + } else if (curr_token->state == JSON_EMPTY) { + curr_token->type = JSON_VALUE; + curr_token->name = str; + curr_token->state = JSON_STARTED; + } else if (curr_token->state == JSON_WAITING_VALUE) { + curr_token->string = str; + curr_token->state = JSON_COMPLETED; + curr_token->type = JSON_STRING; + wpa_printf(MSG_MSGDUMP, + "JSON: String value: '%s' = '%s'", + curr_token->name, + curr_token->string); + } else { + wpa_printf(MSG_DEBUG, + "JSON: Invalid state for a string"); + os_free(str); + goto fail; + } + break; + case ' ': + case '\t': + case '\r': + case '\n': + /* ignore whitespace */ + break; + case ':': /* name/value separator */ + if (!curr_token || curr_token->state != JSON_STARTED) + goto fail; + curr_token->state = JSON_WAITING_VALUE; + break; + case ',': /* member separator */ + if (!curr_token) + goto fail; + curr_token->sibling = json_alloc_token(&tokens); + if (!curr_token->sibling) + goto fail; + curr_token->sibling->parent = curr_token->parent; + curr_token = curr_token->sibling; + curr_token->state = JSON_EMPTY; + break; + case 't': /* true */ + case 'f': /* false */ + case 'n': /* null */ + if (!((end - pos >= 4 && + os_strncmp(pos, "true", 4) == 0) || + (end - pos >= 5 && + os_strncmp(pos, "false", 5) == 0) || + (end - pos >= 4 && + os_strncmp(pos, "null", 4) == 0))) { + wpa_printf(MSG_DEBUG, + "JSON: Invalid literal name"); + goto fail; + } + if (!curr_token) { + token = json_alloc_token(&tokens); + if (!token) + goto fail; + curr_token = token; + } else if (curr_token->state == JSON_WAITING_VALUE) { + wpa_printf(MSG_MSGDUMP, + "JSON: Literal name: '%s' = %c", + curr_token->name, *pos); + } else if (curr_token->parent && + curr_token->parent->type == JSON_ARRAY && + curr_token->parent->state == JSON_STARTED && + curr_token->state == JSON_EMPTY) { + wpa_printf(MSG_MSGDUMP, + "JSON: Literal name: %c", *pos); + } else { + wpa_printf(MSG_DEBUG, + "JSON: Invalid state for a literal name"); + goto fail; + } + switch (*pos) { + case 't': + curr_token->type = JSON_BOOLEAN; + curr_token->number = 1; + pos += 3; + break; + case 'f': + curr_token->type = JSON_BOOLEAN; + curr_token->number = 0; + pos += 4; + break; + case 'n': + curr_token->type = JSON_NULL; + pos += 3; + break; + } + curr_token->state = JSON_COMPLETED; + break; + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + /* number */ + if (json_parse_number(&pos, end, &num) < 0) + goto fail; + if (!curr_token) { + token = json_alloc_token(&tokens); + if (!token) + goto fail; + token->type = JSON_NUMBER; + token->number = num; + token->state = JSON_COMPLETED; + } else if (curr_token->state == JSON_WAITING_VALUE) { + curr_token->number = num; + curr_token->state = JSON_COMPLETED; + curr_token->type = JSON_NUMBER; + wpa_printf(MSG_MSGDUMP, + "JSON: Number value: '%s' = '%d'", + curr_token->name, + curr_token->number); + } else if (curr_token->parent && + curr_token->parent->type == JSON_ARRAY && + curr_token->parent->state == JSON_STARTED && + curr_token->state == JSON_EMPTY) { + curr_token->number = num; + curr_token->state = JSON_COMPLETED; + curr_token->type = JSON_NUMBER; + wpa_printf(MSG_MSGDUMP, + "JSON: Number value: %d", + curr_token->number); + } else { + wpa_printf(MSG_DEBUG, + "JSON: Invalid state for a number"); + goto fail; + } + break; + default: + wpa_printf(MSG_DEBUG, + "JSON: Unexpected JSON character: %c", *pos); + goto fail; + } + + if (!root) + root = token; + if (!curr_token) + curr_token = token; + } + + if (json_check_tree_state(root) < 0) { + wpa_printf(MSG_DEBUG, "JSON: Incomplete token in the tree"); + goto fail; + } + + return root; +fail: + wpa_printf(MSG_DEBUG, "JSON: Parsing failed"); + json_free(root); + return NULL; +} + + +void json_free(struct json_token *json) +{ + if (!json) + return; + json_free(json->child); + json_free(json->sibling); + os_free(json->name); + os_free(json->string); + os_free(json); +} + + +struct json_token * json_get_member(struct json_token *json, const char *name) +{ + struct json_token *token, *ret = NULL; + + if (!json || json->type != JSON_OBJECT) + return NULL; + /* Return last matching entry */ + for (token = json->child; token; token = token->sibling) { + if (token->name && os_strcmp(token->name, name) == 0) + ret = token; + } + return ret; +} + + +struct wpabuf * json_get_member_base64url(struct json_token *json, + const char *name) +{ + struct json_token *token; + unsigned char *buf; + size_t buflen; + struct wpabuf *ret; + + token = json_get_member(json, name); + if (!token || token->type != JSON_STRING) + return NULL; + buf = base64_url_decode((const unsigned char *) token->string, + os_strlen(token->string), &buflen); + if (!buf) + return NULL; + ret = wpabuf_alloc_ext_data(buf, buflen); + if (!ret) + os_free(buf); + + return ret; +} + + +static const char * json_type_str(enum json_type type) +{ + switch (type) { + case JSON_VALUE: + return "VALUE"; + case JSON_OBJECT: + return "OBJECT"; + case JSON_ARRAY: + return "ARRAY"; + case JSON_STRING: + return "STRING"; + case JSON_NUMBER: + return "NUMBER"; + case JSON_BOOLEAN: + return "BOOLEAN"; + case JSON_NULL: + return "NULL"; + } + return "??"; +} + + +static void json_print_token(struct json_token *token, int depth, + char *buf, size_t buflen) +{ + size_t len; + int ret; + + if (!token) + return; + len = os_strlen(buf); + ret = os_snprintf(buf + len, buflen - len, "[%d:%s:%s]", + depth, json_type_str(token->type), + token->name ? token->name : ""); + if (os_snprintf_error(buflen - len, ret)) { + buf[len] = '\0'; + return; + } + json_print_token(token->child, depth + 1, buf, buflen); + json_print_token(token->sibling, depth, buf, buflen); +} + + +void json_print_tree(struct json_token *root, char *buf, size_t buflen) +{ + buf[0] = '\0'; + json_print_token(root, 1, buf, buflen); +} diff --git a/src/utils/json.h b/src/utils/json.h new file mode 100644 index 0000000000000..8faa95d8bf20d --- /dev/null +++ b/src/utils/json.h @@ -0,0 +1,42 @@ +/* + * JavaScript Object Notation (JSON) parser (RFC7159) + * Copyright (c) 2017, Qualcomm Atheros, Inc. + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef JSON_H +#define JSON_H + +struct json_token { + enum json_type { + JSON_VALUE, + JSON_OBJECT, + JSON_ARRAY, + JSON_STRING, + JSON_NUMBER, + JSON_BOOLEAN, + JSON_NULL, + } type; + enum json_parsing_state { + JSON_EMPTY, + JSON_STARTED, + JSON_WAITING_VALUE, + JSON_COMPLETED, + } state; + char *name; + char *string; + int number; + struct json_token *parent, *child, *sibling; +}; + +void json_escape_string(char *txt, size_t maxlen, const char *data, size_t len); +struct json_token * json_parse(const char *data, size_t data_len); +void json_free(struct json_token *json); +struct json_token * json_get_member(struct json_token *json, const char *name); +struct wpabuf * json_get_member_base64url(struct json_token *json, + const char *name); +void json_print_tree(struct json_token *root, char *buf, size_t buflen); + +#endif /* JSON_H */ diff --git a/src/utils/os.h b/src/utils/os.h index e8f0b792738ab..21ba5c3ff85b3 100644 --- a/src/utils/os.h +++ b/src/utils/os.h @@ -614,6 +614,18 @@ size_t os_strlcpy(char *dest, const char *src, size_t siz); */ int os_memcmp_const(const void *a, const void *b, size_t len); + +/** + * os_memdup - Allocate duplicate of passed memory chunk + * @src: Source buffer to duplicate + * @len: Length of source buffer + * Returns: %NULL if allocation failed, copy of src buffer otherwise + * + * This function allocates a memory block like os_malloc() would, and + * copies the given source buffer into it. + */ +void * os_memdup(const void *src, size_t len); + /** * os_exec - Execute an external program * @program: Path to the program diff --git a/src/utils/os_none.c b/src/utils/os_none.c index 0c3214d32ce50..e74f206a2c5a2 100644 --- a/src/utils/os_none.c +++ b/src/utils/os_none.c @@ -114,6 +114,12 @@ void * os_zalloc(size_t size) } +void * os_memdup(const void *src, size_t n) +{ + return NULL; +} + + #ifdef OS_NO_C_LIB_DEFINES void * os_malloc(size_t size) { diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c index 65c6fa412f207..1894fcdb0cf25 100644 --- a/src/utils/os_unix.c +++ b/src/utils/os_unix.c @@ -80,6 +80,9 @@ int os_get_reltime(struct os_reltime *t) struct timespec ts; int res; + if (TEST_FAIL()) + return -1; + while (1) { res = clock_gettime(clock_id, &ts); if (res == 0) { @@ -505,6 +508,16 @@ int os_memcmp_const(const void *a, const void *b, size_t len) } +void * os_memdup(const void *src, size_t len) +{ + void *r = os_malloc(len); + + if (r) + os_memcpy(r, src, len); + return r; +} + + #ifdef WPA_TRACE #if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS) @@ -537,6 +550,8 @@ static int testing_fail_alloc(void) i++; if (i < res && os_strcmp(func[i], "os_strdup") == 0) i++; + if (i < res && os_strcmp(func[i], "os_memdup") == 0) + i++; pos = wpa_trace_fail_func; diff --git a/src/utils/os_win32.c b/src/utils/os_win32.c index dea27b9f2ad84..f9e4b308ea5fd 100644 --- a/src/utils/os_win32.c +++ b/src/utils/os_win32.c @@ -283,3 +283,13 @@ int os_exec(const char *program, const char *arg, int wait_completion) { return -1; } + + +void * os_memdup(const void *src, size_t len) +{ + void *r = os_malloc(len); + + if (r) + os_memcpy(r, src, len); + return r; +} diff --git a/src/utils/trace.c b/src/utils/trace.c index d72cf604f8e9b..e0b5b0bb99f52 100644 --- a/src/utils/trace.c +++ b/src/utils/trace.c @@ -6,6 +6,10 @@ * See README for more details. */ +#ifdef WPA_TRACE_BFD +#define _GNU_SOURCE +#include <link.h> +#endif /* WPA_TRACE_BCD */ #include "includes.h" #include "common.h" @@ -25,6 +29,28 @@ static struct dl_list active_references = static char *prg_fname = NULL; static bfd *cached_abfd = NULL; static asymbol **syms = NULL; +static unsigned long start_offset; +static int start_offset_looked_up; + + +static int callback(struct dl_phdr_info *info, size_t size, void *data) +{ + /* + * dl_iterate_phdr(3): + * "The first object visited by callback is the main program." + */ + start_offset = info->dlpi_addr; + + /* + * dl_iterate_phdr(3): + * "The dl_iterate_phdr() function walks through the list of an + * application's shared objects and calls the function callback + * once for each object, until either all shared objects have + * been processed or callback returns a nonzero value." + */ + return 1; +} + static void get_prg_fname(void) { @@ -160,7 +186,7 @@ static void wpa_trace_bfd_addr(void *pc) if (abfd == NULL) return; - data.pc = (bfd_hostptr_t) pc; + data.pc = (bfd_hostptr_t) (pc - start_offset); data.found = FALSE; bfd_map_over_sections(abfd, find_addr_sect, &data); @@ -201,7 +227,7 @@ static const char * wpa_trace_bfd_addr2func(void *pc) if (abfd == NULL) return NULL; - data.pc = (bfd_hostptr_t) pc; + data.pc = (bfd_hostptr_t) (pc - start_offset); data.found = FALSE; bfd_map_over_sections(abfd, find_addr_sect, &data); @@ -233,6 +259,11 @@ static void wpa_trace_bfd_init(void) wpa_printf(MSG_INFO, "Failed to read symbols"); return; } + + if (!start_offset_looked_up) { + dl_iterate_phdr(callback, NULL); + start_offset_looked_up = 1; + } } @@ -268,7 +299,7 @@ size_t wpa_trace_calling_func(const char *buf[], size_t len) for (i = 0; i < btrace_num; i++) { struct bfd_data data; - data.pc = (bfd_hostptr_t) btrace_res[i]; + data.pc = (bfd_hostptr_t) (btrace_res[i] - start_offset); data.found = FALSE; bfd_map_over_sections(abfd, find_addr_sect, &data); diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c index abdb79c9879c1..1b8ff82b41731 100644 --- a/src/utils/utils_module_tests.c +++ b/src/utils/utils_module_tests.c @@ -16,6 +16,7 @@ #include "utils/base64.h" #include "utils/ip_addr.h" #include "utils/eloop.h" +#include "utils/json.h" #include "utils/module_tests.h" @@ -839,6 +840,85 @@ static int eloop_tests(void) } +#ifdef CONFIG_JSON +struct json_test_data { + const char *json; + const char *tree; +}; + +static const struct json_test_data json_test_cases[] = { + { "{}", "[1:OBJECT:]" }, + { "[]", "[1:ARRAY:]" }, + { "{", NULL }, + { "[", NULL }, + { "}", NULL }, + { "]", NULL }, + { "[[]]", "[1:ARRAY:][2:ARRAY:]" }, + { "{\"t\":\"test\"}", "[1:OBJECT:][2:STRING:t]" }, + { "{\"t\":123}", "[1:OBJECT:][2:NUMBER:t]" }, + { "{\"t\":true}", "[1:OBJECT:][2:BOOLEAN:t]" }, + { "{\"t\":false}", "[1:OBJECT:][2:BOOLEAN:t]" }, + { "{\"t\":null}", "[1:OBJECT:][2:NULL:t]" }, + { "{\"t\":truetrue}", NULL }, + { "\"test\"", "[1:STRING:]" }, + { "123", "[1:NUMBER:]" }, + { "true", "[1:BOOLEAN:]" }, + { "false", "[1:BOOLEAN:]" }, + { "null", "[1:NULL:]" }, + { "truetrue", NULL }, + { " {\t\n\r\"a\"\n:\r1\n,\n\"b\":3\n}\n", + "[1:OBJECT:][2:NUMBER:a][2:NUMBER:b]" }, + { ",", NULL }, + { "{,}", NULL }, + { "[,]", NULL }, + { ":", NULL }, + { "{:}", NULL }, + { "[:]", NULL }, + { "{ \"\\u005c\" : \"\\u005c\" }", "[1:OBJECT:][2:STRING:\\]" }, + { "[{},{}]", "[1:ARRAY:][2:OBJECT:][2:OBJECT:]" }, + { "[1,2]", "[1:ARRAY:][2:NUMBER:][2:NUMBER:]" }, + { "[\"1\",\"2\"]", "[1:ARRAY:][2:STRING:][2:STRING:]" }, + { "[true,false]", "[1:ARRAY:][2:BOOLEAN:][2:BOOLEAN:]" }, +}; +#endif /* CONFIG_JSON */ + + +static int json_tests(void) +{ +#ifdef CONFIG_JSON + unsigned int i; + struct json_token *root; + char buf[1000]; + + wpa_printf(MSG_INFO, "JSON tests"); + + for (i = 0; i < ARRAY_SIZE(json_test_cases); i++) { + const struct json_test_data *test = &json_test_cases[i]; + int res = 0; + + root = json_parse(test->json, os_strlen(test->json)); + if ((root && !test->tree) || (!root && test->tree)) { + wpa_printf(MSG_INFO, "JSON test %u failed", i); + res = -1; + } else if (root) { + json_print_tree(root, buf, sizeof(buf)); + if (os_strcmp(buf, test->tree) != 0) { + wpa_printf(MSG_INFO, + "JSON test %u tree mismatch: %s %s", + i, buf, test->tree); + res = -1; + } + } + json_free(root); + if (res < 0) + return -1; + + } +#endif /* CONFIG_JSON */ + return 0; +} + + int utils_module_tests(void) { int ret = 0; @@ -855,6 +935,7 @@ int utils_module_tests(void) wpabuf_tests() < 0 || ip_addr_tests() < 0 || eloop_tests() < 0 || + json_tests() < 0 || int_array_tests() < 0) ret = -1; diff --git a/src/utils/uuid.c b/src/utils/uuid.c index 0f224f976b803..98e43d02f68b5 100644 --- a/src/utils/uuid.c +++ b/src/utils/uuid.c @@ -9,6 +9,7 @@ #include "includes.h" #include "common.h" +#include "crypto/sha256.h" #include "uuid.h" int uuid_str2bin(const char *str, u8 *bin) @@ -69,3 +70,27 @@ int is_nil_uuid(const u8 *uuid) return 0; return 1; } + + +int uuid_random(u8 *uuid) +{ + struct os_time t; + u8 hash[SHA256_MAC_LEN]; + + /* Use HMAC-SHA256 and timestamp as context to avoid exposing direct + * os_get_random() output in the UUID field. */ + os_get_time(&t); + if (os_get_random(uuid, UUID_LEN) < 0 || + hmac_sha256(uuid, UUID_LEN, (const u8 *) &t, sizeof(t), hash) < 0) + return -1; + + os_memcpy(uuid, hash, UUID_LEN); + + /* Version: 4 = random */ + uuid[6] = (4 << 4) | (uuid[6] & 0x0f); + + /* Variant specified in RFC 4122 */ + uuid[8] = 0x80 | (uuid[8] & 0x3f); + + return 0; +} diff --git a/src/utils/uuid.h b/src/utils/uuid.h index 5e860cbc59366..6e20210f99b9c 100644 --- a/src/utils/uuid.h +++ b/src/utils/uuid.h @@ -14,5 +14,6 @@ int uuid_str2bin(const char *str, u8 *bin); int uuid_bin2str(const u8 *bin, char *str, size_t max_len); int is_nil_uuid(const u8 *uuid); +int uuid_random(u8 *uuid); #endif /* UUID_H */ diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c index f7acf6b9f6984..a56462b8bbdc7 100644 --- a/src/utils/wpa_debug.c +++ b/src/utils/wpa_debug.c @@ -13,7 +13,7 @@ #ifdef CONFIG_DEBUG_SYSLOG #include <syslog.h> -static int wpa_debug_syslog = 0; +int wpa_debug_syslog = 0; #endif /* CONFIG_DEBUG_SYSLOG */ #ifdef CONFIG_DEBUG_LINUX_TRACING @@ -58,6 +58,10 @@ static int wpa_to_android_level(int level) #ifndef CONFIG_NO_STDOUT_DEBUG #ifdef CONFIG_DEBUG_FILE +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + static FILE *out_file = NULL; #endif /* CONFIG_DEBUG_FILE */ @@ -539,6 +543,8 @@ int wpa_debug_reopen_file(void) int wpa_debug_open_file(const char *path) { #ifdef CONFIG_DEBUG_FILE + int out_fd; + if (!path) return 0; @@ -548,10 +554,28 @@ int wpa_debug_open_file(const char *path) last_path = os_strdup(path); } - out_file = fopen(path, "a"); + out_fd = open(path, O_CREAT | O_APPEND | O_WRONLY, + S_IRUSR | S_IWUSR | S_IRGRP); + if (out_fd < 0) { + wpa_printf(MSG_ERROR, + "%s: Failed to open output file descriptor, using standard output", + __func__); + return -1; + } + +#ifdef __linux__ + if (fcntl(out_fd, F_SETFD, FD_CLOEXEC) < 0) { + wpa_printf(MSG_DEBUG, + "%s: Failed to set FD_CLOEXEC - continue without: %s", + __func__, strerror(errno)); + } +#endif /* __linux__ */ + + out_file = fdopen(out_fd, "a"); if (out_file == NULL) { wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open " "output file, using standard output"); + close(out_fd); return -1; } #ifndef _WIN32 diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h index 17d8f963802e4..1fe0b7db7482b 100644 --- a/src/utils/wpa_debug.h +++ b/src/utils/wpa_debug.h @@ -14,6 +14,9 @@ extern int wpa_debug_level; extern int wpa_debug_show_keys; extern int wpa_debug_timestamp; +#ifdef CONFIG_DEBUG_SYSLOG +extern int wpa_debug_syslog; +#endif /* CONFIG_DEBUG_SYSLOG */ /* Debugging function - conditional printf and hex dump. Driver wrappers can * use these for debugging purposes. */ diff --git a/src/utils/wpabuf.c b/src/utils/wpabuf.c index 96cb25cc1764b..77ee47288007d 100644 --- a/src/utils/wpabuf.c +++ b/src/utils/wpabuf.c @@ -244,15 +244,13 @@ struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b) if (a) len += wpabuf_len(a); - if (b) - len += wpabuf_len(b); + len += wpabuf_len(b); n = wpabuf_alloc(len); if (n) { if (a) wpabuf_put_buf(n, a); - if (b) - wpabuf_put_buf(n, b); + wpabuf_put_buf(n, b); } wpabuf_free(a); diff --git a/src/utils/xml-utils.c b/src/utils/xml-utils.c index 4916d29765f91..dae91fee46f6c 100644 --- a/src/utils/xml-utils.c +++ b/src/utils/xml-utils.c @@ -246,10 +246,10 @@ static void node_to_tnds(struct xml_node_ctx *ctx, xml_node_t *out, xml_node_create_text(ctx, tnds, NULL, "Path", uri); val = get_val(ctx, node); - if (val) { - xml_node_create_text(ctx, tnds, NULL, "Value", val); - xml_node_get_text_free(ctx, val); - } + if (val || !xml_node_first_child(ctx, node)) + xml_node_create_text(ctx, tnds, NULL, "Value", + val ? val : ""); + xml_node_get_text_free(ctx, val); new_uri = add_path(uri, name); node_to_tnds(ctx, new_uri ? out : tnds, node, new_uri); diff --git a/src/wps/wps.c b/src/wps/wps.c index fade6b6905dc8..8d228270ff10c 100644 --- a/src/wps/wps.c +++ b/src/wps/wps.c @@ -51,12 +51,11 @@ struct wps_data * wps_init(const struct wps_config *cfg) } if (cfg->pin) { data->dev_pw_id = cfg->dev_pw_id; - data->dev_password = os_malloc(cfg->pin_len); + data->dev_password = os_memdup(cfg->pin, cfg->pin_len); if (data->dev_password == NULL) { os_free(data); return NULL; } - os_memcpy(data->dev_password, cfg->pin, cfg->pin_len); data->dev_password_len = cfg->pin_len; wpa_hexdump_key(MSG_DEBUG, "WPS: AP PIN dev_password", data->dev_password, data->dev_password_len); @@ -75,14 +74,12 @@ struct wps_data * wps_init(const struct wps_config *cfg) data->dev_pw_id = cfg->wps->ap_nfc_dev_pw_id; data->dev_password = - os_malloc(wpabuf_len(cfg->wps->ap_nfc_dev_pw)); + os_memdup(wpabuf_head(cfg->wps->ap_nfc_dev_pw), + wpabuf_len(cfg->wps->ap_nfc_dev_pw)); if (data->dev_password == NULL) { os_free(data); return NULL; } - os_memcpy(data->dev_password, - wpabuf_head(cfg->wps->ap_nfc_dev_pw), - wpabuf_len(cfg->wps->ap_nfc_dev_pw)); data->dev_password_len = wpabuf_len(cfg->wps->ap_nfc_dev_pw); wpa_hexdump_key(MSG_DEBUG, "WPS: NFC dev_password", data->dev_password, data->dev_password_len); @@ -124,15 +121,14 @@ struct wps_data * wps_init(const struct wps_config *cfg) if (cfg->new_ap_settings) { data->new_ap_settings = - os_malloc(sizeof(*data->new_ap_settings)); + os_memdup(cfg->new_ap_settings, + sizeof(*data->new_ap_settings)); if (data->new_ap_settings == NULL) { bin_clear_free(data->dev_password, data->dev_password_len); os_free(data); return NULL; } - os_memcpy(data->new_ap_settings, cfg->new_ap_settings, - sizeof(*data->new_ap_settings)); } if (cfg->peer_addr) diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c index 2e3472177de9e..bcae1ba5887b6 100644 --- a/src/wps/wps_common.c +++ b/src/wps/wps_common.c @@ -654,6 +654,7 @@ int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey) pub = wpabuf_zeropad(pub, 192); if (pub == NULL) { wpabuf_free(priv); + dh5_free(dh_ctx); return -1; } wpa_hexdump_buf(MSG_DEBUG, "WPS: Generated new DH pubkey", pub); diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c index b840acd924a74..affd6a4af38a3 100644 --- a/src/wps/wps_er.c +++ b/src/wps/wps_er.c @@ -322,11 +322,10 @@ static int wps_er_ap_use_cached_settings(struct wps_er *er, if (!s) return -1; - ap->ap_settings = os_malloc(sizeof(*ap->ap_settings)); + ap->ap_settings = os_memdup(&s->ap_settings, sizeof(*ap->ap_settings)); if (ap->ap_settings == NULL) return -1; - os_memcpy(ap->ap_settings, &s->ap_settings, sizeof(*ap->ap_settings)); wpa_printf(MSG_DEBUG, "WPS ER: Use cached AP settings"); return 0; } @@ -1958,10 +1957,9 @@ int wps_er_set_config(struct wps_er *er, const u8 *uuid, const u8 *addr, } os_free(ap->ap_settings); - ap->ap_settings = os_malloc(sizeof(*cred)); + ap->ap_settings = os_memdup(cred, sizeof(*cred)); if (ap->ap_settings == NULL) return -1; - os_memcpy(ap->ap_settings, cred, sizeof(*cred)); ap->ap_settings->cred_attr = NULL; wpa_printf(MSG_DEBUG, "WPS ER: Updated local AP settings based set " "config request"); @@ -2018,10 +2016,9 @@ int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *addr, } os_free(ap->ap_settings); - ap->ap_settings = os_malloc(sizeof(*cred)); + ap->ap_settings = os_memdup(cred, sizeof(*cred)); if (ap->ap_settings == NULL) return -1; - os_memcpy(ap->ap_settings, cred, sizeof(*cred)); ap->ap_settings->cred_attr = NULL; if (wps_er_send_get_device_info(ap, wps_er_ap_config_m1) < 0) diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index fac8bd837f2fd..379925e3f0a9e 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -748,12 +748,11 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr, p->wildcard_uuid = 1; else os_memcpy(p->uuid, uuid, WPS_UUID_LEN); - p->pin = os_malloc(pin_len); + p->pin = os_memdup(pin, pin_len); if (p->pin == NULL) { os_free(p); return -1; } - os_memcpy(p->pin, pin, pin_len); p->pin_len = pin_len; if (timeout) { @@ -881,6 +880,7 @@ static const u8 * wps_registrar_get_pin(struct wps_registrar *reg, const u8 *uuid, size_t *pin_len) { struct wps_uuid_pin *pin, *found = NULL; + int wildcard = 0; wps_registrar_expire_pins(reg); @@ -900,7 +900,7 @@ static const u8 * wps_registrar_get_pin(struct wps_registrar *reg, pin->wildcard_uuid == 2) { wpa_printf(MSG_DEBUG, "WPS: Found a wildcard " "PIN. Assigned it for this UUID-E"); - pin->wildcard_uuid++; + wildcard = 1; os_memcpy(pin->uuid, uuid, WPS_UUID_LEN); found = pin; break; @@ -922,6 +922,8 @@ static const u8 * wps_registrar_get_pin(struct wps_registrar *reg, } *pin_len = found->pin_len; found->flags |= PIN_LOCKED; + if (wildcard) + found->wildcard_uuid++; return found->pin; } @@ -1404,10 +1406,9 @@ static int wps_get_dev_password(struct wps_data *wps) return -1; } - wps->dev_password = os_malloc(pin_len); + wps->dev_password = os_memdup(pin, pin_len); if (wps->dev_password == NULL) return -1; - os_memcpy(wps->dev_password, pin, pin_len); wps->dev_password_len = pin_len; return 0; diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index a8d6a7f944e9f..a6809956d86ff 100644 --- a/wpa_supplicant/Android.mk +++ b/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/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog index f28055f4093e0..bf4daaa4cb1e7 100644 --- a/wpa_supplicant/ChangeLog +++ b/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/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index f3e86c1de6c05..c2e93e20b58ac 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -103,6 +103,9 @@ OBJS += eap_register.o OBJS += ../src/utils/common.o OBJS += ../src/utils/wpa_debug.o OBJS += ../src/utils/wpabuf.o +OBJS += ../src/utils/bitfield.o +OBJS += op_classes.o +OBJS += rrm.o OBJS_p = wpa_passphrase.o OBJS_p += ../src/utils/common.o OBJS_p += ../src/utils/wpa_debug.o @@ -254,8 +257,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 CFLAGS += -DCONFIG_MESH @@ -271,6 +272,47 @@ NEED_ECC=y NEED_DH_GROUPS=y endif +ifdef CONFIG_DPP +CFLAGS += -DCONFIG_DPP +OBJS += ../src/common/dpp.o +OBJS += dpp_supplicant.o +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 +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 +CFLAGS += -DCONFIG_FILS +NEED_SHA384=y +NEED_AES_SIV=y +ifdef CONFIG_FILS_SK_PFS +CFLAGS += -DCONFIG_FILS_SK_PFS +NEED_ECC=y +endif +endif + +ifdef CONFIG_MBO +CONFIG_WNM=y +endif + ifdef CONFIG_WNM CFLAGS += -DCONFIG_WNM OBJS += wnm_sta.o @@ -287,15 +329,14 @@ ifdef CONFIG_TDLS_TESTING CFLAGS += -DCONFIG_TDLS_TESTING endif -ifdef CONFIG_PEERKEY -CFLAGS += -DCONFIG_PEERKEY +ifdef CONFIG_PMKSA_CACHE_EXTERNAL +CFLAGS += -DCONFIG_PMKSA_CACHE_EXTERNAL endif ifndef CONFIG_NO_WPA OBJS += ../src/rsn_supp/wpa.o OBJS += ../src/rsn_supp/preauth.o OBJS += ../src/rsn_supp/pmksa_cache.o -OBJS += ../src/rsn_supp/peerkey.o OBJS += ../src/rsn_supp/wpa_ie.o OBJS += ../src/common/wpa_common.o NEED_AES=y @@ -335,7 +376,6 @@ OBJS += ../src/p2p/p2p_invitation.o OBJS += ../src/p2p/p2p_dev_disc.o OBJS += ../src/p2p/p2p_group.o OBJS += ../src/ap/p2p_hostapd.o -OBJS += ../src/utils/bitfield.o CFLAGS += -DCONFIG_P2P NEED_GAS=y NEED_OFFCHANNEL=y @@ -665,9 +705,13 @@ endif ifdef CONFIG_EAP_PWD CFLAGS += -DEAP_PWD +ifeq ($(CONFIG_TLS), wolfssl) +CFLAGS += -DCONFIG_ECC +endif OBJS += ../src/eap_peer/eap_pwd.o ../src/eap_common/eap_pwd_common.o CONFIG_IEEE8021X_EAPOL=y NEED_SHA256=y +NEED_ECC=y endif ifdef CONFIG_EAP_EKE @@ -794,20 +838,9 @@ endif endif endif -ifdef CONFIG_IEEE8021X_EAPOL -# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication) -CFLAGS += -DIEEE8021X_EAPOL -OBJS += ../src/eapol_supp/eapol_supp_sm.o -OBJS += ../src/eap_peer/eap.o ../src/eap_peer/eap_methods.o -NEED_EAP_COMMON=y -ifdef CONFIG_DYNAMIC_EAP_METHODS -CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS -LIBS += -ldl -rdynamic -endif -endif - ifdef CONFIG_MACSEC CFLAGS += -DCONFIG_MACSEC +CONFIG_IEEE8021X_EAPOL=y NEED_AES_ENCBLOCK=y NEED_AES_UNWRAP=y NEED_AES_WRAP=y @@ -819,6 +852,18 @@ OBJS += ../src/pae/ieee802_1x_key.o OBJS += ../src/pae/ieee802_1x_secy_ops.o endif +ifdef CONFIG_IEEE8021X_EAPOL +# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication) +CFLAGS += -DIEEE8021X_EAPOL +OBJS += ../src/eapol_supp/eapol_supp_sm.o +OBJS += ../src/eap_peer/eap.o ../src/eap_peer/eap_methods.o +NEED_EAP_COMMON=y +ifdef CONFIG_DYNAMIC_EAP_METHODS +CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS +LIBS += -ldl -rdynamic +endif +endif + ifdef CONFIG_AP NEED_EAP_COMMON=y NEED_RSN_AUTHENTICATOR=y @@ -852,13 +897,20 @@ OBJS += ../src/ap/ieee802_11_ht.o ifdef CONFIG_IEEE80211AC OBJS += ../src/ap/ieee802_11_vht.o endif +ifdef CONFIG_IEEE80211AX +OBJS += ../src/ap/ieee802_11_he.o endif -ifdef CONFIG_WNM +endif +ifdef CONFIG_WNM_AP +CFLAGS += -DCONFIG_WNM_AP OBJS += ../src/ap/wnm_ap.o endif ifdef CONFIG_MBO OBJS += ../src/ap/mbo_ap.o endif +ifdef CONFIG_FILS +OBJS += ../src/ap/fils_hlp.o +endif ifdef CONFIG_CTRL_IFACE OBJS += ../src/ap/ctrl_iface_ap.o endif @@ -873,11 +925,9 @@ CFLAGS += -DCONFIG_IEEE80211N ifdef CONFIG_IEEE80211AC CFLAGS += -DCONFIG_IEEE80211AC endif +ifdef CONFIG_IEEE80211AX +CFLAGS += -DCONFIG_IEEE80211AX endif - -ifdef CONFIG_MBO -OBJS += mbo.o -CFLAGS += -DCONFIG_MBO endif ifdef NEED_AP_MLME @@ -893,6 +943,10 @@ CFLAGS += -DEAP_SERVER_WSC OBJS += ../src/ap/wps_hostapd.o OBJS += ../src/eap_server/eap_server_wsc.o endif +ifdef CONFIG_DPP +OBJS += ../src/ap/dpp_hostapd.o +OBJS += ../src/ap/gas_query_ap.o +endif ifdef CONFIG_INTERWORKING OBJS += ../src/ap/gas_serv.o endif @@ -901,18 +955,17 @@ OBJS += ../src/ap/hs20.o endif endif +ifdef CONFIG_MBO +OBJS += mbo.o +CFLAGS += -DCONFIG_MBO +endif + ifdef NEED_RSN_AUTHENTICATOR CFLAGS += -DCONFIG_NO_RADIUS NEED_AES_WRAP=y OBJS += ../src/ap/wpa_auth.o OBJS += ../src/ap/wpa_auth_ie.o OBJS += ../src/ap/pmksa_cache_auth.o -ifdef CONFIG_IEEE80211R -OBJS += ../src/ap/wpa_auth_ft.o -endif -ifdef CONFIG_PEERKEY -OBJS += ../src/ap/peerkey_auth.o -endif endif ifdef CONFIG_ACS @@ -996,6 +1049,21 @@ CFLAGS += -DCONFIG_TLSV12 NEED_SHA256=y endif +ifeq ($(CONFIG_TLS), wolfssl) +ifdef TLS_FUNCS +CFLAGS += -DWOLFSSL_DER_LOAD -I/usr/local/include/wolfssl +OBJS += ../src/crypto/tls_wolfssl.o +endif +OBJS += ../src/crypto/crypto_wolfssl.o +OBJS_p += ../src/crypto/crypto_wolfssl.o +ifdef NEED_FIPS186_2_PRF +OBJS += ../src/crypto/fips_prf_wolfssl.o +endif +NEED_TLS_PRF_SHA256=y +LIBS += -lwolfssl -lm +LIBS_p += -lwolfssl -lm +endif + ifeq ($(CONFIG_TLS), openssl) ifdef TLS_FUNCS CFLAGS += -DEAP_TLS_OPENSSL @@ -1017,26 +1085,41 @@ ifdef CONFIG_TLS_ADD_DL LIBS += -ldl LIBS_p += -ldl endif +ifndef CONFIG_TLS_DEFAULT_CIPHERS +CONFIG_TLS_DEFAULT_CIPHERS = "DEFAULT:!EXP:!LOW" +endif +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.o LIBS += -lgnutls -lgpg-error endif -OBJS += ../src/crypto/crypto_gnutls.o -OBJS_p += ../src/crypto/crypto_gnutls.o -OBJS_priv += ../src/crypto/crypto_gnutls.o +OBJS += ../src/crypto/crypto_$(CONFIG_CRYPTO).o +OBJS_p += ../src/crypto/crypto_$(CONFIG_CRYPTO).o +OBJS_priv += ../src/crypto/crypto_$(CONFIG_CRYPTO).o ifdef NEED_FIPS186_2_PRF OBJS += ../src/crypto/fips_prf_internal.o SHA1OBJS += ../src/crypto/sha1-internal.o 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 @@ -1119,6 +1202,48 @@ CONFIG_INTERNAL_RC4=y endif endif +ifeq ($(CONFIG_TLS), linux) +OBJS += ../src/crypto/crypto_linux.o +OBJS_p += ../src/crypto/crypto_linux.o +ifdef TLS_FUNCS +OBJS += ../src/crypto/crypto_internal-rsa.o +OBJS += ../src/crypto/tls_internal.o +OBJS += ../src/tls/tlsv1_common.o +OBJS += ../src/tls/tlsv1_record.o +OBJS += ../src/tls/tlsv1_cred.o +OBJS += ../src/tls/tlsv1_client.o +OBJS += ../src/tls/tlsv1_client_write.o +OBJS += ../src/tls/tlsv1_client_read.o +OBJS += ../src/tls/tlsv1_client_ocsp.o +OBJS += ../src/tls/asn1.o +OBJS += ../src/tls/rsa.o +OBJS += ../src/tls/x509v3.o +OBJS += ../src/tls/pkcs1.o +OBJS += ../src/tls/pkcs5.o +OBJS += ../src/tls/pkcs8.o +NEED_SHA256=y +NEED_BASE64=y +NEED_TLS_PRF=y +ifdef CONFIG_TLSV12 +NEED_TLS_PRF_SHA256=y +endif +NEED_MODEXP=y +NEED_CIPHER=y +CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT +endif +ifdef NEED_MODEXP +OBJS += ../src/crypto/crypto_internal-modexp.o +OBJS += ../src/tls/bignum.o +CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH +CFLAGS += -DLTM_FAST +endif +CONFIG_INTERNAL_DH_GROUP5=y +ifdef NEED_FIPS186_2_PRF +OBJS += ../src/crypto/fips_prf_internal.o +OBJS += ../src/crypto/sha1-internal.o +endif +endif + ifeq ($(CONFIG_TLS), none) ifdef TLS_FUNCS OBJS += ../src/crypto/tls_none.o @@ -1159,8 +1284,10 @@ AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-dec.o endif ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), wolfssl) NEED_INTERNAL_AES_WRAP=y endif +endif ifdef CONFIG_OPENSSL_INTERNAL_AES_WRAP # Seems to be needed at least with BoringSSL NEED_INTERNAL_AES_WRAP=y @@ -1173,11 +1300,19 @@ NEED_INTERNAL_AES_WRAP=y endif ifdef NEED_INTERNAL_AES_WRAP +ifneq ($(CONFIG_TLS), linux) AESOBJS += ../src/crypto/aes-unwrap.o endif +endif ifdef NEED_AES_EAX AESOBJS += ../src/crypto/aes-eax.o NEED_AES_CTR=y +NEED_AES_OMAC1=y +endif +ifdef NEED_AES_SIV +AESOBJS += ../src/crypto/aes-siv.o +NEED_AES_CTR=y +NEED_AES_OMAC1=y endif ifdef NEED_AES_CTR AESOBJS += ../src/crypto/aes-ctr.o @@ -1190,11 +1325,12 @@ NEED_AES_ENC=y ifdef CONFIG_OPENSSL_CMAC CFLAGS += -DCONFIG_OPENSSL_CMAC else +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), wolfssl) AESOBJS += ../src/crypto/aes-omac1.o endif endif -ifdef NEED_AES_SIV -AESOBJS += ../src/crypto/aes-siv.o +endif endif ifdef NEED_AES_WRAP NEED_AES_ENC=y @@ -1205,9 +1341,13 @@ endif ifdef NEED_AES_CBC NEED_AES_ENC=y ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), wolfssl) AESOBJS += ../src/crypto/aes-cbc.o endif endif +endif +endif ifdef NEED_AES_ENC ifdef CONFIG_INTERNAL_AES AESOBJS += ../src/crypto/aes-internal-enc.o @@ -1219,8 +1359,14 @@ endif ifdef NEED_SHA1 ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), gnutls) +ifneq ($(CONFIG_TLS), wolfssl) SHA1OBJS += ../src/crypto/sha1.o endif +endif +endif +endif SHA1OBJS += ../src/crypto/sha1-prf.o ifdef CONFIG_INTERNAL_SHA1 SHA1OBJS += ../src/crypto/sha1-internal.o @@ -1232,9 +1378,11 @@ ifdef CONFIG_NO_WPA_PASSPHRASE CFLAGS += -DCONFIG_NO_PBKDF2 else ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), wolfssl) SHA1OBJS += ../src/crypto/sha1-pbkdf2.o endif endif +endif ifdef NEED_T_PRF SHA1OBJS += ../src/crypto/sha1-tprf.o endif @@ -1245,9 +1393,15 @@ endif ifndef CONFIG_FIPS ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), gnutls) +ifneq ($(CONFIG_TLS), wolfssl) MD5OBJS += ../src/crypto/md5.o endif endif +endif +endif +endif ifdef NEED_MD5 ifdef CONFIG_INTERNAL_MD5 MD5OBJS += ../src/crypto/md5-internal.o @@ -1265,6 +1419,9 @@ endif DESOBJS = # none needed when not internal ifdef NEED_DES +ifndef CONFIG_FIPS +CFLAGS += -DCONFIG_DES +endif ifdef CONFIG_INTERNAL_DES DESOBJS += ../src/crypto/des-internal.o endif @@ -1286,8 +1443,14 @@ SHA256OBJS = # none by default ifdef NEED_SHA256 CFLAGS += -DCONFIG_SHA256 ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), gnutls) +ifneq ($(CONFIG_TLS), wolfssl) SHA256OBJS += ../src/crypto/sha256.o endif +endif +endif +endif SHA256OBJS += ../src/crypto/sha256-prf.o ifdef CONFIG_INTERNAL_SHA256 SHA256OBJS += ../src/crypto/sha256-internal.o @@ -1307,12 +1470,42 @@ ifdef NEED_HMAC_SHA256_KDF CFLAGS += -DCONFIG_HMAC_SHA256_KDF OBJS += ../src/crypto/sha256-kdf.o endif +ifdef NEED_HMAC_SHA384_KDF +CFLAGS += -DCONFIG_HMAC_SHA384_KDF +OBJS += ../src/crypto/sha384-kdf.o +endif +ifdef NEED_HMAC_SHA512_KDF +CFLAGS += -DCONFIG_HMAC_SHA512_KDF +OBJS += ../src/crypto/sha512-kdf.o +endif OBJS += $(SHA256OBJS) endif ifdef NEED_SHA384 +ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), gnutls) +ifneq ($(CONFIG_TLS), wolfssl) +OBJS += ../src/crypto/sha384.o +endif +endif +endif +endif CFLAGS += -DCONFIG_SHA384 OBJS += ../src/crypto/sha384-prf.o endif +ifdef NEED_SHA512 +ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), gnutls) +ifneq ($(CONFIG_TLS), wolfssl) +OBJS += ../src/crypto/sha512.o +endif +endif +endif +endif +CFLAGS += -DCONFIG_SHA512 +OBJS += ../src/crypto/sha512-prf.o +endif ifdef NEED_DH_GROUPS OBJS += ../src/crypto/dh_groups.o @@ -1506,9 +1699,11 @@ endif ifdef CONFIG_FIPS CFLAGS += -DCONFIG_FIPS ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), wolfssl) $(error CONFIG_FIPS=y requires CONFIG_TLS=openssl) endif endif +endif OBJS += $(SHA1OBJS) $(DESOBJS) @@ -1561,6 +1756,12 @@ OBJS += ../src/utils/ext_password.o CFLAGS += -DCONFIG_EXT_PASSWORD endif +ifdef NEED_GAS_SERVER +OBJS += ../src/common/gas_server.o +CFLAGS += -DCONFIG_GAS_SERVER +NEED_GAS=y +endif + ifdef NEED_GAS OBJS += ../src/common/gas.o OBJS += gas_query.o @@ -1573,6 +1774,11 @@ OBJS += offchannel.o CFLAGS += -DCONFIG_OFFCHANNEL endif +ifdef NEED_JSON +OBJS += ../src/utils/json.o +CFLAGS += -DCONFIG_JSON +endif + ifdef CONFIG_MODULE_TESTS CFLAGS += -DCONFIG_MODULE_TESTS OBJS += wpas_module_tests.o @@ -1582,9 +1788,6 @@ OBJS += ../src/crypto/crypto_module_tests.o ifdef CONFIG_WPS OBJS += ../src/wps/wps_module_tests.o endif -ifndef CONFIG_P2P -OBJS += ../src/utils/bitfield.o -endif endif OBJS += ../src/drivers/driver_common.o @@ -1698,7 +1901,7 @@ preauth_test: $(OBJS_t2) @$(E) " LD " $@ wpa_passphrase: $(OBJS_p) - $(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) + $(Q)$(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p) $(LIBS) @$(E) " LD " $@ wpa_cli: $(OBJS_c) diff --git a/wpa_supplicant/README b/wpa_supplicant/README index 11ab01a9c1710..2a3265f21eaa8 100644 --- a/wpa_supplicant/README +++ b/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/wpa_supplicant/README-HS20 b/wpa_supplicant/README-HS20 index e4eed2074f915..334287101c927 100644 --- a/wpa_supplicant/README-HS20 +++ b/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/wpa_supplicant/android.config b/wpa_supplicant/android.config index 02505bb991aa4..c97f591311d3c 100644 --- a/wpa_supplicant/android.config +++ b/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/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index 5afb772ba192e..ea846a0fad4b8 100644 --- a/wpa_supplicant/ap.c +++ b/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/wpa_supplicant/ap.h b/wpa_supplicant/ap.h index 5a59ddcc1c93e..447b551863a3d 100644 --- a/wpa_supplicant/ap.h +++ b/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/wpa_supplicant/autoscan.c b/wpa_supplicant/autoscan.c index 072a1d5414aea..5056a9300a875 100644 --- a/wpa_supplicant/autoscan.c +++ b/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/wpa_supplicant/bgscan.c b/wpa_supplicant/bgscan.c index 798b43c3fdf76..1ea640114c8e3 100644 --- a/wpa_supplicant/bgscan.c +++ b/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/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c index a320cc43068c9..cb732f709b9ea 100644 --- a/wpa_supplicant/bgscan_learn.c +++ b/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/wpa_supplicant/bgscan_simple.c b/wpa_supplicant/bgscan_simple.c index a467cc5b92719..41a26df0d6355 100644 --- a/wpa_supplicant/bgscan_simple.c +++ b/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/wpa_supplicant/bss.c b/wpa_supplicant/bss.c index 3a8778db9058d..3a41db98e5ba4 100644 --- a/wpa_supplicant/bss.c +++ b/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/wpa_supplicant/bss.h b/wpa_supplicant/bss.h index 84e8fb07461e4..5251b2c354e31 100644 --- a/wpa_supplicant/bss.h +++ b/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/wpa_supplicant/config.c b/wpa_supplicant/config.c index dd922caf80af9..c43960697dc39 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / Configuration parser and common functions - * Copyright (c) 2003-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/wpa_supplicant/config.h b/wpa_supplicant/config.h index 48e64be5da1a1..cd7571f593291 100644 --- a/wpa_supplicant/config.h +++ b/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/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index 7ae16545bebca..09115e19dc2d4 100644 --- a/wpa_supplicant/config_file.c +++ b/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/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index 010b594af85e8..d2a52d760089e 100644 --- a/wpa_supplicant/config_ssid.h +++ b/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/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c index 82ba3b015dc9a..0ce1830b4ef2b 100644 --- a/wpa_supplicant/config_winreg.c +++ b/wpa_supplicant/config_winreg.c @@ -99,13 +99,12 @@ static int wpa_config_read_blobs(struct wpa_config *config, HKEY hk) break; } blob->name = os_strdup((char *) name); - blob->data = os_malloc(datalen); + blob->data = os_memdup(data, datalen); if (blob->name == NULL || blob->data == NULL) { wpa_config_free_blob(blob); errors++; break; } - os_memcpy(blob->data, data, datalen); blob->len = datalen; wpa_config_set_blob(config, blob); @@ -234,6 +233,7 @@ static int wpa_config_read_global(struct wpa_config *config, HKEY hk) #ifdef CONFIG_WPS if (wpa_config_read_global_uuid(config, hk)) errors++; + wpa_config_read_reg_dword(hk, TEXT("auto_uuid"), &config->auto_uuid); config->device_name = wpa_config_read_reg_string( hk, TEXT("device_name")); config->manufacturer = wpa_config_read_reg_string( @@ -580,6 +580,8 @@ static int wpa_config_write_global(struct wpa_config *config, HKEY hk) uuid_bin2str(config->uuid, buf, sizeof(buf)); wpa_config_write_reg_string(hk, "uuid", buf); } + wpa_config_write_reg_dword(hk, TEXT("auto_uuid"), config->auto_uuid, + 0); wpa_config_write_reg_string(hk, "device_name", config->device_name); wpa_config_write_reg_string(hk, "manufacturer", config->manufacturer); wpa_config_write_reg_string(hk, "model_name", config->model_name); @@ -617,6 +619,8 @@ static int wpa_config_write_global(struct wpa_config *config, HKEY hk) config->filter_ssids, 0); wpa_config_write_reg_dword(hk, TEXT("max_num_sta"), config->max_num_sta, DEFAULT_MAX_NUM_STA); + wpa_config_write_reg_dword(hk, TEXT("ap_isolate"), + config->ap_isolate, DEFAULT_AP_ISOLATE); wpa_config_write_reg_dword(hk, TEXT("disassoc_low_ack"), config->disassoc_low_ack, 0); @@ -868,6 +872,8 @@ static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id) INT(scan_ssid); write_bssid(netw, ssid); write_psk(netw, ssid); + STR(sae_password); + STR(sae_password_id); write_proto(netw, ssid); write_key_mgmt(netw, ssid); write_pairwise(netw, ssid); @@ -877,6 +883,7 @@ static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id) write_eap(netw, ssid); STR(identity); STR(anonymous_identity); + STR(imsi_identity); STR(password); STR(ca_cert); STR(ca_path); @@ -924,7 +931,6 @@ static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id) write_int(netw, "proactive_key_caching", ssid->proactive_key_caching, -1); INT(disabled); - INT(peerkey); #ifdef CONFIG_IEEE80211W write_int(netw, "ieee80211w", ssid->ieee80211w, MGMT_FRAME_PROTECTION_DEFAULT); diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index d814fdf7fd2d4..77a3133d8d560 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/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/wpa_supplicant/ctrl_iface_named_pipe.c b/wpa_supplicant/ctrl_iface_named_pipe.c index 54e0e2fac5838..9c0a47e63936b 100644 --- a/wpa_supplicant/ctrl_iface_named_pipe.c +++ b/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/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c index 0dc0937ff0aa6..8a6057a82bfeb 100644 --- a/wpa_supplicant/ctrl_iface_udp.c +++ b/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/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c index 4db712fff7bbb..b88c80a99551f 100644 --- a/wpa_supplicant/ctrl_iface_unix.c +++ b/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/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c index 27b3012aede8e..d4deb0fe35f01 100644 --- a/wpa_supplicant/dbus/dbus_new.c +++ b/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/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h index d64fceef718c9..40ae133b225e5 100644 --- a/wpa_supplicant/dbus/dbus_new.h +++ b/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/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c index e11dd36ca23c7..94773b3291330 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.c +++ b/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/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h index 1d6235d6f3e4c..6f952cc390919 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers.h +++ b/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/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c index 73b9e20c20b04..9305b9a4f37d6 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c +++ b/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/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/wpa_supplicant/dbus/dbus_new_handlers_wps.c index f16e2290c7ed4..f762b3f2ef5ca 100644 --- a/wpa_supplicant/dbus/dbus_new_handlers_wps.c +++ b/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/wpa_supplicant/defconfig b/wpa_supplicant/defconfig index 1d05198f849af..af281e56d2e13 100644 --- a/wpa_supplicant/defconfig +++ b/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/wpa_supplicant/doc/docbook/eapol_test.8 b/wpa_supplicant/doc/docbook/eapol_test.8 index af232826cb471..1ae1ac1c93ea6 100644 --- a/wpa_supplicant/doc/docbook/eapol_test.8 +++ b/wpa_supplicant/doc/docbook/eapol_test.8 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "EAPOL_TEST" "8" "02 October 2016" "" "" +.TH "EAPOL_TEST" "8" "02 December 2018" "" "" .SH NAME eapol_test \- EAP peer and RADIUS client testing @@ -115,7 +115,7 @@ Save configuration after authentication. \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2016, +wpa_supplicant is copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/eapol_test.sgml b/wpa_supplicant/doc/docbook/eapol_test.sgml index 3f224133a3016..ae6bafecfa158 100644 --- a/wpa_supplicant/doc/docbook/eapol_test.sgml +++ b/wpa_supplicant/doc/docbook/eapol_test.sgml @@ -194,7 +194,7 @@ eapol_test -ctest.conf -a127.0.0.1 -p1812 -ssecret -r1 </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2016, + <para>wpa_supplicant is copyright (c) 2003-2018, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/doc/docbook/wpa_background.8 b/wpa_supplicant/doc/docbook/wpa_background.8 index 9283266d405e1..e2b894b6ae243 100644 --- a/wpa_supplicant/doc/docbook/wpa_background.8 +++ b/wpa_supplicant/doc/docbook/wpa_background.8 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_BACKGROUND" "8" "02 October 2016" "" "" +.TH "WPA_BACKGROUND" "8" "02 December 2018" "" "" .SH NAME wpa_background \- Background information on Wi-Fi Protected Access and IEEE 802.11i @@ -75,7 +75,7 @@ pre-authentication, and PMKSA caching). \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2016, +wpa_supplicant is copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_background.sgml b/wpa_supplicant/doc/docbook/wpa_background.sgml index 13c9f4514ff85..d3e4dbe2a5a86 100644 --- a/wpa_supplicant/doc/docbook/wpa_background.sgml +++ b/wpa_supplicant/doc/docbook/wpa_background.sgml @@ -90,7 +90,7 @@ <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2016, + <para>wpa_supplicant is copyright (c) 2003-2018, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/doc/docbook/wpa_cli.8 b/wpa_supplicant/doc/docbook/wpa_cli.8 index 440bfc5bdfccb..ec96a4dfa93e8 100644 --- a/wpa_supplicant/doc/docbook/wpa_cli.8 +++ b/wpa_supplicant/doc/docbook/wpa_cli.8 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_CLI" "8" "02 October 2016" "" "" +.TH "WPA_CLI" "8" "02 December 2018" "" "" .SH NAME wpa_cli \- WPA command line client @@ -210,7 +210,7 @@ exit wpa_cli \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2016, +wpa_supplicant is copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_cli.sgml b/wpa_supplicant/doc/docbook/wpa_cli.sgml index 15400f04730a9..766dd2cc1370c 100644 --- a/wpa_supplicant/doc/docbook/wpa_cli.sgml +++ b/wpa_supplicant/doc/docbook/wpa_cli.sgml @@ -345,7 +345,7 @@ CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2016, + <para>wpa_supplicant is copyright (c) 2003-2018, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/doc/docbook/wpa_gui.8 b/wpa_supplicant/doc/docbook/wpa_gui.8 index 73bf362a8d46a..2d7e74e48e61b 100644 --- a/wpa_supplicant/doc/docbook/wpa_gui.8 +++ b/wpa_supplicant/doc/docbook/wpa_gui.8 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_GUI" "8" "02 October 2016" "" "" +.TH "WPA_GUI" "8" "02 December 2018" "" "" .SH NAME wpa_gui \- WPA Graphical User Interface @@ -51,7 +51,7 @@ icon pop-up messages. \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2016, +wpa_supplicant is copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_gui.sgml b/wpa_supplicant/doc/docbook/wpa_gui.sgml index 352d3d28cf5b7..91662d54a1fcc 100644 --- a/wpa_supplicant/doc/docbook/wpa_gui.sgml +++ b/wpa_supplicant/doc/docbook/wpa_gui.sgml @@ -91,7 +91,7 @@ </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2016, + <para>wpa_supplicant is copyright (c) 2003-2018, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/doc/docbook/wpa_passphrase.8 b/wpa_supplicant/doc/docbook/wpa_passphrase.8 index ed0347af2c47a..20e7155cf7256 100644 --- a/wpa_supplicant/doc/docbook/wpa_passphrase.8 +++ b/wpa_supplicant/doc/docbook/wpa_passphrase.8 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_PASSPHRASE" "8" "02 October 2016" "" "" +.TH "WPA_PASSPHRASE" "8" "02 December 2018" "" "" .SH NAME wpa_passphrase \- Generate a WPA PSK from an ASCII passphrase for a SSID @@ -31,7 +31,7 @@ passphrase will be read from standard input. \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2016, +wpa_supplicant is copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml index faf1f2799ea6a..2f86b0bdf9873 100644 --- a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml +++ b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml @@ -18,7 +18,7 @@ </refsynopsisdiv> <refsect1> - <title>Overview</title> + <title>Overview</title> <para><command>wpa_passphrase</command> pre-computes PSK entries for network configuration blocks of a @@ -62,7 +62,7 @@ </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2016, + <para>wpa_supplicant is copyright (c) 2003-2018, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/doc/docbook/wpa_priv.8 b/wpa_supplicant/doc/docbook/wpa_priv.8 index 9a9fe82bbaeff..4064d1ba91641 100644 --- a/wpa_supplicant/doc/docbook/wpa_priv.8 +++ b/wpa_supplicant/doc/docbook/wpa_priv.8 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_PRIV" "8" "02 October 2016" "" "" +.TH "WPA_PRIV" "8" "02 December 2018" "" "" .SH NAME wpa_priv \- wpa_supplicant privilege separation helper @@ -111,7 +111,7 @@ processes at the same time, if desired. \fBwpa_supplicant\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2016, +wpa_supplicant is copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_priv.sgml b/wpa_supplicant/doc/docbook/wpa_priv.sgml index 403c9b2d2f847..4a5f319db3b2a 100644 --- a/wpa_supplicant/doc/docbook/wpa_priv.sgml +++ b/wpa_supplicant/doc/docbook/wpa_priv.sgml @@ -137,7 +137,7 @@ wpa_supplicant -i ath0 -c wpa_supplicant.conf </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2016, + <para>wpa_supplicant is copyright (c) 2003-2018, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.8 b/wpa_supplicant/doc/docbook/wpa_supplicant.8 index 95ff5ff8c2a1c..16a94058be90e 100644 --- a/wpa_supplicant/doc/docbook/wpa_supplicant.8 +++ b/wpa_supplicant/doc/docbook/wpa_supplicant.8 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_SUPPLICANT" "8" "02 October 2016" "" "" +.TH "WPA_SUPPLICANT" "8" "02 December 2018" "" "" .SH NAME wpa_supplicant \- Wi-Fi Protected Access client and IEEE 802.1X supplicant @@ -532,7 +532,7 @@ in. \fBwpa_passphrase\fR(8) .SH "LEGAL" .PP -wpa_supplicant is copyright (c) 2003-2016, +wpa_supplicant is copyright (c) 2003-2018, Jouni Malinen <j@w1.fi> and contributors. All Rights Reserved. diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 b/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 index f4c997a1077b8..07461130085e9 100644 --- a/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 +++ b/wpa_supplicant/doc/docbook/wpa_supplicant.conf.5 @@ -3,7 +3,7 @@ .\" <http://shell.ipoline.com/~elmert/comp/docbook2X/> .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng <steve@ggi-project.org>. -.TH "WPA_SUPPLICANT.CONF" "5" "02 October 2016" "" "" +.TH "WPA_SUPPLICANT.CONF" "5" "02 December 2018" "" "" .SH NAME wpa_supplicant.conf \- configuration file for wpa_supplicant diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml index 11e0e90d76527..eeb9c07305b92 100644 --- a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml +++ b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml @@ -565,7 +565,7 @@ wpa_supplicant \ using ap_scan=0 option in configuration file.</para> </listitem> </varlistentry> - + <varlistentry> <term>Wired Ethernet drivers</term> <listitem> @@ -590,7 +590,7 @@ wpa_supplicant \ </varlistentry> </variablelist> - + <para>wpa_supplicant was designed to be portable for different drivers and operating systems. Hopefully, support for more wlan cards and OSes will be added in the future. See developer.txt for @@ -729,7 +729,7 @@ fi </refsect1> <refsect1> <title>Legal</title> - <para>wpa_supplicant is copyright (c) 2003-2016, + <para>wpa_supplicant is copyright (c) 2003-2018, Jouni Malinen <email>j@w1.fi</email> and contributors. All Rights Reserved.</para> diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c new file mode 100644 index 0000000000000..7bc46610a9718 --- /dev/null +++ b/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/wpa_supplicant/dpp_supplicant.h b/wpa_supplicant/dpp_supplicant.h new file mode 100644 index 0000000000000..5a4f06e2e97ed --- /dev/null +++ b/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/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h index 220b7ba3ddca6..078de23f794fd 100644 --- a/wpa_supplicant/driver_i.h +++ b/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/wpa_supplicant/events.c b/wpa_supplicant/events.c index abe3b476773d4..37d429d33022d 100644 --- a/wpa_supplicant/events.c +++ b/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/wpa_supplicant/examples/dpp-qrcode.py b/wpa_supplicant/examples/dpp-qrcode.py new file mode 100755 index 0000000000000..e2a00c9108126 --- /dev/null +++ b/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/wpa_supplicant/examples/wps-ap-cli b/wpa_supplicant/examples/wps-ap-cli index cc2cff2ebc24f..15d913ef1fae6 100755 --- a/wpa_supplicant/examples/wps-ap-cli +++ b/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/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c index 691de0345d136..f4f60c58bee53 100644 --- a/wpa_supplicant/gas_query.c +++ b/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/wpa_supplicant/gas_query.h b/wpa_supplicant/gas_query.h index ef82097e2424b..982c0f7ce60e3 100644 --- a/wpa_supplicant/gas_query.h +++ b/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/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c index e88f147bbd1b4..f4187900ed42d 100644 --- a/wpa_supplicant/hs20_supplicant.c +++ b/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/wpa_supplicant/hs20_supplicant.h b/wpa_supplicant/hs20_supplicant.h index 0dd559fdbf016..66fc540be3e4f 100644 --- a/wpa_supplicant/hs20_supplicant.h +++ b/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/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c index 53d7d57bde350..00919d14a55e9 100644 --- a/wpa_supplicant/ibss_rsn.c +++ b/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/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c index 1fb40c74e5cf9..60c8be9a6c6a5 100644 --- a/wpa_supplicant/interworking.c +++ b/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/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h index 3d22292618b22..37ee2e904e48e 100644 --- a/wpa_supplicant/interworking.h +++ b/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/wpa_supplicant/mbo.c b/wpa_supplicant/mbo.c index 7e049be3df413..5adf61e58bd0b 100644 --- a/wpa_supplicant/mbo.c +++ b/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/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c index d67d3b2aa390e..38b9fb320ca9f 100644 --- a/wpa_supplicant/mesh.c +++ b/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/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c index d14c7e3b20458..eafb0af7b82a9 100644 --- a/wpa_supplicant/mesh_mpm.c +++ b/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/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c index 27ab8cb364586..e74cb16b07251 100644 --- a/wpa_supplicant/mesh_rsn.c +++ b/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/wpa_supplicant/notify.c b/wpa_supplicant/notify.c index 67e36ae34cb87..83df04f394c74 100644 --- a/wpa_supplicant/notify.c +++ b/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/wpa_supplicant/notify.h b/wpa_supplicant/notify.h index 8cce0f30c2a96..3ca933c7621a5 100644 --- a/wpa_supplicant/notify.h +++ b/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/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c index 26d41a4ad5c63..b74be7dad4acf 100644 --- a/wpa_supplicant/offchannel.c +++ b/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/wpa_supplicant/op_classes.c b/wpa_supplicant/op_classes.c new file mode 100644 index 0000000000000..d23b0094c440d --- /dev/null +++ b/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/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index b1fdc2837ff06..c596d5ab61484 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/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/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c index f4bba98e2a825..f2fff550aa81e 100644 --- a/wpa_supplicant/preauth_test.c +++ b/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/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c new file mode 100644 index 0000000000000..f4fbfa7193524 --- /dev/null +++ b/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/wpa_supplicant/scan.c b/wpa_supplicant/scan.c index fb8ebdf2ecc1c..ee39e0c9228d1 100644 --- a/wpa_supplicant/scan.c +++ b/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/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 61fd3b24549cb..39c80696a94c3 100644 --- a/wpa_supplicant/sme.c +++ b/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/wpa_supplicant/sme.h b/wpa_supplicant/sme.h index fd5c3b4e1ed83..f3c8220255745 100644 --- a/wpa_supplicant/sme.h +++ b/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/wpa_supplicant/wifi_display.c b/wpa_supplicant/wifi_display.c index c363b21b92b16..c94e4610893a5 100644 --- a/wpa_supplicant/wifi_display.c +++ b/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/wpa_supplicant/wmm_ac.c b/wpa_supplicant/wmm_ac.c index 5625d36638b5c..a88cc46f39560 100644 --- a/wpa_supplicant/wmm_ac.c +++ b/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/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c index 67a07ff7b1e7c..6b68fc9e3772a 100644 --- a/wpa_supplicant/wnm_sta.c +++ b/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/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h index 81d815359634e..29625f8ca943e 100644 --- a/wpa_supplicant/wnm_sta.h +++ b/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/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index a848b7737db51..779355440a01d 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - command line interface for wpa_supplicant daemon - * Copyright (c) 2004-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/wpa_supplicant/wpa_passphrase.c b/wpa_supplicant/wpa_passphrase.c index 9b568f0f7c672..adca1cce13ee0 100644 --- a/wpa_supplicant/wpa_passphrase.c +++ b/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/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c index 511df4f18148e..b3ad45eca516c 100644 --- a/wpa_supplicant/wpa_priv.c +++ b/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/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 7361ee96d1df8..e587d7e3cd69b 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/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/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf index b3138e3017985..4f5916025aab3 100644 --- a/wpa_supplicant/wpa_supplicant.conf +++ b/wpa_supplicant/wpa_supplicant.conf @@ -98,9 +98,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, @@ -185,13 +183,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 @@ -220,9 +218,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 @@ -424,11 +428,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 @@ -554,6 +597,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 @@ -681,7 +738,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 @@ -689,6 +746,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 @@ -801,6 +865,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 @@ -813,15 +878,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 @@ -855,6 +928,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 @@ -872,22 +953,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. @@ -903,18 +1016,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 @@ -1113,12 +1220,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. @@ -1187,6 +1299,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 # @@ -1216,6 +1332,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 @@ -1674,15 +1795,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/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index ef9273d09a325..8b749f44e235c 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/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/wpa_supplicant/wpa_supplicant_template.conf b/wpa_supplicant/wpa_supplicant_template.conf index f3f2a6417d2e7..f55227f826857 100644 --- a/wpa_supplicant/wpa_supplicant_template.conf +++ b/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/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index f84c8b90ac2fc..4634ed7fc368c 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/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/wpa_supplicant/wpas_kay.c b/wpa_supplicant/wpas_kay.c index d6ec8c5090e9a..d3d06b8ae231f 100644 --- a/wpa_supplicant/wpas_kay.c +++ b/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/wpa_supplicant/wpas_kay.h b/wpa_supplicant/wpas_kay.h index b7236d0776c44..81f8e0ce329ea 100644 --- a/wpa_supplicant/wpas_kay.h +++ b/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/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index 74a420c671d0c..1a2677b8eea42 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/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"; |