diff --git a/contrib/wpa/CONTRIBUTIONS b/contrib/wpa/CONTRIBUTIONS
index c81ad640995..1b4caf7ac81 100644
--- a/contrib/wpa/CONTRIBUTIONS
+++ b/contrib/wpa/CONTRIBUTIONS
@@ -56,6 +56,9 @@ In general, the best way of generating a suitable formatted patch file
is by committing the changes to a cloned git repository and using git
format-patch. The patch can then be sent, e.g., with git send-email.
+A list of pending patches waiting for review is available in
+Patchwork: https://patchwork.ozlabs.org/project/hostap/list/
+
History of license and contributions terms
------------------------------------------
@@ -140,7 +143,7 @@ The license terms used for hostap.git files
Modified BSD license (no advertisement clause):
-Copyright (c) 2002-2019, Jouni Malinen and contributors
+Copyright (c) 2002-2021, Jouni Malinen and contributors
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/contrib/wpa/hostapd/Android.mk b/contrib/wpa/hostapd/Android.mk
new file mode 100644
index 00000000000..dd8aa2450d7
--- /dev/null
+++ b/contrib/wpa/hostapd/Android.mk
@@ -0,0 +1,1152 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+WPA_BUILD_HOSTAPD := false
+ifneq ($(BOARD_HOSTAPD_DRIVER),)
+ WPA_BUILD_HOSTAPD := true
+ CONFIG_DRIVER_$(BOARD_HOSTAPD_DRIVER) := y
+endif
+
+ifeq ($(WPA_BUILD_HOSTAPD),true)
+
+include $(LOCAL_PATH)/android.config
+
+# To ignore possible wrong network configurations
+L_CFLAGS = -DWPA_IGNORE_CONFIG_ERRORS
+
+L_CFLAGS += -DVERSION_STR_POSTFIX=\"-$(PLATFORM_VERSION)\"
+
+# Set Android log name
+L_CFLAGS += -DANDROID_LOG_NAME=\"hostapd\"
+
+# Disable unused parameter warnings
+L_CFLAGS += -Wno-unused-parameter
+
+# Set Android extended P2P functionality
+L_CFLAGS += -DANDROID_P2P
+
+ifeq ($(BOARD_HOSTAPD_PRIVATE_LIB),)
+L_CFLAGS += -DANDROID_LIB_STUB
+endif
+
+ifneq ($(BOARD_HOSTAPD_PRIVATE_LIB_EVENT),)
+L_CFLAGS += -DANDROID_LIB_EVENT
+endif
+
+# Use Android specific directory for control interface sockets
+L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
+L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/system/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
+endif
+
+INCLUDES = $(LOCAL_PATH)
+INCLUDES += $(LOCAL_PATH)/src
+INCLUDES += $(LOCAL_PATH)/src/utils
+INCLUDES += system/security/keystore/include
+ifdef CONFIG_DRIVER_NL80211
+ifneq ($(wildcard external/libnl),)
+INCLUDES += external/libnl/include
+else
+INCLUDES += external/libnl-headers
+endif
+endif
+
+
+ifndef CONFIG_OS
+ifdef CONFIG_NATIVE_WINDOWS
+CONFIG_OS=win32
+else
+CONFIG_OS=unix
+endif
+endif
+
+ifeq ($(CONFIG_OS), internal)
+L_CFLAGS += -DOS_NO_C_LIB_DEFINES
+endif
+
+ifdef CONFIG_NATIVE_WINDOWS
+L_CFLAGS += -DCONFIG_NATIVE_WINDOWS
+LIBS += -lws2_32
+endif
+
+OBJS = main.c
+OBJS += config_file.c
+
+OBJS += src/ap/hostapd.c
+OBJS += src/ap/wpa_auth_glue.c
+OBJS += src/ap/drv_callbacks.c
+OBJS += src/ap/ap_drv_ops.c
+OBJS += src/ap/utils.c
+OBJS += src/ap/authsrv.c
+OBJS += src/ap/ieee802_1x.c
+OBJS += src/ap/ap_config.c
+OBJS += src/ap/eap_user_db.c
+OBJS += src/ap/ieee802_11_auth.c
+OBJS += src/ap/sta_info.c
+OBJS += src/ap/wpa_auth.c
+OBJS += src/ap/tkip_countermeasures.c
+OBJS += src/ap/ap_mlme.c
+OBJS += src/ap/wpa_auth_ie.c
+OBJS += src/ap/preauth_auth.c
+OBJS += src/ap/pmksa_cache_auth.c
+OBJS += src/ap/ieee802_11_shared.c
+OBJS += src/ap/beacon.c
+OBJS += src/ap/bss_load.c
+OBJS += src/ap/neighbor_db.c
+OBJS += src/ap/rrm.c
+OBJS_d =
+OBJS_p =
+LIBS =
+LIBS_c =
+HOBJS =
+LIBS_h =
+
+NEED_RC4=y
+NEED_AES=y
+NEED_MD5=y
+NEED_SHA1=y
+
+OBJS += src/drivers/drivers.c
+L_CFLAGS += -DHOSTAPD
+
+ifdef CONFIG_WPA_TRACE
+L_CFLAGS += -DWPA_TRACE
+OBJS += src/utils/trace.c
+HOBJS += src/utils/trace.c
+LDFLAGS += -rdynamic
+L_CFLAGS += -funwind-tables
+ifdef CONFIG_WPA_TRACE_BFD
+L_CFLAGS += -DWPA_TRACE_BFD
+LIBS += -lbfd
+LIBS_c += -lbfd
+LIBS_h += -lbfd
+endif
+endif
+
+OBJS += src/utils/eloop.c
+
+ifdef CONFIG_ELOOP_POLL
+L_CFLAGS += -DCONFIG_ELOOP_POLL
+endif
+
+ifdef CONFIG_ELOOP_EPOLL
+L_CFLAGS += -DCONFIG_ELOOP_EPOLL
+endif
+
+OBJS += src/utils/common.c
+OBJS += src/utils/wpa_debug.c
+OBJS += src/utils/wpabuf.c
+OBJS += src/utils/os_$(CONFIG_OS).c
+OBJS += src/utils/ip_addr.c
+OBJS += src/utils/crc32.c
+
+OBJS += src/common/ieee802_11_common.c
+OBJS += src/common/wpa_common.c
+OBJS += src/common/hw_features_common.c
+
+OBJS += src/eapol_auth/eapol_auth_sm.c
+
+
+ifndef CONFIG_NO_DUMP_STATE
+# define HOSTAPD_DUMP_STATE to include support for dumping internal state
+# through control interface commands (undefine it, if you want to save in
+# binary size)
+L_CFLAGS += -DHOSTAPD_DUMP_STATE
+OBJS += src/eapol_auth/eapol_auth_dump.c
+endif
+
+ifdef CONFIG_NO_RADIUS
+L_CFLAGS += -DCONFIG_NO_RADIUS
+CONFIG_NO_ACCOUNTING=y
+else
+OBJS += src/radius/radius.c
+OBJS += src/radius/radius_client.c
+OBJS += src/radius/radius_das.c
+endif
+
+ifdef CONFIG_NO_ACCOUNTING
+L_CFLAGS += -DCONFIG_NO_ACCOUNTING
+else
+OBJS += src/ap/accounting.c
+endif
+
+ifdef CONFIG_NO_VLAN
+L_CFLAGS += -DCONFIG_NO_VLAN
+else
+OBJS += src/ap/vlan_init.c
+OBJS += src/ap/vlan_ifconfig.c
+OBJS += src/ap/vlan.c
+ifdef CONFIG_FULL_DYNAMIC_VLAN
+# Define CONFIG_FULL_DYNAMIC_VLAN to have hostapd manipulate bridges
+# and VLAN interfaces for the VLAN feature.
+L_CFLAGS += -DCONFIG_FULL_DYNAMIC_VLAN
+OBJS += src/ap/vlan_full.c
+ifdef CONFIG_VLAN_NETLINK
+OBJS += src/ap/vlan_util.c
+else
+OBJS += src/ap/vlan_ioctl.c
+endif
+endif
+endif
+
+ifdef CONFIG_NO_CTRL_IFACE
+L_CFLAGS += -DCONFIG_NO_CTRL_IFACE
+else
+OBJS += src/common/ctrl_iface_common.c
+OBJS += ctrl_iface.c
+OBJS += src/ap/ctrl_iface_ap.c
+endif
+
+L_CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX
+
+ifdef CONFIG_RSN_PREAUTH
+L_CFLAGS += -DCONFIG_RSN_PREAUTH
+CONFIG_L2_PACKET=y
+endif
+
+ifdef CONFIG_HS20
+CONFIG_PROXYARP=y
+endif
+
+ifdef CONFIG_PROXYARP
+CONFIG_L2_PACKET=y
+endif
+
+ifdef CONFIG_SUITEB
+L_CFLAGS += -DCONFIG_SUITEB
+endif
+
+ifdef CONFIG_SUITEB192
+L_CFLAGS += -DCONFIG_SUITEB192
+NEED_SHA384=y
+endif
+
+ifdef CONFIG_OCV
+L_CFLAGS += -DCONFIG_OCV
+OBJS += src/common/ocv.c
+endif
+
+ifdef CONFIG_IEEE80211R
+L_CFLAGS += -DCONFIG_IEEE80211R -DCONFIG_IEEE80211R_AP
+OBJS += src/ap/wpa_auth_ft.c
+NEED_AES_UNWRAP=y
+NEED_AES_SIV=y
+NEED_ETH_P_OUI=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
+L_CFLAGS += -DCONFIG_SAE
+OBJS += src/common/sae.c
+ifdef CONFIG_SAE_PK
+L_CFLAGS += -DCONFIG_SAE_PK
+OBJS += src/common/sae_pk.c
+endif
+NEED_ECC=y
+NEED_DH_GROUPS=y
+NEED_HMAC_SHA256_KDF=y
+NEED_DRAGONFLY=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_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 -DCONFIG_WNM_AP
+OBJS += src/ap/wnm_ap.c
+endif
+
+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
+endif
+
+ifdef CONFIG_FST
+L_CFLAGS += -DCONFIG_FST
+OBJS += src/fst/fst.c
+OBJS += src/fst/fst_group.c
+OBJS += src/fst/fst_iface.c
+OBJS += src/fst/fst_session.c
+OBJS += src/fst/fst_ctrl_aux.c
+ifdef CONFIG_FST_TEST
+L_CFLAGS += -DCONFIG_FST_TEST
+endif
+ifndef CONFIG_NO_CTRL_IFACE
+OBJS += src/fst/fst_ctrl_iface.c
+endif
+endif
+
+ifdef CONFIG_WEP
+L_CFLAGS += -DCONFIG_WEP
+endif
+
+ifdef CONFIG_NO_TKIP
+L_CFLAGS += -DCONFIG_NO_TKIP
+endif
+
+
+include $(LOCAL_PATH)/src/drivers/drivers.mk
+
+OBJS += $(DRV_AP_OBJS)
+L_CFLAGS += $(DRV_AP_CFLAGS)
+LDFLAGS += $(DRV_AP_LDFLAGS)
+LIBS += $(DRV_AP_LIBS)
+
+ifdef CONFIG_L2_PACKET
+ifdef CONFIG_DNET_PCAP
+ifdef CONFIG_L2_FREEBSD
+LIBS += -lpcap
+OBJS += src/l2_packet/l2_packet_freebsd.c
+else
+LIBS += -ldnet -lpcap
+OBJS += src/l2_packet/l2_packet_pcap.c
+endif
+else
+OBJS += src/l2_packet/l2_packet_linux.c
+endif
+else
+OBJS += src/l2_packet/l2_packet_none.c
+endif
+
+
+ifdef CONFIG_EAP_MD5
+L_CFLAGS += -DEAP_SERVER_MD5
+OBJS += src/eap_server/eap_server_md5.c
+CHAP=y
+endif
+
+ifdef CONFIG_EAP_TLS
+L_CFLAGS += -DEAP_SERVER_TLS
+OBJS += src/eap_server/eap_server_tls.c
+TLS_FUNCS=y
+endif
+
+ifdef CONFIG_EAP_UNAUTH_TLS
+L_CFLAGS += -DEAP_SERVER_UNAUTH_TLS
+ifndef CONFIG_EAP_TLS
+OBJS += src/eap_server/eap_server_tls.c
+TLS_FUNCS=y
+endif
+endif
+
+ifdef CONFIG_EAP_PEAP
+L_CFLAGS += -DEAP_SERVER_PEAP
+OBJS += src/eap_server/eap_server_peap.c
+OBJS += src/eap_common/eap_peap_common.c
+TLS_FUNCS=y
+CONFIG_EAP_MSCHAPV2=y
+endif
+
+ifdef CONFIG_EAP_TTLS
+L_CFLAGS += -DEAP_SERVER_TTLS
+OBJS += src/eap_server/eap_server_ttls.c
+TLS_FUNCS=y
+CHAP=y
+endif
+
+ifdef CONFIG_EAP_MSCHAPV2
+L_CFLAGS += -DEAP_SERVER_MSCHAPV2
+OBJS += src/eap_server/eap_server_mschapv2.c
+MS_FUNCS=y
+endif
+
+ifdef CONFIG_EAP_GTC
+L_CFLAGS += -DEAP_SERVER_GTC
+OBJS += src/eap_server/eap_server_gtc.c
+endif
+
+ifdef CONFIG_EAP_SIM
+L_CFLAGS += -DEAP_SERVER_SIM
+OBJS += src/eap_server/eap_server_sim.c
+CONFIG_EAP_SIM_COMMON=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_EAP_AKA
+L_CFLAGS += -DEAP_SERVER_AKA
+OBJS += src/eap_server/eap_server_aka.c
+CONFIG_EAP_SIM_COMMON=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_EAP_AKA_PRIME
+L_CFLAGS += -DEAP_SERVER_AKA_PRIME
+endif
+
+ifdef CONFIG_EAP_SIM_COMMON
+OBJS += src/eap_common/eap_sim_common.c
+# Example EAP-SIM/AKA interface for GSM/UMTS authentication. This can be
+# replaced with another file implementing the interface specified in
+# eap_sim_db.h.
+OBJS += src/eap_server/eap_sim_db.c
+NEED_FIPS186_2_PRF=y
+endif
+
+ifdef CONFIG_EAP_PAX
+L_CFLAGS += -DEAP_SERVER_PAX
+OBJS += src/eap_server/eap_server_pax.c src/eap_common/eap_pax_common.c
+endif
+
+ifdef CONFIG_EAP_PSK
+L_CFLAGS += -DEAP_SERVER_PSK
+OBJS += src/eap_server/eap_server_psk.c src/eap_common/eap_psk_common.c
+NEED_AES_ENCBLOCK=y
+NEED_AES_EAX=y
+endif
+
+ifdef CONFIG_EAP_SAKE
+L_CFLAGS += -DEAP_SERVER_SAKE
+OBJS += src/eap_server/eap_server_sake.c src/eap_common/eap_sake_common.c
+endif
+
+ifdef CONFIG_EAP_GPSK
+L_CFLAGS += -DEAP_SERVER_GPSK
+OBJS += src/eap_server/eap_server_gpsk.c src/eap_common/eap_gpsk_common.c
+ifdef CONFIG_EAP_GPSK_SHA256
+L_CFLAGS += -DEAP_GPSK_SHA256
+endif
+endif
+
+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_ECC=y
+NEED_DRAGONFLY=y
+endif
+
+ifdef CONFIG_EAP_EKE
+L_CFLAGS += -DEAP_SERVER_EKE
+OBJS += src/eap_server/eap_server_eke.c src/eap_common/eap_eke_common.c
+NEED_DH_GROUPS=y
+NEED_DH_GROUPS_ALL=y
+endif
+
+ifdef CONFIG_EAP_VENDOR_TEST
+L_CFLAGS += -DEAP_SERVER_VENDOR_TEST
+OBJS += src/eap_server/eap_server_vendor_test.c
+endif
+
+ifdef CONFIG_EAP_FAST
+L_CFLAGS += -DEAP_SERVER_FAST
+OBJS += src/eap_server/eap_server_fast.c
+OBJS += src/eap_common/eap_fast_common.c
+TLS_FUNCS=y
+NEED_T_PRF=y
+NEED_AES_UNWRAP=y
+endif
+
+ifdef CONFIG_EAP_TEAP
+L_CFLAGS += -DEAP_SERVER_TEAP
+OBJS += src/eap_server/eap_server_teap.c
+OBJS += src/eap_common/eap_teap_common.c
+TLS_FUNCS=y
+NEED_T_PRF=y
+NEED_SHA384=y
+NEED_TLS_PRF_SHA256=y
+NEED_TLS_PRF_SHA384=y
+NEED_AES_UNWRAP=y
+endif
+
+ifdef CONFIG_WPS
+L_CFLAGS += -DCONFIG_WPS -DEAP_SERVER_WSC
+OBJS += src/utils/uuid.c
+OBJS += src/ap/wps_hostapd.c
+OBJS += src/eap_server/eap_server_wsc.c src/eap_common/eap_wsc_common.c
+OBJS += src/wps/wps.c
+OBJS += src/wps/wps_common.c
+OBJS += src/wps/wps_attr_parse.c
+OBJS += src/wps/wps_attr_build.c
+OBJS += src/wps/wps_attr_process.c
+OBJS += src/wps/wps_dev_attr.c
+OBJS += src/wps/wps_enrollee.c
+OBJS += src/wps/wps_registrar.c
+NEED_DH_GROUPS=y
+NEED_BASE64=y
+NEED_AES_CBC=y
+NEED_MODEXP=y
+CONFIG_EAP=y
+
+ifdef CONFIG_WPS_NFC
+L_CFLAGS += -DCONFIG_WPS_NFC
+OBJS += src/wps/ndef.c
+NEED_WPS_OOB=y
+endif
+
+ifdef NEED_WPS_OOB
+L_CFLAGS += -DCONFIG_WPS_OOB
+endif
+
+ifdef CONFIG_WPS_UPNP
+L_CFLAGS += -DCONFIG_WPS_UPNP
+OBJS += src/wps/wps_upnp.c
+OBJS += src/wps/wps_upnp_ssdp.c
+OBJS += src/wps/wps_upnp_web.c
+OBJS += src/wps/wps_upnp_event.c
+OBJS += src/wps/wps_upnp_ap.c
+OBJS += src/wps/upnp_xml.c
+OBJS += src/wps/httpread.c
+OBJS += src/wps/http_client.c
+OBJS += src/wps/http_server.c
+endif
+
+ifdef CONFIG_WPS_STRICT
+L_CFLAGS += -DCONFIG_WPS_STRICT
+OBJS += src/wps/wps_validate.c
+endif
+
+ifdef CONFIG_WPS_TESTING
+L_CFLAGS += -DCONFIG_WPS_TESTING
+endif
+
+endif
+
+ifdef CONFIG_DPP
+L_CFLAGS += -DCONFIG_DPP
+OBJS += src/common/dpp.c
+OBJS += src/common/dpp_auth.c
+OBJS += src/common/dpp_backup.c
+OBJS += src/common/dpp_crypto.c
+OBJS += src/common/dpp_pkex.c
+OBJS += src/common/dpp_reconfig.c
+OBJS += src/common/dpp_tcp.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_SHA384=y
+NEED_SHA512=y
+NEED_ECC=y
+NEED_JSON=y
+NEED_GAS=y
+NEED_BASE64=y
+NEED_ASN1=y
+ifdef CONFIG_DPP2
+L_CFLAGS += -DCONFIG_DPP2
+endif
+endif
+
+ifdef CONFIG_PASN
+L_CFLAGS += -DCONFIG_PASN
+L_CFLAGS += -DCONFIG_PTKSA_CACHE
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+OBJS += src/common/ptksa_cache.c
+endif
+
+ifdef CONFIG_EAP_IKEV2
+L_CFLAGS += -DEAP_SERVER_IKEV2
+OBJS += src/eap_server/eap_server_ikev2.c src/eap_server/ikev2.c
+OBJS += src/eap_common/eap_ikev2_common.c src/eap_common/ikev2_common.c
+NEED_DH_GROUPS=y
+NEED_DH_GROUPS_ALL=y
+NEED_MODEXP=y
+NEED_CIPHER=y
+endif
+
+ifdef CONFIG_EAP_TNC
+L_CFLAGS += -DEAP_SERVER_TNC
+OBJS += src/eap_server/eap_server_tnc.c
+OBJS += src/eap_server/tncs.c
+NEED_BASE64=y
+ifndef CONFIG_DRIVER_BSD
+LIBS += -ldl
+endif
+endif
+
+# Basic EAP functionality is needed for EAPOL
+OBJS += eap_register.c
+OBJS += src/eap_server/eap_server.c
+OBJS += src/eap_common/eap_common.c
+OBJS += src/eap_server/eap_server_methods.c
+OBJS += src/eap_server/eap_server_identity.c
+L_CFLAGS += -DEAP_SERVER_IDENTITY
+
+ifdef CONFIG_EAP
+L_CFLAGS += -DEAP_SERVER
+endif
+
+ifdef CONFIG_PKCS12
+L_CFLAGS += -DPKCS12_FUNCS
+endif
+
+ifdef NEED_DRAGONFLY
+OBJS += src/common/dragonfly.c
+endif
+
+ifdef MS_FUNCS
+OBJS += src/crypto/ms_funcs.c
+NEED_DES=y
+NEED_MD4=y
+endif
+
+ifdef CHAP
+OBJS += src/eap_common/chap.c
+endif
+
+ifdef TLS_FUNCS
+NEED_DES=y
+# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS)
+L_CFLAGS += -DEAP_TLS_FUNCS
+OBJS += src/eap_server/eap_server_tls_common.c
+NEED_TLS_PRF=y
+endif
+
+ifndef CONFIG_TLS
+CONFIG_TLS=openssl
+endif
+
+ifdef CONFIG_TLSV11
+L_CFLAGS += -DCONFIG_TLSV11
+endif
+
+ifdef CONFIG_TLSV12
+L_CFLAGS += -DCONFIG_TLSV12
+endif
+
+ifeq ($(CONFIG_TLS), openssl)
+ifdef TLS_FUNCS
+OBJS += src/crypto/tls_openssl.c
+OBJS += src/crypto/tls_openssl_ocsp.c
+LIBS += -lssl
+endif
+OBJS += src/crypto/crypto_openssl.c
+HOBJS += src/crypto/crypto_openssl.c
+ifdef NEED_FIPS186_2_PRF
+OBJS += src/crypto/fips_prf_openssl.c
+endif
+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_$(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_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
+CONFIG_CRYPTO=internal
+endif
+ifdef TLS_FUNCS
+OBJS += src/crypto/crypto_internal-rsa.c
+OBJS += src/crypto/tls_internal.c
+OBJS += src/tls/tlsv1_common.c
+OBJS += src/tls/tlsv1_record.c
+OBJS += src/tls/tlsv1_cred.c
+OBJS += src/tls/tlsv1_server.c
+OBJS += src/tls/tlsv1_server_write.c
+OBJS += src/tls/tlsv1_server_read.c
+OBJS += src/tls/rsa.c
+OBJS += src/tls/x509v3.c
+OBJS += src/tls/pkcs1.c
+OBJS += src/tls/pkcs5.c
+OBJS += src/tls/pkcs8.c
+NEED_ASN1=y
+NEED_BASE64=y
+NEED_TLS_PRF=y
+ifdef CONFIG_TLSV12
+NEED_TLS_PRF_SHA256=y
+endif
+NEED_MODEXP=y
+NEED_CIPHER=y
+L_CFLAGS += -DCONFIG_TLS_INTERNAL
+L_CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
+endif
+ifdef NEED_CIPHER
+NEED_DES=y
+OBJS += src/crypto/crypto_internal-cipher.c
+endif
+ifdef NEED_MODEXP
+OBJS += src/crypto/crypto_internal-modexp.c
+OBJS += src/tls/bignum.c
+endif
+ifeq ($(CONFIG_CRYPTO), libtomcrypt)
+OBJS += src/crypto/crypto_libtomcrypt.c
+LIBS += -ltomcrypt -ltfm
+LIBS_h += -ltomcrypt -ltfm
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+ifeq ($(CONFIG_CRYPTO), internal)
+OBJS += src/crypto/crypto_internal.c
+NEED_AES_DEC=y
+L_CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+ifdef CONFIG_INTERNAL_LIBTOMMATH
+L_CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST
+L_CFLAGS += -DLTM_FAST
+endif
+else
+LIBS += -ltommath
+LIBS_h += -ltommath
+endif
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_DES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD4=y
+CONFIG_INTERNAL_MD5=y
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_SHA384=y
+CONFIG_INTERNAL_SHA512=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+ifeq ($(CONFIG_CRYPTO), cryptoapi)
+OBJS += src/crypto/crypto_cryptoapi.c
+OBJS_p += src/crypto/crypto_cryptoapi.c
+L_CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+endif
+endif
+
+ifeq ($(CONFIG_TLS), none)
+ifdef TLS_FUNCS
+OBJS += src/crypto/tls_none.c
+L_CFLAGS += -DEAP_TLS_NONE
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD5=y
+endif
+OBJS += src/crypto/crypto_none.c
+OBJS_p += src/crypto/crypto_none.c
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+endif
+
+ifndef TLS_FUNCS
+OBJS += src/crypto/tls_none.c
+ifeq ($(CONFIG_TLS), internal)
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD5=y
+CONFIG_INTERNAL_RC4=y
+endif
+endif
+
+AESOBJS = # none so far
+ifdef CONFIG_INTERNAL_AES
+AESOBJS += src/crypto/aes-internal.c src/crypto/aes-internal-enc.c
+endif
+
+ifneq ($(CONFIG_TLS), openssl)
+AESOBJS += src/crypto/aes-wrap.c
+endif
+ifdef NEED_AES_EAX
+AESOBJS += src/crypto/aes-eax.c
+NEED_AES_CTR=y
+endif
+ifdef NEED_AES_SIV
+AESOBJS += src/crypto/aes-siv.c
+NEED_AES_CTR=y
+endif
+ifdef NEED_AES_CTR
+AESOBJS += src/crypto/aes-ctr.c
+endif
+ifdef NEED_AES_ENCBLOCK
+AESOBJS += src/crypto/aes-encblock.c
+endif
+AESOBJS += src/crypto/aes-omac1.c
+ifdef NEED_AES_UNWRAP
+ifneq ($(CONFIG_TLS), openssl)
+NEED_AES_DEC=y
+AESOBJS += src/crypto/aes-unwrap.c
+endif
+endif
+ifdef NEED_AES_CBC
+NEED_AES_DEC=y
+ifneq ($(CONFIG_TLS), openssl)
+AESOBJS += src/crypto/aes-cbc.c
+endif
+endif
+ifdef NEED_AES_DEC
+ifdef CONFIG_INTERNAL_AES
+AESOBJS += src/crypto/aes-internal-dec.c
+endif
+endif
+ifdef NEED_AES
+OBJS += $(AESOBJS)
+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
+ifdef NEED_FIPS186_2_PRF
+SHA1OBJS += src/crypto/fips_prf_internal.c
+endif
+endif
+ifneq ($(CONFIG_TLS), openssl)
+SHA1OBJS += src/crypto/sha1-pbkdf2.c
+endif
+ifdef NEED_T_PRF
+SHA1OBJS += src/crypto/sha1-tprf.c
+endif
+ifdef NEED_TLS_PRF
+SHA1OBJS += src/crypto/sha1-tlsprf.c
+endif
+endif
+
+ifdef NEED_SHA1
+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
+OBJS += src/crypto/md5-internal.c
+HOBJS += src/crypto/md5-internal.c
+endif
+endif
+
+ifdef NEED_MD4
+ifdef CONFIG_INTERNAL_MD4
+OBJS += src/crypto/md4-internal.c
+endif
+endif
+
+ifdef NEED_DES
+ifdef CONFIG_INTERNAL_DES
+OBJS += src/crypto/des-internal.c
+endif
+endif
+
+ifdef CONFIG_NO_RC4
+L_CFLAGS += -DCONFIG_NO_RC4
+endif
+
+ifdef NEED_RC4
+ifdef CONFIG_INTERNAL_RC4
+ifndef CONFIG_NO_RC4
+OBJS += src/crypto/rc4.c
+endif
+endif
+endif
+
+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
+endif
+ifdef NEED_TLS_PRF_SHA256
+OBJS += src/crypto/sha256-tlsprf.c
+endif
+ifdef NEED_TLS_PRF_SHA384
+OBJS += src/crypto/sha384-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
+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
+OBJS += src/crypto/sha384-internal.c
+endif
+
+ifdef CONFIG_INTERNAL_SHA512
+L_CFLAGS += -DCONFIG_INTERNAL_SHA512
+OBJS += src/crypto/sha512-internal.c
+endif
+
+ifdef NEED_ASN1
+OBJS += src/tls/asn1.c
+endif
+
+ifdef NEED_DH_GROUPS
+OBJS += src/crypto/dh_groups.c
+endif
+ifdef NEED_DH_GROUPS_ALL
+L_CFLAGS += -DALL_DH_GROUPS
+endif
+ifdef CONFIG_INTERNAL_DH_GROUP5
+ifdef NEED_DH_GROUPS
+OBJS += src/crypto/dh_group5.c
+endif
+endif
+
+ifdef NEED_ECC
+L_CFLAGS += -DCONFIG_ECC
+endif
+
+ifdef CONFIG_NO_RANDOM_POOL
+L_CFLAGS += -DCONFIG_NO_RANDOM_POOL
+else
+OBJS += src/crypto/random.c
+HOBJS += src/crypto/random.c
+HOBJS += src/utils/eloop.c
+HOBJS += $(SHA1OBJS)
+ifneq ($(CONFIG_TLS), openssl)
+HOBJS += src/crypto/md5.c
+endif
+endif
+
+ifdef CONFIG_RADIUS_SERVER
+L_CFLAGS += -DRADIUS_SERVER
+OBJS += src/radius/radius_server.c
+endif
+
+ifdef CONFIG_IPV6
+L_CFLAGS += -DCONFIG_IPV6
+endif
+
+ifdef CONFIG_DRIVER_RADIUS_ACL
+L_CFLAGS += -DCONFIG_DRIVER_RADIUS_ACL
+endif
+
+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
+OBJS += src/ap/ieee802_11.c
+OBJS += src/ap/hw_features.c
+OBJS += src/ap/dfs.c
+L_CFLAGS += -DNEED_AP_MLME
+endif
+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_P2P_MANAGER
+L_CFLAGS += -DCONFIG_P2P_MANAGER
+OBJS += src/ap/p2p_hostapd.c
+endif
+
+ifdef CONFIG_HS20
+L_CFLAGS += -DCONFIG_HS20
+OBJS += src/ap/hs20.c
+CONFIG_INTERWORKING=y
+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
+
+ifdef CONFIG_PROXYARP
+L_CFLAGS += -DCONFIG_PROXYARP
+OBJS += src/ap/x_snoop.c
+OBJS += src/ap/dhcp_snoop.c
+ifdef CONFIG_IPV6
+OBJS += src/ap/ndisc_snoop.c
+endif
+endif
+
+OBJS += src/drivers/driver_common.c
+
+ifdef CONFIG_ACS
+L_CFLAGS += -DCONFIG_ACS
+OBJS += src/ap/acs.c
+LIBS += -lm
+endif
+
+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
+
+ifdef CONFIG_DEBUG_FILE
+L_CFLAGS += -DCONFIG_DEBUG_FILE
+endif
+
+ifdef CONFIG_ANDROID_LOG
+L_CFLAGS += -DCONFIG_ANDROID_LOG
+endif
+
+OBJS_c = hostapd_cli.c
+OBJS_c += src/common/wpa_ctrl.c
+OBJS_c += src/utils/os_$(CONFIG_OS).c
+OBJS_c += src/common/cli.c
+OBJS_c += src/utils/eloop.c
+OBJS_c += src/utils/common.c
+ifdef CONFIG_WPA_TRACE
+OBJS_c += src/utils/trace.c
+endif
+OBJS_c += src/utils/wpa_debug.c
+ifdef CONFIG_WPA_CLI_EDIT
+OBJS_c += src/utils/edit.c
+else
+OBJS_c += src/utils/edit_simple.c
+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)
+LOCAL_C_INCLUDES := $(INCLUDES)
+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
+ifneq ($(BOARD_HOSTAPD_PRIVATE_LIB),)
+LOCAL_STATIC_LIBRARIES += $(BOARD_HOSTAPD_PRIVATE_LIB)
+endif
+LOCAL_SHARED_LIBRARIES := libc libcutils liblog libcrypto libssl
+ifdef CONFIG_DRIVER_NL80211
+ifneq ($(wildcard external/libnl),)
+LOCAL_SHARED_LIBRARIES += libnl
+else
+LOCAL_STATIC_LIBRARIES += libnl_2
+endif
+endif
+LOCAL_CFLAGS := $(L_CFLAGS)
+LOCAL_SRC_FILES := $(OBJS)
+LOCAL_C_INCLUDES := $(INCLUDES)
+LOCAL_INIT_RC := hostapd.android.rc
+include $(BUILD_EXECUTABLE)
+
+endif # ifeq ($(WPA_BUILD_HOSTAPD),true)
diff --git a/contrib/wpa/hostapd/ChangeLog b/contrib/wpa/hostapd/ChangeLog
index 6c4410e8ec6..34a8a081879 100644
--- a/contrib/wpa/hostapd/ChangeLog
+++ b/contrib/wpa/hostapd/ChangeLog
@@ -362,7 +362,7 @@ ChangeLog for hostapd
* RADIUS server functionality
- add minimal RADIUS accounting server support (hostapd-as-server);
this is mainly to enable testing coverage with hwsim scripts
- - allow authentication log to be written into SQLite databse
+ - allow authentication log to be written into SQLite database
- added option for TLS protocol testing of an EAP peer by simulating
various misbehaviors/known attacks
- MAC ACL support for testing purposes
@@ -668,7 +668,7 @@ ChangeLog for hostapd
* fixed HT Capabilities IE with nl80211 drivers
* moved generic AP functionality code into src/ap
* WPS: handle Selected Registrar as union of info from all Registrars
- * remove obsolte Prism54.org driver wrapper
+ * remove obsolete Prism54.org driver wrapper
* added internal debugging mechanism with backtrace support and memory
allocation/freeing validation, etc. tests (CONFIG_WPA_TRACE=y)
* EAP-FAST server: piggyback Phase 2 start with the end of Phase 1
diff --git a/contrib/wpa/hostapd/Makefile b/contrib/wpa/hostapd/Makefile
new file mode 100644
index 00000000000..ac085fd1052
--- /dev/null
+++ b/contrib/wpa/hostapd/Makefile
@@ -0,0 +1,1375 @@
+ALL=hostapd hostapd_cli
+CONFIG_FILE = .config
+
+include ../src/build.rules
+
+ifdef LIBS
+# If LIBS is set with some global build system defaults, clone those for
+# LIBS_c, LIBS_h, and LIBS_n to cover hostapd_cli, hlr_auc_gw, and
+# nt_password_hash as well.
+ifndef LIBS_c
+LIBS_c := $(LIBS)
+endif
+ifndef LIBS_h
+LIBS_h := $(LIBS)
+endif
+ifndef LIBS_n
+LIBS_n := $(LIBS)
+endif
+ifndef LIBS_s
+LIBS_s := $(LIBS)
+endif
+endif
+
+CFLAGS += $(EXTRA_CFLAGS)
+CFLAGS += -I$(abspath ../src)
+CFLAGS += -I$(abspath ../src/utils)
+
+export BINDIR ?= /usr/local/bin/
+
+ifndef CONFIG_NO_GITVER
+# Add VERSION_STR postfix for builds from a git repository
+ifeq ($(wildcard ../.git),../.git)
+GITVER := $(shell git describe --dirty=+)
+ifneq ($(GITVER),)
+CFLAGS += -DGIT_VERSION_STR_POSTFIX=\"-$(GITVER)\"
+endif
+endif
+endif
+
+ifdef CONFIG_TESTING_OPTIONS
+CFLAGS += -DCONFIG_TESTING_OPTIONS
+CONFIG_WPS_TESTING=y
+endif
+
+ifndef CONFIG_OS
+ifdef CONFIG_NATIVE_WINDOWS
+CONFIG_OS=win32
+else
+CONFIG_OS=unix
+endif
+endif
+
+ifeq ($(CONFIG_OS), internal)
+CFLAGS += -DOS_NO_C_LIB_DEFINES
+endif
+
+ifdef CONFIG_NATIVE_WINDOWS
+CFLAGS += -DCONFIG_NATIVE_WINDOWS
+LIBS += -lws2_32
+endif
+
+OBJS += main.o
+OBJS += config_file.o
+
+OBJS += ../src/ap/hostapd.o
+OBJS += ../src/ap/wpa_auth_glue.o
+OBJS += ../src/ap/drv_callbacks.o
+OBJS += ../src/ap/ap_drv_ops.o
+OBJS += ../src/ap/utils.o
+OBJS += ../src/ap/authsrv.o
+OBJS += ../src/ap/ieee802_1x.o
+OBJS += ../src/ap/ap_config.o
+OBJS += ../src/ap/eap_user_db.o
+OBJS += ../src/ap/ieee802_11_auth.o
+OBJS += ../src/ap/sta_info.o
+OBJS += ../src/ap/wpa_auth.o
+OBJS += ../src/ap/tkip_countermeasures.o
+OBJS += ../src/ap/ap_mlme.o
+OBJS += ../src/ap/wpa_auth_ie.o
+OBJS += ../src/ap/preauth_auth.o
+OBJS += ../src/ap/pmksa_cache_auth.o
+OBJS += ../src/ap/ieee802_11_shared.o
+OBJS += ../src/ap/beacon.o
+OBJS += ../src/ap/bss_load.o
+OBJS += ../src/ap/neighbor_db.o
+OBJS += ../src/ap/rrm.o
+
+OBJS_c = hostapd_cli.o
+OBJS_c += ../src/common/wpa_ctrl.o
+OBJS_c += ../src/utils/os_$(CONFIG_OS).o
+OBJS_c += ../src/common/cli.o
+
+NEED_RC4=y
+NEED_AES=y
+NEED_MD5=y
+NEED_SHA1=y
+
+OBJS += ../src/drivers/drivers.o
+CFLAGS += -DHOSTAPD
+
+ifdef CONFIG_TAXONOMY
+CFLAGS += -DCONFIG_TAXONOMY
+OBJS += ../src/ap/taxonomy.o
+endif
+
+ifdef CONFIG_MODULE_TESTS
+CFLAGS += -DCONFIG_MODULE_TESTS
+OBJS += hapd_module_tests.o
+endif
+
+ifdef CONFIG_WPA_TRACE
+CFLAGS += -DWPA_TRACE
+OBJS += ../src/utils/trace.o
+HOBJS += ../src/utils/trace.o
+LDFLAGS += -rdynamic
+CFLAGS += -funwind-tables
+ifdef CONFIG_WPA_TRACE_BFD
+CFLAGS += -DPACKAGE="hostapd" -DWPA_TRACE_BFD
+LIBS += -lbfd -ldl -liberty -lz
+LIBS_c += -lbfd -ldl -liberty -lz
+LIBS_h += -lbfd -ldl -liberty -lz
+LIBS_n += -lbfd -ldl -liberty -lz
+LIBS_s += -lbfd -ldl -liberty -lz
+endif
+endif
+
+ifndef CONFIG_ELOOP
+CONFIG_ELOOP=eloop
+endif
+OBJS += ../src/utils/$(CONFIG_ELOOP).o
+OBJS_c += ../src/utils/$(CONFIG_ELOOP).o
+
+ifeq ($(CONFIG_ELOOP), eloop)
+# Using glibc < 2.17 requires -lrt for clock_gettime()
+LIBS += -lrt
+LIBS_c += -lrt
+LIBS_h += -lrt
+LIBS_n += -lrt
+endif
+
+ifdef CONFIG_ELOOP_POLL
+CFLAGS += -DCONFIG_ELOOP_POLL
+endif
+
+ifdef CONFIG_ELOOP_EPOLL
+CFLAGS += -DCONFIG_ELOOP_EPOLL
+endif
+
+ifdef CONFIG_ELOOP_KQUEUE
+CFLAGS += -DCONFIG_ELOOP_KQUEUE
+endif
+
+OBJS += ../src/utils/common.o
+OBJS_c += ../src/utils/common.o
+OBJS += ../src/utils/wpa_debug.o
+OBJS_c += ../src/utils/wpa_debug.o
+OBJS += ../src/utils/wpabuf.o
+OBJS += ../src/utils/os_$(CONFIG_OS).o
+OBJS += ../src/utils/ip_addr.o
+OBJS += ../src/utils/crc32.o
+
+OBJS += ../src/common/ieee802_11_common.o
+OBJS += ../src/common/wpa_common.o
+OBJS += ../src/common/hw_features_common.o
+
+OBJS += ../src/eapol_auth/eapol_auth_sm.o
+
+
+ifdef CONFIG_CODE_COVERAGE
+CFLAGS += -O0 -fprofile-arcs -ftest-coverage
+LIBS += -lgcov
+LIBS_c += -lgcov
+LIBS_h += -lgcov
+LIBS_n += -lgcov
+endif
+
+ifndef CONFIG_NO_DUMP_STATE
+# define HOSTAPD_DUMP_STATE to include support for dumping internal state
+# through control interface commands (undefine it, if you want to save in
+# binary size)
+CFLAGS += -DHOSTAPD_DUMP_STATE
+OBJS += ../src/eapol_auth/eapol_auth_dump.o
+endif
+
+ifdef CONFIG_NO_RADIUS
+CFLAGS += -DCONFIG_NO_RADIUS
+CONFIG_NO_ACCOUNTING=y
+else
+OBJS += ../src/radius/radius.o
+OBJS += ../src/radius/radius_client.o
+OBJS += ../src/radius/radius_das.o
+endif
+
+ifdef CONFIG_NO_ACCOUNTING
+CFLAGS += -DCONFIG_NO_ACCOUNTING
+else
+OBJS += ../src/ap/accounting.o
+endif
+
+ifdef CONFIG_NO_VLAN
+CFLAGS += -DCONFIG_NO_VLAN
+else
+OBJS += ../src/ap/vlan_init.o
+OBJS += ../src/ap/vlan_ifconfig.o
+OBJS += ../src/ap/vlan.o
+ifdef CONFIG_FULL_DYNAMIC_VLAN
+# Define CONFIG_FULL_DYNAMIC_VLAN to have hostapd manipulate bridges
+# and VLAN interfaces for the VLAN feature.
+CFLAGS += -DCONFIG_FULL_DYNAMIC_VLAN
+OBJS += ../src/ap/vlan_full.o
+ifdef CONFIG_VLAN_NETLINK
+OBJS += ../src/ap/vlan_util.o
+else
+OBJS += ../src/ap/vlan_ioctl.o
+endif
+endif
+endif
+
+ifdef CONFIG_NO_CTRL_IFACE
+CFLAGS += -DCONFIG_NO_CTRL_IFACE
+else
+ifeq ($(CONFIG_CTRL_IFACE), udp)
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+else
+ifeq ($(CONFIG_CTRL_IFACE), udp6)
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_IPV6
+else
+ifeq ($(CONFIG_CTRL_IFACE), udp-remote)
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE
+else
+ifeq ($(CONFIG_CTRL_IFACE), udp6-remote)
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_IPV6
+else
+CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
+endif
+endif
+endif
+endif
+OBJS += ../src/common/ctrl_iface_common.o
+OBJS += ctrl_iface.o
+OBJS += ../src/ap/ctrl_iface_ap.o
+endif
+
+ifndef CONFIG_NO_CTRL_IFACE
+CFLAGS += -DCONFIG_CTRL_IFACE
+endif
+
+ifdef CONFIG_RSN_PREAUTH
+CFLAGS += -DCONFIG_RSN_PREAUTH
+CONFIG_L2_PACKET=y
+endif
+
+ifdef CONFIG_HS20
+CONFIG_PROXYARP=y
+endif
+
+ifdef CONFIG_PROXYARP
+CONFIG_L2_PACKET=y
+endif
+
+ifdef CONFIG_SUITEB
+CFLAGS += -DCONFIG_SUITEB
+endif
+
+ifdef CONFIG_SUITEB192
+CFLAGS += -DCONFIG_SUITEB192
+NEED_SHA384=y
+endif
+
+ifdef CONFIG_OCV
+CFLAGS += -DCONFIG_OCV
+OBJS += ../src/common/ocv.o
+endif
+
+ifdef CONFIG_IEEE80211R
+CFLAGS += -DCONFIG_IEEE80211R -DCONFIG_IEEE80211R_AP
+OBJS += ../src/ap/wpa_auth_ft.o
+NEED_AES_UNWRAP=y
+NEED_AES_SIV=y
+NEED_ETH_P_OUI=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
+CFLAGS += -DCONFIG_SAE
+OBJS += ../src/common/sae.o
+ifdef CONFIG_SAE_PK
+CFLAGS += -DCONFIG_SAE_PK
+OBJS += ../src/common/sae_pk.o
+endif
+NEED_ECC=y
+NEED_DH_GROUPS=y
+NEED_HMAC_SHA256_KDF=y
+NEED_AP_MLME=y
+NEED_DRAGONFLY=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_SHA384=y
+NEED_SHA512=y
+endif
+
+ifdef CONFIG_AIRTIME_POLICY
+CFLAGS += -DCONFIG_AIRTIME_POLICY
+OBJS += ../src/ap/airtime_policy.o
+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 -DCONFIG_WNM_AP
+OBJS += ../src/ap/wnm_ap.o
+endif
+
+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
+endif
+
+include ../src/drivers/drivers.mak
+OBJS += $(DRV_AP_OBJS)
+CFLAGS += $(DRV_AP_CFLAGS)
+LDFLAGS += $(DRV_AP_LDFLAGS)
+LIBS += $(DRV_AP_LIBS)
+
+ifdef CONFIG_L2_PACKET
+ifdef CONFIG_DNET_PCAP
+ifdef CONFIG_L2_FREEBSD
+LIBS += -lpcap
+OBJS += ../src/l2_packet/l2_packet_freebsd.o
+else
+LIBS += -ldnet -lpcap
+OBJS += ../src/l2_packet/l2_packet_pcap.o
+endif
+else
+OBJS += ../src/l2_packet/l2_packet_linux.o
+endif
+else
+OBJS += ../src/l2_packet/l2_packet_none.o
+endif
+
+
+ifdef CONFIG_ERP
+CFLAGS += -DCONFIG_ERP
+NEED_HMAC_SHA256_KDF=y
+endif
+
+ifdef CONFIG_EAP_MD5
+CFLAGS += -DEAP_SERVER_MD5
+OBJS += ../src/eap_server/eap_server_md5.o
+CHAP=y
+endif
+
+ifdef CONFIG_EAP_TLS
+CFLAGS += -DEAP_SERVER_TLS
+OBJS += ../src/eap_server/eap_server_tls.o
+TLS_FUNCS=y
+endif
+
+ifdef CONFIG_EAP_UNAUTH_TLS
+CFLAGS += -DEAP_SERVER_UNAUTH_TLS
+ifndef CONFIG_EAP_TLS
+OBJS += ../src/eap_server/eap_server_tls.o
+TLS_FUNCS=y
+endif
+endif
+
+ifdef CONFIG_EAP_PEAP
+CFLAGS += -DEAP_SERVER_PEAP
+OBJS += ../src/eap_server/eap_server_peap.o
+OBJS += ../src/eap_common/eap_peap_common.o
+TLS_FUNCS=y
+CONFIG_EAP_MSCHAPV2=y
+endif
+
+ifdef CONFIG_EAP_TTLS
+CFLAGS += -DEAP_SERVER_TTLS
+OBJS += ../src/eap_server/eap_server_ttls.o
+TLS_FUNCS=y
+CHAP=y
+endif
+
+ifdef CONFIG_EAP_MSCHAPV2
+CFLAGS += -DEAP_SERVER_MSCHAPV2
+OBJS += ../src/eap_server/eap_server_mschapv2.o
+MS_FUNCS=y
+endif
+
+ifdef CONFIG_EAP_GTC
+CFLAGS += -DEAP_SERVER_GTC
+OBJS += ../src/eap_server/eap_server_gtc.o
+endif
+
+ifdef CONFIG_EAP_SIM
+CFLAGS += -DEAP_SERVER_SIM
+OBJS += ../src/eap_server/eap_server_sim.o
+CONFIG_EAP_SIM_COMMON=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_EAP_AKA
+CFLAGS += -DEAP_SERVER_AKA
+OBJS += ../src/eap_server/eap_server_aka.o
+CONFIG_EAP_SIM_COMMON=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_EAP_AKA_PRIME
+CFLAGS += -DEAP_SERVER_AKA_PRIME
+endif
+
+ifdef CONFIG_EAP_SIM_COMMON
+OBJS += ../src/eap_common/eap_sim_common.o
+# Example EAP-SIM/AKA interface for GSM/UMTS authentication. This can be
+# replaced with another file implementing the interface specified in
+# eap_sim_db.h.
+OBJS += ../src/eap_server/eap_sim_db.o
+NEED_FIPS186_2_PRF=y
+endif
+
+ifdef CONFIG_EAP_PAX
+CFLAGS += -DEAP_SERVER_PAX
+OBJS += ../src/eap_server/eap_server_pax.o ../src/eap_common/eap_pax_common.o
+endif
+
+ifdef CONFIG_EAP_PSK
+CFLAGS += -DEAP_SERVER_PSK
+OBJS += ../src/eap_server/eap_server_psk.o ../src/eap_common/eap_psk_common.o
+NEED_AES_ENCBLOCK=y
+NEED_AES_EAX=y
+endif
+
+ifdef CONFIG_EAP_SAKE
+CFLAGS += -DEAP_SERVER_SAKE
+OBJS += ../src/eap_server/eap_server_sake.o ../src/eap_common/eap_sake_common.o
+endif
+
+ifdef CONFIG_EAP_GPSK
+CFLAGS += -DEAP_SERVER_GPSK
+OBJS += ../src/eap_server/eap_server_gpsk.o ../src/eap_common/eap_gpsk_common.o
+ifdef CONFIG_EAP_GPSK_SHA256
+CFLAGS += -DEAP_GPSK_SHA256
+endif
+endif
+
+ifdef CONFIG_EAP_PWD
+CFLAGS += -DEAP_SERVER_PWD
+OBJS += ../src/eap_server/eap_server_pwd.o ../src/eap_common/eap_pwd_common.o
+NEED_ECC=y
+NEED_DRAGONFLY=y
+endif
+
+ifdef CONFIG_EAP_EKE
+CFLAGS += -DEAP_SERVER_EKE
+OBJS += ../src/eap_server/eap_server_eke.o ../src/eap_common/eap_eke_common.o
+NEED_DH_GROUPS=y
+NEED_DH_GROUPS_ALL=y
+endif
+
+ifdef CONFIG_EAP_VENDOR_TEST
+CFLAGS += -DEAP_SERVER_VENDOR_TEST
+OBJS += ../src/eap_server/eap_server_vendor_test.o
+endif
+
+ifdef CONFIG_EAP_FAST
+CFLAGS += -DEAP_SERVER_FAST
+OBJS += ../src/eap_server/eap_server_fast.o
+OBJS += ../src/eap_common/eap_fast_common.o
+TLS_FUNCS=y
+NEED_T_PRF=y
+NEED_AES_UNWRAP=y
+endif
+
+ifdef CONFIG_EAP_TEAP
+CFLAGS += -DEAP_SERVER_TEAP
+OBJS += ../src/eap_server/eap_server_teap.o
+OBJS += ../src/eap_common/eap_teap_common.o
+TLS_FUNCS=y
+NEED_T_PRF=y
+NEED_SHA384=y
+NEED_TLS_PRF_SHA256=y
+NEED_TLS_PRF_SHA384=y
+NEED_AES_UNWRAP=y
+endif
+
+ifdef CONFIG_WPS
+CFLAGS += -DCONFIG_WPS -DEAP_SERVER_WSC
+OBJS += ../src/utils/uuid.o
+OBJS += ../src/ap/wps_hostapd.o
+OBJS += ../src/eap_server/eap_server_wsc.o ../src/eap_common/eap_wsc_common.o
+OBJS += ../src/wps/wps.o
+OBJS += ../src/wps/wps_common.o
+OBJS += ../src/wps/wps_attr_parse.o
+OBJS += ../src/wps/wps_attr_build.o
+OBJS += ../src/wps/wps_attr_process.o
+OBJS += ../src/wps/wps_dev_attr.o
+OBJS += ../src/wps/wps_enrollee.o
+OBJS += ../src/wps/wps_registrar.o
+NEED_DH_GROUPS=y
+NEED_BASE64=y
+NEED_AES_CBC=y
+NEED_MODEXP=y
+CONFIG_EAP=y
+
+ifdef CONFIG_WPS_NFC
+CFLAGS += -DCONFIG_WPS_NFC
+OBJS += ../src/wps/ndef.o
+NEED_WPS_OOB=y
+endif
+
+ifdef NEED_WPS_OOB
+CFLAGS += -DCONFIG_WPS_OOB
+endif
+
+ifdef CONFIG_WPS_UPNP
+CFLAGS += -DCONFIG_WPS_UPNP
+OBJS += ../src/wps/wps_upnp.o
+OBJS += ../src/wps/wps_upnp_ssdp.o
+OBJS += ../src/wps/wps_upnp_web.o
+OBJS += ../src/wps/wps_upnp_event.o
+OBJS += ../src/wps/wps_upnp_ap.o
+OBJS += ../src/wps/upnp_xml.o
+OBJS += ../src/wps/httpread.o
+OBJS += ../src/wps/http_client.o
+OBJS += ../src/wps/http_server.o
+endif
+
+ifdef CONFIG_WPS_STRICT
+CFLAGS += -DCONFIG_WPS_STRICT
+OBJS += ../src/wps/wps_validate.o
+endif
+
+ifdef CONFIG_WPS_TESTING
+CFLAGS += -DCONFIG_WPS_TESTING
+endif
+
+endif
+
+ifdef CONFIG_DPP
+CFLAGS += -DCONFIG_DPP
+OBJS += ../src/common/dpp.o
+OBJS += ../src/common/dpp_auth.o
+OBJS += ../src/common/dpp_backup.o
+OBJS += ../src/common/dpp_crypto.o
+OBJS += ../src/common/dpp_pkex.o
+OBJS += ../src/common/dpp_reconfig.o
+OBJS += ../src/common/dpp_tcp.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_SHA384=y
+NEED_SHA512=y
+NEED_ECC=y
+NEED_JSON=y
+NEED_GAS=y
+NEED_BASE64=y
+NEED_ASN1=y
+ifdef CONFIG_DPP2
+CFLAGS += -DCONFIG_DPP2
+endif
+endif
+
+ifdef CONFIG_PASN
+CFLAGS += -DCONFIG_PASN
+CFLAGS += -DCONFIG_PTKSA_CACHE
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+OBJS += ../src/common/ptksa_cache.o
+endif
+
+ifdef CONFIG_EAP_IKEV2
+CFLAGS += -DEAP_SERVER_IKEV2
+OBJS += ../src/eap_server/eap_server_ikev2.o ../src/eap_server/ikev2.o
+OBJS += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o
+NEED_DH_GROUPS=y
+NEED_DH_GROUPS_ALL=y
+NEED_MODEXP=y
+NEED_CIPHER=y
+endif
+
+ifdef CONFIG_EAP_TNC
+CFLAGS += -DEAP_SERVER_TNC
+OBJS += ../src/eap_server/eap_server_tnc.o
+OBJS += ../src/eap_server/tncs.o
+NEED_BASE64=y
+ifndef CONFIG_DRIVER_BSD
+LIBS += -ldl
+endif
+endif
+
+ifdef CONFIG_MACSEC
+CFLAGS += -DCONFIG_MACSEC
+OBJS += ../src/ap/wpa_auth_kay.o
+OBJS += ../src/pae/ieee802_1x_cp.o
+OBJS += ../src/pae/ieee802_1x_kay.o
+OBJS += ../src/pae/ieee802_1x_key.o
+OBJS += ../src/pae/ieee802_1x_secy_ops.o
+endif
+
+# Basic EAP functionality is needed for EAPOL
+OBJS += eap_register.o
+OBJS += ../src/eap_server/eap_server.o
+OBJS += ../src/eap_common/eap_common.o
+OBJS += ../src/eap_server/eap_server_methods.o
+OBJS += ../src/eap_server/eap_server_identity.o
+CFLAGS += -DEAP_SERVER_IDENTITY
+
+ifdef CONFIG_EAP
+CFLAGS += -DEAP_SERVER
+endif
+
+ifdef CONFIG_PKCS12
+CFLAGS += -DPKCS12_FUNCS
+endif
+
+ifdef NEED_DRAGONFLY
+OBJS += ../src/common/dragonfly.o
+endif
+
+ifdef MS_FUNCS
+OBJS += ../src/crypto/ms_funcs.o
+NEED_DES=y
+NEED_MD4=y
+endif
+
+ifdef CHAP
+OBJS += ../src/eap_common/chap.o
+endif
+
+ifdef TLS_FUNCS
+NEED_DES=y
+# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, and EAP_TTLS)
+CFLAGS += -DEAP_TLS_FUNCS
+OBJS += ../src/eap_server/eap_server_tls_common.o
+NEED_TLS_PRF=y
+endif
+
+ifndef CONFIG_TLS
+CONFIG_TLS=openssl
+endif
+
+ifdef CONFIG_TLSV11
+CFLAGS += -DCONFIG_TLSV11
+endif
+
+ifdef CONFIG_TLSV12
+CFLAGS += -DCONFIG_TLSV12
+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_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
+LIBS += -lssl
+endif
+OBJS += ../src/crypto/crypto_openssl.o
+HOBJS += ../src/crypto/crypto_openssl.o
+SOBJS += ../src/crypto/crypto_openssl.o
+ifdef NEED_FIPS186_2_PRF
+OBJS += ../src/crypto/fips_prf_openssl.o
+endif
+NEED_TLS_PRF_SHA256=y
+LIBS += -lcrypto
+LIBS_h += -lcrypto
+LIBS_n += -lcrypto
+LIBS_s += -lcrypto
+ifdef CONFIG_TLS_ADD_DL
+LIBS += -ldl
+LIBS_h += -ldl
+LIBS_s += -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_$(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
+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
+CONFIG_CRYPTO=internal
+endif
+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/rsa.o
+OBJS += ../src/tls/x509v3.o
+OBJS += ../src/tls/pkcs1.o
+OBJS += ../src/tls/pkcs5.o
+OBJS += ../src/tls/pkcs8.o
+NEED_ASN1=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_CIPHER
+NEED_DES=y
+OBJS += ../src/crypto/crypto_internal-cipher.o
+endif
+ifdef NEED_MODEXP
+OBJS += ../src/crypto/crypto_internal-modexp.o
+OBJS += ../src/tls/bignum.o
+endif
+ifeq ($(CONFIG_CRYPTO), libtomcrypt)
+OBJS += ../src/crypto/crypto_libtomcrypt.o
+LIBS += -ltomcrypt -ltfm
+LIBS_h += -ltomcrypt -ltfm
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+ifeq ($(CONFIG_CRYPTO), internal)
+OBJS += ../src/crypto/crypto_internal.o
+NEED_AES_DEC=y
+CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+ifdef CONFIG_INTERNAL_LIBTOMMATH
+CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST
+CFLAGS += -DLTM_FAST
+endif
+else
+LIBS += -ltommath
+LIBS_h += -ltommath
+endif
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_DES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD4=y
+CONFIG_INTERNAL_MD5=y
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_SHA384=y
+CONFIG_INTERNAL_SHA512=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+ifeq ($(CONFIG_CRYPTO), cryptoapi)
+OBJS += ../src/crypto/crypto_cryptoapi.o
+OBJS_p += ../src/crypto/crypto_cryptoapi.o
+CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI
+CONFIG_INTERNAL_SHA256=y
+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/rsa.o
+OBJS += ../src/tls/x509v3.o
+OBJS += ../src/tls/pkcs1.o
+OBJS += ../src/tls/pkcs5.o
+OBJS += ../src/tls/pkcs8.o
+NEED_ASN1=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
+CFLAGS += -DEAP_TLS_NONE
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD5=y
+endif
+OBJS += ../src/crypto/crypto_none.o
+OBJS_p += ../src/crypto/crypto_none.o
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+endif
+
+ifndef TLS_FUNCS
+OBJS += ../src/crypto/tls_none.o
+ifeq ($(CONFIG_TLS), internal)
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD5=y
+CONFIG_INTERNAL_RC4=y
+endif
+endif
+
+AESOBJS = # none so far
+ifdef CONFIG_INTERNAL_AES
+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
+endif
+ifdef NEED_AES_SIV
+AESOBJS += ../src/crypto/aes-siv.o
+NEED_AES_CTR=y
+endif
+ifdef NEED_AES_CTR
+AESOBJS += ../src/crypto/aes-ctr.o
+endif
+ifdef NEED_AES_ENCBLOCK
+AESOBJS += ../src/crypto/aes-encblock.o
+endif
+ifneq ($(CONFIG_TLS), linux)
+ifneq ($(CONFIG_TLS), wolfssl)
+AESOBJS += ../src/crypto/aes-omac1.o
+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
+endif
+endif
+ifdef NEED_AES
+OBJS += $(AESOBJS)
+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
+ifdef NEED_FIPS186_2_PRF
+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
+ifdef NEED_TLS_PRF
+SHA1OBJS += ../src/crypto/sha1-tlsprf.o
+endif
+endif
+
+ifdef NEED_SHA1
+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
+OBJS += ../src/crypto/md5-internal.o
+HOBJS += ../src/crypto/md5-internal.o
+endif
+endif
+
+ifdef NEED_MD4
+ifdef CONFIG_INTERNAL_MD4
+OBJS += ../src/crypto/md4-internal.o
+endif
+endif
+
+ifdef NEED_DES
+CFLAGS += -DCONFIG_DES
+ifdef CONFIG_INTERNAL_DES
+OBJS += ../src/crypto/des-internal.o
+endif
+endif
+
+ifdef CONFIG_NO_RC4
+CFLAGS += -DCONFIG_NO_RC4
+endif
+
+ifdef NEED_RC4
+ifdef CONFIG_INTERNAL_RC4
+ifndef CONFIG_NO_RC4
+OBJS += ../src/crypto/rc4.o
+endif
+endif
+endif
+
+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
+endif
+ifdef NEED_TLS_PRF_SHA256
+OBJS += ../src/crypto/sha256-tlsprf.o
+endif
+ifdef NEED_TLS_PRF_SHA384
+OBJS += ../src/crypto/sha384-tlsprf.o
+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
+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
+OBJS += ../src/crypto/sha384-internal.o
+endif
+
+ifdef CONFIG_INTERNAL_SHA512
+CFLAGS += -DCONFIG_INTERNAL_SHA512
+OBJS += ../src/crypto/sha512-internal.o
+endif
+
+ifdef NEED_ASN1
+OBJS += ../src/tls/asn1.o
+endif
+
+ifdef NEED_DH_GROUPS
+OBJS += ../src/crypto/dh_groups.o
+endif
+ifdef NEED_DH_GROUPS_ALL
+CFLAGS += -DALL_DH_GROUPS
+endif
+ifdef CONFIG_INTERNAL_DH_GROUP5
+ifdef NEED_DH_GROUPS
+OBJS += ../src/crypto/dh_group5.o
+endif
+endif
+
+ifdef NEED_ECC
+CFLAGS += -DCONFIG_ECC
+endif
+
+ifdef CONFIG_NO_RANDOM_POOL
+CFLAGS += -DCONFIG_NO_RANDOM_POOL
+else
+ifdef CONFIG_GETRANDOM
+CFLAGS += -DCONFIG_GETRANDOM
+endif
+OBJS += ../src/crypto/random.o
+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
+OBJS += ../src/radius/radius_server.o
+endif
+
+ifdef CONFIG_IPV6
+CFLAGS += -DCONFIG_IPV6
+endif
+
+ifdef CONFIG_DRIVER_RADIUS_ACL
+CFLAGS += -DCONFIG_DRIVER_RADIUS_ACL
+endif
+
+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
+OBJS += ../src/ap/ieee802_11.o
+OBJS += ../src/ap/hw_features.o
+OBJS += ../src/ap/dfs.o
+CFLAGS += -DNEED_AP_MLME
+endif
+OBJS += ../src/ap/ieee802_11_ht.o
+
+ifdef CONFIG_IEEE80211AC
+OBJS += ../src/ap/ieee802_11_vht.o
+endif
+
+ifdef CONFIG_P2P_MANAGER
+CFLAGS += -DCONFIG_P2P_MANAGER
+OBJS += ../src/ap/p2p_hostapd.o
+endif
+
+ifdef CONFIG_HS20
+CFLAGS += -DCONFIG_HS20
+OBJS += ../src/ap/hs20.o
+CONFIG_INTERWORKING=y
+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
+
+ifdef CONFIG_PROXYARP
+CFLAGS += -DCONFIG_PROXYARP
+OBJS += ../src/ap/x_snoop.o
+OBJS += ../src/ap/dhcp_snoop.o
+ifdef CONFIG_IPV6
+OBJS += ../src/ap/ndisc_snoop.o
+endif
+endif
+
+OBJS += ../src/drivers/driver_common.o
+
+ifdef CONFIG_WPA_CLI_EDIT
+OBJS_c += ../src/utils/edit.o
+else
+OBJS_c += ../src/utils/edit_simple.o
+endif
+
+ifdef CONFIG_ACS
+CFLAGS += -DCONFIG_ACS
+OBJS += ../src/ap/acs.o
+LIBS += -lm
+endif
+
+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
+
+ifdef CONFIG_DEBUG_FILE
+CFLAGS += -DCONFIG_DEBUG_FILE
+endif
+
+ifdef CONFIG_SQLITE
+CFLAGS += -DCONFIG_SQLITE
+LIBS += -lsqlite3
+LIBS_h += -lsqlite3
+endif
+
+ifdef CONFIG_FST
+CFLAGS += -DCONFIG_FST
+OBJS += ../src/fst/fst.o
+OBJS += ../src/fst/fst_group.o
+OBJS += ../src/fst/fst_iface.o
+OBJS += ../src/fst/fst_session.o
+OBJS += ../src/fst/fst_ctrl_aux.o
+ifdef CONFIG_FST_TEST
+CFLAGS += -DCONFIG_FST_TEST
+endif
+ifndef CONFIG_NO_CTRL_IFACE
+OBJS += ../src/fst/fst_ctrl_iface.o
+endif
+endif
+
+ifdef CONFIG_WEP
+CFLAGS += -DCONFIG_WEP
+endif
+
+ifdef CONFIG_NO_TKIP
+CFLAGS += -DCONFIG_NO_TKIP
+endif
+
+$(DESTDIR)$(BINDIR)/%: %
+ install -D $(<) $(@)
+
+install: $(addprefix $(DESTDIR)$(BINDIR)/,$(ALL))
+
+_OBJS_VAR := OBJS
+include ../src/objs.mk
+
+hostapd: $(OBJS)
+ $(Q)$(CC) $(LDFLAGS) -o hostapd $(OBJS) $(LIBS)
+ @$(E) " LD " $@
+
+ifdef CONFIG_WPA_TRACE
+OBJS_c += ../src/utils/trace.o
+endif
+
+_OBJS_VAR := OBJS_c
+include ../src/objs.mk
+
+hostapd_cli: $(OBJS_c)
+ $(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
+ @$(E) " LD " $@
+
+NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS)
+NOBJS += ../src/utils/common.o
+ifdef NEED_RC4
+ifdef CONFIG_INTERNAL_RC4
+ifndef CONFIG_NO_RC4
+NOBJS += ../src/crypto/rc4.o
+endif
+endif
+endif
+ifdef CONFIG_INTERNAL_MD5
+NOBJS += ../src/crypto/md5-internal.o
+endif
+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
+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
+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
+
+SOBJS += sae_pk_gen.o
+SOBJS += ../src/utils/common.o
+SOBJS += ../src/utils/os_$(CONFIG_OS).o
+SOBJS += ../src/utils/base64.o
+SOBJS += ../src/utils/wpa_debug.o
+SOBJS += ../src/utils/wpabuf.o
+ifdef CONFIG_WPA_TRACE
+SOBJS += ../src/utils/trace.o
+endif
+SOBJS += ../src/common/ieee802_11_common.o
+SOBJS += ../src/common/sae.o
+SOBJS += ../src/common/sae_pk.o
+SOBJS += ../src/common/dragonfly.o
+SOBJS += $(AESOBJS)
+SOBJS += ../src/crypto/sha256-prf.o
+SOBJS += ../src/crypto/sha384-prf.o
+SOBJS += ../src/crypto/sha512-prf.o
+SOBJS += ../src/crypto/dh_groups.o
+SOBJS += ../src/crypto/sha256-kdf.o
+SOBJS += ../src/crypto/sha384-kdf.o
+SOBJS += ../src/crypto/sha512-kdf.o
+
+_OBJS_VAR := NOBJS
+include ../src/objs.mk
+_OBJS_VAR := HOBJS
+include ../src/objs.mk
+_OBJS_VAR := SOBJS
+include ../src/objs.mk
+
+nt_password_hash: $(NOBJS)
+ $(Q)$(CC) $(LDFLAGS) -o nt_password_hash $(NOBJS) $(LIBS_n)
+ @$(E) " LD " $@
+
+hlr_auc_gw: $(HOBJS)
+ $(Q)$(CC) $(LDFLAGS) -o hlr_auc_gw $(HOBJS) $(LIBS_h)
+ @$(E) " LD " $@
+
+sae_pk_gen: $(SOBJS)
+ $(Q)$(CC) $(LDFLAGS) -o sae_pk_gen $(SOBJS) $(LIBS_s)
+ @$(E) " LD " $@
+
+.PHONY: lcov-html
+lcov-html:
+ lcov -c -d $(BUILDDIR) > lcov.info
+ genhtml lcov.info --output-directory lcov-html
+
+clean: common-clean
+ rm -f core *~ nt_password_hash hlr_auc_gw
+ rm -f sae_pk_gen
+ rm -f lcov.info
+ rm -rf lcov-html
diff --git a/contrib/wpa/hostapd/android.config b/contrib/wpa/hostapd/android.config
new file mode 100644
index 00000000000..c8b3afabef8
--- /dev/null
+++ b/contrib/wpa/hostapd/android.config
@@ -0,0 +1,214 @@
+# Example hostapd 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.
+#
+# This file is included in Makefile, so variables like CFLAGS and LIBS can also
+# be modified from here. In most cass, these lines should use += in order not
+# to override previous values of the variables.
+
+# Driver interface for Host AP driver
+#CONFIG_DRIVER_HOSTAP=y
+
+# Driver interface for wired authenticator
+#CONFIG_DRIVER_WIRED=y
+
+# Driver interface for drivers using the nl80211 kernel interface
+#CONFIG_DRIVER_NL80211=y
+# driver_nl80211.c requires a rather new libnl (version 1.1) which may not be
+# shipped with your distribution yet. If that is the case, you need to build
+# newer libnl version and point the hostapd build to use it.
+#LIBNL=/usr/src/libnl
+#CFLAGS += -I$(LIBNL)/include
+#LIBS += -L$(LIBNL)/lib
+CONFIG_LIBNL20=y
+
+# QCA vendor extensions to nl80211
+CONFIG_DRIVER_NL80211_QCA=y
+
+# Broadcom vendor extensions to nl80211
+#CONFIG_DRIVER_NL80211_BRCM=y
+
+# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
+#CONFIG_DRIVER_BSD=y
+#CFLAGS += -I/usr/local/include
+#LIBS += -L/usr/local/lib
+#LIBS_p += -L/usr/local/lib
+#LIBS_c += -L/usr/local/lib
+
+# Driver interface for no driver (e.g., RADIUS server only)
+#CONFIG_DRIVER_NONE=y
+
+# WPA2/IEEE 802.11i RSN pre-authentication
+#CONFIG_RSN_PREAUTH=y
+
+# Support Operating Channel Validation
+#CONFIG_OCV=y
+
+# Integrated EAP server
+#CONFIG_EAP=y
+
+# EAP-MD5 for the integrated EAP server
+#CONFIG_EAP_MD5=y
+
+# EAP-TLS for the integrated EAP server
+#CONFIG_EAP_TLS=y
+
+# EAP-MSCHAPv2 for the integrated EAP server
+#CONFIG_EAP_MSCHAPV2=y
+
+# EAP-PEAP for the integrated EAP server
+#CONFIG_EAP_PEAP=y
+
+# EAP-GTC for the integrated EAP server
+#CONFIG_EAP_GTC=y
+
+# EAP-TTLS for the integrated EAP server
+#CONFIG_EAP_TTLS=y
+
+# EAP-SIM for the integrated EAP server
+#CONFIG_EAP_SIM=y
+
+# EAP-AKA for the integrated EAP server
+#CONFIG_EAP_AKA=y
+
+# EAP-AKA' for the integrated EAP server
+# This requires CONFIG_EAP_AKA to be enabled, too.
+#CONFIG_EAP_AKA_PRIME=y
+
+# EAP-PAX for the integrated EAP server
+#CONFIG_EAP_PAX=y
+
+# EAP-PSK for the integrated EAP server (this is _not_ needed for WPA-PSK)
+#CONFIG_EAP_PSK=y
+
+# EAP-SAKE for the integrated EAP server
+#CONFIG_EAP_SAKE=y
+
+# EAP-GPSK for the integrated EAP server
+#CONFIG_EAP_GPSK=y
+# Include support for optional SHA256 cipher suite in EAP-GPSK
+#CONFIG_EAP_GPSK_SHA256=y
+
+# EAP-FAST for the integrated EAP server
+# 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.9-session-ticket.patch)
+# to add the needed functions.
+#CONFIG_EAP_FAST=y
+
+# Wi-Fi Protected Setup (WPS)
+CONFIG_WPS=y
+# Enable UPnP support for external WPS Registrars
+#CONFIG_WPS_UPNP=y
+
+# EAP-IKEv2
+#CONFIG_EAP_IKEV2=y
+
+# Trusted Network Connect (EAP-TNC)
+#CONFIG_EAP_TNC=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
+
+# RADIUS authentication server. This provides access to the integrated EAP
+# server from external hosts using RADIUS.
+#CONFIG_RADIUS_SERVER=y
+
+# Build IPv6 support for RADIUS operations
+CONFIG_IPV6=y
+
+# IEEE Std 802.11r-2008 (Fast BSS Transition)
+#CONFIG_IEEE80211R=y
+
+# Use the hostapd's IEEE 802.11 authentication (ACL), but without
+# the IEEE 802.11 Management capability (e.g., FreeBSD/net80211)
+#CONFIG_DRIVER_RADIUS_ACL=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.
+#CONFIG_NO_STDOUT_DEBUG=y
+
+# Add support for writing debug log to Android logcat instead of standard output
+CONFIG_ANDROID_LOG=y
+
+# Remove support for RADIUS accounting
+#CONFIG_NO_ACCOUNTING=y
+
+# Remove support for RADIUS
+CONFIG_NO_RADIUS=y
+
+# Remove support for VLANs
+#CONFIG_NO_VLAN=y
+
+# Remove support for dumping internal state through control interface commands
+# This can be used to reduce binary size at the cost of disabling a debugging
+# option.
+#CONFIG_NO_DUMP_STATE=y
+
+# Select wrapper for operatins system and C library specific functions
+# unix = UNIX/POSIX like systems (default)
+# win32 = Windows systems
+# none = Empty template
+CONFIG_OS=unix
+
+# Enable tracing code for developer debugging
+# This tracks use of memory allocations and other registrations and reports
+# incorrect use with a backtrace of call (or allocation) location.
+#CONFIG_WPA_TRACE=y
+# For BSD, comment out these.
+#LIBS += -lexecinfo
+#LIBS_p += -lexecinfo
+#LIBS_c += -lexecinfo
+
+# Use libbfd to get more details for developer debugging
+# This enables use of libbfd to get more detailed symbols for the backtraces
+# generated by CONFIG_WPA_TRACE=y.
+#CONFIG_WPA_TRACE_BFD=y
+# For BSD, comment out these.
+#LIBS += -lbfd -liberty -lz
+#LIBS_p += -lbfd -liberty -lz
+#LIBS_c += -lbfd -liberty -lz
+
+# Should we use poll instead of select? Select is used by default.
+#CONFIG_ELOOP_POLL=y
+
+# Should we use epoll instead of select? Select is used by default.
+#CONFIG_ELOOP_EPOLL=y
+
+# Enable AP
+CONFIG_AP=y
+
+# Enable Fast Session Transfer (FST)
+#CONFIG_FST=y
+
+# Multiband Operation support
+# These extensions 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
+
+# Wired equivalent privacy (WEP)
+# WEP is an obsolete cryptographic data confidentiality algorithm that is not
+# considered secure. It should not be used for anything anymore. The
+# functionality needed to use WEP is available in the current hostapd
+# release under this optional build parameter. This functionality is subject to
+# be completely removed in a future release.
+CONFIG_WEP=y
diff --git a/contrib/wpa/hostapd/config_file.c b/contrib/wpa/hostapd/config_file.c
index e09e6e141ba..9bc1dc7756e 100644
--- a/contrib/wpa/hostapd/config_file.c
+++ b/contrib/wpa/hostapd/config_file.c
@@ -14,6 +14,7 @@
#include "utils/common.h"
#include "utils/uuid.h"
#include "common/ieee802_11_defs.h"
+#include "common/sae.h"
#include "crypto/sha256.h"
#include "crypto/tls.h"
#include "drivers/driver.h"
@@ -340,7 +341,7 @@ static int hostapd_config_read_eap_user(const char *fname,
struct hostapd_radius_attr *attr, *a;
attr = hostapd_parse_radius_attr(buf + 19);
if (attr == NULL) {
- wpa_printf(MSG_ERROR, "Invalid radius_auth_req_attr: %s",
+ wpa_printf(MSG_ERROR, "Invalid radius_accept_attr: %s",
buf + 19);
user = NULL; /* already in the BSS list */
goto failed;
@@ -711,12 +712,10 @@ static int hostapd_config_parse_key_mgmt(int line, const char *value)
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;
else if (os_strcmp(start, "WPA-EAP-SHA256") == 0)
val |= WPA_KEY_MGMT_IEEE8021X_SHA256;
-#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_SAE
else if (os_strcmp(start, "SAE") == 0)
val |= WPA_KEY_MGMT_SAE;
@@ -755,6 +754,10 @@ static int hostapd_config_parse_key_mgmt(int line, const char *value)
else if (os_strcmp(start, "OSEN") == 0)
val |= WPA_KEY_MGMT_OSEN;
#endif /* CONFIG_HS20 */
+#ifdef CONFIG_PASN
+ else if (os_strcmp(start, "PASN") == 0)
+ val |= WPA_KEY_MGMT_PASN;
+#endif /* CONFIG_PASN */
else {
wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
line, start);
@@ -795,6 +798,7 @@ static int hostapd_config_parse_cipher(int line, const char *value)
}
+#ifdef CONFIG_WEP
static int hostapd_config_read_wep(struct hostapd_wep_keys *wep, int keyidx,
char *val)
{
@@ -845,6 +849,7 @@ static int hostapd_config_read_wep(struct hostapd_wep_keys *wep, int keyidx,
return 0;
}
+#endif /* CONFIG_WEP */
static int hostapd_parse_chanlist(struct hostapd_config *conf, char *val)
@@ -942,104 +947,6 @@ static int hostapd_config_bss(struct hostapd_config *conf, const char *ifname)
}
-/* convert floats with one decimal place to value*10 int, i.e.,
- * "1.5" will return 15 */
-static int hostapd_config_read_int10(const char *value)
-{
- int i, d;
- char *pos;
-
- i = atoi(value);
- pos = os_strchr(value, '.');
- d = 0;
- if (pos) {
- pos++;
- if (*pos >= '0' && *pos <= '9')
- d = *pos - '0';
- }
-
- return i * 10 + d;
-}
-
-
-static int valid_cw(int cw)
-{
- return (cw == 1 || cw == 3 || cw == 7 || cw == 15 || cw == 31 ||
- cw == 63 || cw == 127 || cw == 255 || cw == 511 || cw == 1023 ||
- cw == 2047 || cw == 4095 || cw == 8191 || cw == 16383 ||
- cw == 32767);
-}
-
-
-enum {
- IEEE80211_TX_QUEUE_DATA0 = 0, /* used for EDCA AC_VO data */
- IEEE80211_TX_QUEUE_DATA1 = 1, /* used for EDCA AC_VI data */
- IEEE80211_TX_QUEUE_DATA2 = 2, /* used for EDCA AC_BE data */
- IEEE80211_TX_QUEUE_DATA3 = 3 /* used for EDCA AC_BK data */
-};
-
-static int hostapd_config_tx_queue(struct hostapd_config *conf,
- const char *name, const char *val)
-{
- int num;
- const char *pos;
- struct hostapd_tx_queue_params *queue;
-
- /* skip 'tx_queue_' prefix */
- pos = name + 9;
- if (os_strncmp(pos, "data", 4) == 0 &&
- pos[4] >= '0' && pos[4] <= '9' && pos[5] == '_') {
- num = pos[4] - '0';
- pos += 6;
- } else if (os_strncmp(pos, "after_beacon_", 13) == 0 ||
- os_strncmp(pos, "beacon_", 7) == 0) {
- wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
- return 0;
- } else {
- wpa_printf(MSG_ERROR, "Unknown tx_queue name '%s'", pos);
- return -1;
- }
-
- if (num >= NUM_TX_QUEUES) {
- /* for backwards compatibility, do not trigger failure */
- wpa_printf(MSG_INFO, "DEPRECATED: '%s' not used", name);
- return 0;
- }
-
- queue = &conf->tx_queue[num];
-
- if (os_strcmp(pos, "aifs") == 0) {
- queue->aifs = atoi(val);
- if (queue->aifs < 0 || queue->aifs > 255) {
- wpa_printf(MSG_ERROR, "Invalid AIFS value %d",
- queue->aifs);
- return -1;
- }
- } else if (os_strcmp(pos, "cwmin") == 0) {
- queue->cwmin = atoi(val);
- if (!valid_cw(queue->cwmin)) {
- wpa_printf(MSG_ERROR, "Invalid cwMin value %d",
- queue->cwmin);
- return -1;
- }
- } else if (os_strcmp(pos, "cwmax") == 0) {
- queue->cwmax = atoi(val);
- if (!valid_cw(queue->cwmax)) {
- wpa_printf(MSG_ERROR, "Invalid cwMax value %d",
- queue->cwmax);
- return -1;
- }
- } else if (os_strcmp(pos, "burst") == 0) {
- queue->burst = hostapd_config_read_int10(val);
- } else {
- wpa_printf(MSG_ERROR, "Unknown tx_queue field '%s'", pos);
- return -1;
- }
-
- return 0;
-}
-
-
#ifdef CONFIG_IEEE80211R_AP
static int rkh_derive_key(const char *pos, u8 *key, size_t key_len)
@@ -1153,7 +1060,6 @@ static int add_r1kh(struct hostapd_bss_config *bss, char *value)
#endif /* CONFIG_IEEE80211R_AP */
-#ifdef CONFIG_IEEE80211N
static int hostapd_config_ht_capab(struct hostapd_config *conf,
const char *capab)
{
@@ -1173,14 +1079,6 @@ static int hostapd_config_ht_capab(struct hostapd_config *conf,
}
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;
- }
- if (os_strstr(capab, "[SMPS-DYNAMIC]")) {
- conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK;
- conf->ht_capab |= HT_CAP_INFO_SMPS_DYNAMIC;
- }
if (os_strstr(capab, "[GF]"))
conf->ht_capab |= HT_CAP_INFO_GREEN_FIELD;
if (os_strstr(capab, "[SHORT-GI-20]"))
@@ -1214,7 +1112,6 @@ static int hostapd_config_ht_capab(struct hostapd_config *conf,
return 0;
}
-#endif /* CONFIG_IEEE80211N */
#ifdef CONFIG_IEEE80211AC
@@ -1323,6 +1220,32 @@ static u8 set_he_cap(int val, u8 mask)
return (u8) (mask & (val << find_bit_offset(mask)));
}
+
+static int hostapd_parse_he_srg_bitmap(u8 *bitmap, char *val)
+{
+ int bitpos;
+ char *pos, *end;
+
+ os_memset(bitmap, 0, 8);
+ pos = val;
+ while (*pos != '\0') {
+ end = os_strchr(pos, ' ');
+ if (end)
+ *end = '\0';
+
+ bitpos = atoi(pos);
+ if (bitpos < 0 || bitpos > 64)
+ return -1;
+
+ bitmap[bitpos / 8] |= BIT(bitpos % 8);
+ if (!end)
+ break;
+ pos = end + 1;
+ }
+
+ return 0;
+}
+
#endif /* CONFIG_IEEE80211AX */
@@ -2300,6 +2223,35 @@ static int parse_sae_password(struct hostapd_bss_config *bss, const char *val)
pw->vlan_id = atoi(pos2);
}
+#ifdef CONFIG_SAE_PK
+ pos2 = os_strstr(pos, "|pk=");
+ if (pos2) {
+ const char *epos;
+ char *tmp;
+
+ if (!end)
+ end = pos2;
+ pos2 += 4;
+ epos = os_strchr(pos2, '|');
+ if (epos) {
+ tmp = os_malloc(epos - pos2 + 1);
+ if (!tmp)
+ goto fail;
+ os_memcpy(tmp, pos2, epos - pos2);
+ tmp[epos - pos2] = '\0';
+ } else {
+ tmp = os_strdup(pos2);
+ if (!tmp)
+ goto fail;
+ }
+
+ pw->pk = sae_parse_pk(tmp);
+ str_clear_free(tmp);
+ if (!pw->pk)
+ goto fail;
+ }
+#endif /* CONFIG_SAE_PK */
+
pos2 = os_strstr(pos, "|id=");
if (pos2) {
if (!end)
@@ -2322,6 +2274,18 @@ static int parse_sae_password(struct hostapd_bss_config *bss, const char *val)
pw->password[end - val] = '\0';
}
+#ifdef CONFIG_SAE_PK
+ if (pw->pk &&
+#ifdef CONFIG_TESTING_OPTIONS
+ !bss->sae_pk_password_check_skip &&
+#endif /* CONFIG_TESTING_OPTIONS */
+ !sae_pk_valid_password(pw->password)) {
+ wpa_printf(MSG_INFO,
+ "Invalid SAE password for a SAE-PK sae_password entry");
+ goto fail;
+ }
+#endif /* CONFIG_SAE_PK */
+
pw->next = bss->sae_passwords;
bss->sae_passwords = pw;
@@ -2329,6 +2293,9 @@ static int parse_sae_password(struct hostapd_bss_config *bss, const char *val)
fail:
str_clear_free(pw->password);
os_free(pw->identifier);
+#ifdef CONFIG_SAE_PK
+ sae_deinit_pk(pw->pk);
+#endif /* CONFIG_SAE_PK */
os_free(pw);
return -1;
}
@@ -2365,6 +2332,22 @@ fail:
#endif /* CONFIG_DPP2 */
+static int get_hex_config(u8 *buf, size_t max_len, int line,
+ const char *field, const char *val)
+{
+ size_t hlen = os_strlen(val), len = hlen / 2;
+ u8 tmp[EXT_CAPA_MAX_LEN];
+
+ os_memset(tmp, 0, EXT_CAPA_MAX_LEN);
+ if (hlen & 1 || len > EXT_CAPA_MAX_LEN || hexstr2bin(val, tmp, len)) {
+ wpa_printf(MSG_ERROR, "Line %d: Invalid %s", line, field);
+ return -1;
+ }
+ os_memcpy(buf, tmp, EXT_CAPA_MAX_LEN);
+ return 0;
+}
+
+
static int hostapd_config_fill(struct hostapd_config *conf,
struct hostapd_bss_config *bss,
const char *buf, char *pos, int line)
@@ -2473,6 +2456,13 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "skip_inactivity_poll") == 0) {
bss->skip_inactivity_poll = atoi(pos);
} else if (os_strcmp(buf, "country_code") == 0) {
+ if (pos[0] < 'A' || pos[0] > 'Z' ||
+ pos[1] < 'A' || pos[1] > 'Z') {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid country_code '%s'",
+ line, pos);
+ return 1;
+ }
os_memcpy(conf->country, pos, 2);
} else if (os_strcmp(buf, "country3") == 0) {
conf->country[2] = strtol(pos, NULL, 16);
@@ -2484,12 +2474,13 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->ieee802_1x = atoi(pos);
} else if (os_strcmp(buf, "eapol_version") == 0) {
int eapol_version = atoi(pos);
-
#ifdef CONFIG_MACSEC
- if (eapol_version < 1 || eapol_version > 3) {
+ int max_ver = 3;
#else /* CONFIG_MACSEC */
- if (eapol_version < 1 || eapol_version > 2) {
+ int max_ver = 2;
#endif /* CONFIG_MACSEC */
+
+ if (eapol_version < 1 || eapol_version > max_ver) {
wpa_printf(MSG_ERROR,
"Line %d: invalid EAPOL version (%d): '%s'.",
line, eapol_version, pos);
@@ -2547,6 +2538,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
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, "max_auth_rounds") == 0) {
+ bss->max_auth_rounds = atoi(pos);
+ } else if (os_strcmp(buf, "max_auth_rounds_short") == 0) {
+ bss->max_auth_rounds_short = atoi(pos);
} else if (os_strcmp(buf, "ocsp_stapling_response") == 0) {
os_free(bss->ocsp_stapling_response);
bss->ocsp_stapling_response = os_strdup(pos);
@@ -2611,7 +2606,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "eap_teap_auth") == 0) {
int val = atoi(pos);
- if (val < 0 || val > 1) {
+ if (val < 0 || val > 2) {
wpa_printf(MSG_ERROR,
"Line %d: Invalid eap_teap_auth value",
line);
@@ -2620,6 +2615,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->eap_teap_auth = val;
} else if (os_strcmp(buf, "eap_teap_pac_no_inner") == 0) {
bss->eap_teap_pac_no_inner = atoi(pos);
+ } else if (os_strcmp(buf, "eap_teap_separate_result") == 0) {
+ bss->eap_teap_separate_result = atoi(pos);
+ } else if (os_strcmp(buf, "eap_teap_id") == 0) {
+ bss->eap_teap_id = atoi(pos);
#endif /* EAP_SERVER_TEAP */
#ifdef EAP_SERVER_SIM
} else if (os_strcmp(buf, "eap_sim_db") == 0) {
@@ -2668,6 +2667,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "erp_domain") == 0) {
os_free(bss->erp_domain);
bss->erp_domain = os_strdup(pos);
+#ifdef CONFIG_WEP
} else if (os_strcmp(buf, "wep_key_len_broadcast") == 0) {
int val = atoi(pos);
@@ -2695,6 +2695,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
line, bss->wep_rekeying_period);
return 1;
}
+#endif /* CONFIG_WEP */
} else if (os_strcmp(buf, "eap_reauth_period") == 0) {
bss->eap_reauth_period = atoi(pos);
if (bss->eap_reauth_period < 0) {
@@ -2706,8 +2707,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->eapol_key_index_workaround = atoi(pos);
#ifdef CONFIG_IAPP
} else if (os_strcmp(buf, "iapp_interface") == 0) {
- bss->ieee802_11f = 1;
- os_strlcpy(bss->iapp_iface, pos, sizeof(bss->iapp_iface));
+ wpa_printf(MSG_INFO, "DEPRECATED: iapp_interface not used");
#endif /* CONFIG_IAPP */
} else if (os_strcmp(buf, "own_ip_addr") == 0) {
if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) {
@@ -2728,6 +2728,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
return 1;
}
bss->radius->force_client_addr = 1;
+ } else if (os_strcmp(buf, "radius_client_dev") == 0) {
+ os_free(bss->radius->force_client_dev);
+ bss->radius->force_client_dev = os_strdup(pos);
} else if (os_strcmp(buf, "auth_server_addr") == 0) {
if (hostapd_config_read_radius_addr(
&bss->radius->auth_servers,
@@ -2870,6 +2873,16 @@ static int hostapd_config_fill(struct hostapd_config *conf,
}
} else if (os_strcmp(buf, "wpa") == 0) {
bss->wpa = atoi(pos);
+ } else if (os_strcmp(buf, "extended_key_id") == 0) {
+ int val = atoi(pos);
+
+ if (val < 0 || val > 2) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid extended_key_id=%d; allowed range 0..2",
+ line, val);
+ return 1;
+ }
+ bss->extended_key_id = val;
} else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
bss->wpa_group_rekey = atoi(pos);
bss->wpa_group_rekey_set = 1;
@@ -2879,6 +2892,15 @@ static int hostapd_config_fill(struct hostapd_config *conf,
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_deny_ptk0_rekey") == 0) {
+ bss->wpa_deny_ptk0_rekey = atoi(pos);
+ if (bss->wpa_deny_ptk0_rekey < 0 ||
+ bss->wpa_deny_ptk0_rekey > 2) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid wpa_deny_ptk0_rekey=%d; allowed range 0..2",
+ line, bss->wpa_deny_ptk0_rekey);
+ return 1;
+ }
} else if (os_strcmp(buf, "wpa_group_update_count") == 0) {
char *endp;
unsigned long val = strtoul(pos, &endp, 0);
@@ -3131,6 +3153,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
}
} else if (os_strcmp(buf, "acs_exclude_dfs") == 0) {
conf->acs_exclude_dfs = atoi(pos);
+ } else if (os_strcmp(buf, "op_class") == 0) {
+ conf->op_class = atoi(pos);
} else if (os_strcmp(buf, "channel") == 0) {
if (os_strcmp(pos, "acs_survey") == 0) {
#ifndef CONFIG_ACS
@@ -3145,12 +3169,25 @@ static int hostapd_config_fill(struct hostapd_config *conf,
conf->channel = atoi(pos);
conf->acs = conf->channel == 0;
}
+ } else if (os_strcmp(buf, "edmg_channel") == 0) {
+ conf->edmg_channel = atoi(pos);
+ } else if (os_strcmp(buf, "enable_edmg") == 0) {
+ conf->enable_edmg = atoi(pos);
} else if (os_strcmp(buf, "chanlist") == 0) {
if (hostapd_parse_chanlist(conf, pos)) {
wpa_printf(MSG_ERROR, "Line %d: invalid channel list",
line);
return 1;
}
+ } else if (os_strcmp(buf, "freqlist") == 0) {
+ if (freq_range_list_parse(&conf->acs_freq_list, pos)) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid frequency list",
+ line);
+ return 1;
+ }
+ conf->acs_freq_list_present = 1;
+ } else if (os_strcmp(buf, "acs_exclude_6ghz_non_psc") == 0) {
+ conf->acs_exclude_6ghz_non_psc = atoi(pos);
} else if (os_strcmp(buf, "beacon_int") == 0) {
int val = atoi(pos);
/* MIB defines range as 1..65535, but very small values
@@ -3272,6 +3309,16 @@ static int hostapd_config_fill(struct hostapd_config *conf,
}
conf->rate_type = BEACON_RATE_VHT;
conf->beacon_rate = val;
+ } else if (os_strncmp(pos, "he:", 3) == 0) {
+ val = atoi(pos + 3);
+ if (val < 0 || val > 11) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid beacon_rate HE-MCS %d",
+ line, val);
+ return 1;
+ }
+ conf->rate_type = BEACON_RATE_HE;
+ conf->beacon_rate = val;
} else {
val = atoi(pos);
if (val < 10 || val > 10000) {
@@ -3292,6 +3339,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->ignore_broadcast_ssid = atoi(pos);
} else if (os_strcmp(buf, "no_probe_resp_if_max_sta") == 0) {
bss->no_probe_resp_if_max_sta = atoi(pos);
+#ifdef CONFIG_WEP
} else if (os_strcmp(buf, "wep_default_key") == 0) {
bss->ssid.wep.idx = atoi(pos);
if (bss->ssid.wep.idx > 3) {
@@ -3310,6 +3358,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
line, buf);
return 1;
}
+#endif /* CONFIG_WEP */
#ifndef CONFIG_NO_VLAN
} else if (os_strcmp(buf, "dynamic_vlan") == 0) {
bss->ssid.dynamic_vlan = atoi(pos);
@@ -3341,7 +3390,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "ap_table_expiration_time") == 0) {
conf->ap_table_expiration_time = atoi(pos);
} else if (os_strncmp(buf, "tx_queue_", 9) == 0) {
- if (hostapd_config_tx_queue(conf, buf, pos)) {
+ if (hostapd_config_tx_queue(conf->tx_queue, buf, pos)) {
wpa_printf(MSG_ERROR, "Line %d: invalid TX queue item",
line);
return 1;
@@ -3372,7 +3421,6 @@ static int hostapd_config_fill(struct hostapd_config *conf,
}
} else if (os_strcmp(buf, "use_driver_iface_addr") == 0) {
conf->use_driver_iface_addr = atoi(pos);
-#ifdef CONFIG_IEEE80211W
} else if (os_strcmp(buf, "ieee80211w") == 0) {
bss->ieee80211w = atoi(pos);
} else if (os_strcmp(buf, "group_mgmt_cipher") == 0) {
@@ -3389,6 +3437,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
line, pos);
return 1;
}
+ } else if (os_strcmp(buf, "beacon_prot") == 0) {
+ bss->beacon_prot = atoi(pos);
} else if (os_strcmp(buf, "assoc_sa_query_max_timeout") == 0) {
bss->assoc_sa_query_max_timeout = atoi(pos);
if (bss->assoc_sa_query_max_timeout == 0) {
@@ -3403,14 +3453,12 @@ static int hostapd_config_fill(struct hostapd_config *conf,
line);
return 1;
}
-#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_OCV
} else if (os_strcmp(buf, "ocv") == 0) {
bss->ocv = atoi(pos);
if (bss->ocv && !bss->ieee80211w)
bss->ieee80211w = 1;
#endif /* CONFIG_OCV */
-#ifdef CONFIG_IEEE80211N
} else if (os_strcmp(buf, "ieee80211n") == 0) {
conf->ieee80211n = atoi(pos);
} else if (os_strcmp(buf, "ht_capab") == 0) {
@@ -3423,7 +3471,6 @@ static int hostapd_config_fill(struct hostapd_config *conf,
conf->require_ht = atoi(pos);
} else if (os_strcmp(buf, "obss_interval") == 0) {
conf->obss_interval = atoi(pos);
-#endif /* CONFIG_IEEE80211N */
#ifdef CONFIG_IEEE80211AC
} else if (os_strcmp(buf, "ieee80211ac") == 0) {
conf->ieee80211ac = atoi(pos);
@@ -3456,11 +3503,16 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} 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);
+ conf->he_op.he_bss_color = atoi(pos) & 0x3f;
+ conf->he_op.he_bss_color_disabled = 0;
+ } else if (os_strcmp(buf, "he_bss_color_partial") == 0) {
+ conf->he_op.he_bss_color_partial = 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_twt_responder") == 0) {
+ conf->he_op.he_twt_responder = atoi(pos);
} else if (os_strcmp(buf, "he_rts_threshold") == 0) {
conf->he_op.he_rts_threshold = atoi(pos);
} else if (os_strcmp(buf, "he_basic_mcs_nss_set") == 0) {
@@ -3550,19 +3602,53 @@ static int hostapd_config_fill(struct hostapd_config *conf,
conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_TIMER_IDX] =
atoi(pos) & 0xff;
} else if (os_strcmp(buf, "he_spr_sr_control") == 0) {
- conf->spr.sr_control = atoi(pos) & 0xff;
+ conf->spr.sr_control = atoi(pos) & 0x1f;
} else if (os_strcmp(buf, "he_spr_non_srg_obss_pd_max_offset") == 0) {
conf->spr.non_srg_obss_pd_max_offset = atoi(pos);
} else if (os_strcmp(buf, "he_spr_srg_obss_pd_min_offset") == 0) {
conf->spr.srg_obss_pd_min_offset = atoi(pos);
} else if (os_strcmp(buf, "he_spr_srg_obss_pd_max_offset") == 0) {
conf->spr.srg_obss_pd_max_offset = atoi(pos);
+ } else if (os_strcmp(buf, "he_spr_srg_bss_colors") == 0) {
+ if (hostapd_parse_he_srg_bitmap(
+ conf->spr.srg_bss_color_bitmap, pos)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid srg bss colors list '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "he_spr_srg_partial_bssid") == 0) {
+ if (hostapd_parse_he_srg_bitmap(
+ conf->spr.srg_partial_bssid_bitmap, pos)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid srg partial bssid list '%s'",
+ line, pos);
+ return 1;
+ }
} else if (os_strcmp(buf, "he_oper_chwidth") == 0) {
conf->he_oper_chwidth = atoi(pos);
} else if (os_strcmp(buf, "he_oper_centr_freq_seg0_idx") == 0) {
conf->he_oper_centr_freq_seg0_idx = atoi(pos);
} else if (os_strcmp(buf, "he_oper_centr_freq_seg1_idx") == 0) {
conf->he_oper_centr_freq_seg1_idx = atoi(pos);
+ } else if (os_strcmp(buf, "he_6ghz_max_mpdu") == 0) {
+ conf->he_6ghz_max_mpdu = atoi(pos);
+ } else if (os_strcmp(buf, "he_6ghz_max_ampdu_len_exp") == 0) {
+ conf->he_6ghz_max_ampdu_len_exp = atoi(pos);
+ } else if (os_strcmp(buf, "he_6ghz_rx_ant_pat") == 0) {
+ conf->he_6ghz_rx_ant_pat = atoi(pos);
+ } else if (os_strcmp(buf, "he_6ghz_tx_ant_pat") == 0) {
+ conf->he_6ghz_tx_ant_pat = atoi(pos);
+ } else if (os_strcmp(buf, "unsol_bcast_probe_resp_interval") == 0) {
+ int val = atoi(pos);
+
+ if (val < 0 || val > 20) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: invalid unsol_bcast_probe_resp_interval value",
+ line);
+ return 1;
+ }
+ bss->unsol_bcast_probe_resp_interval = val;
#endif /* CONFIG_IEEE80211AX */
} else if (os_strcmp(buf, "max_listen_interval") == 0) {
bss->max_listen_interval = atoi(pos);
@@ -3744,6 +3830,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "server_id") == 0) {
os_free(bss->server_id);
bss->server_id = os_strdup(pos);
+ } else if (os_strcmp(buf, "wps_application_ext") == 0) {
+ wpabuf_free(bss->wps_application_ext);
+ bss->wps_application_ext = wpabuf_parse_bin(pos);
#ifdef CONFIG_WPS_NFC
} else if (os_strcmp(buf, "wps_nfc_dev_pw_id") == 0) {
bss->wps_nfc_dev_pw_id = atoi(pos);
@@ -4144,9 +4233,53 @@ static int hostapd_config_fill(struct hostapd_config *conf,
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_status") == 0) {
+ bss->sae_commit_status = atoi(pos);
+ } else if (os_strcmp(buf, "sae_pk_omit") == 0) {
+ bss->sae_pk_omit = atoi(pos);
+ } else if (os_strcmp(buf, "sae_pk_password_check_skip") == 0) {
+ bss->sae_pk_password_check_skip = 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);
+ } else if (os_strcmp(buf, "rsne_override_eapol") == 0) {
+ wpabuf_free(bss->rsne_override_eapol);
+ bss->rsne_override_eapol = wpabuf_parse_bin(pos);
+ } else if (os_strcmp(buf, "rsnxe_override_eapol") == 0) {
+ wpabuf_free(bss->rsnxe_override_eapol);
+ bss->rsnxe_override_eapol = wpabuf_parse_bin(pos);
+ } else if (os_strcmp(buf, "rsne_override_ft") == 0) {
+ wpabuf_free(bss->rsne_override_ft);
+ bss->rsne_override_ft = wpabuf_parse_bin(pos);
+ } else if (os_strcmp(buf, "rsnxe_override_ft") == 0) {
+ wpabuf_free(bss->rsnxe_override_ft);
+ bss->rsnxe_override_ft = wpabuf_parse_bin(pos);
+ } else if (os_strcmp(buf, "gtk_rsc_override") == 0) {
+ wpabuf_free(bss->gtk_rsc_override);
+ bss->gtk_rsc_override = wpabuf_parse_bin(pos);
+ } else if (os_strcmp(buf, "igtk_rsc_override") == 0) {
+ wpabuf_free(bss->igtk_rsc_override);
+ bss->igtk_rsc_override = wpabuf_parse_bin(pos);
+ } else if (os_strcmp(buf, "no_beacon_rsnxe") == 0) {
+ bss->no_beacon_rsnxe = atoi(pos);
+ } else if (os_strcmp(buf, "skip_prune_assoc") == 0) {
+ bss->skip_prune_assoc = atoi(pos);
+ } else if (os_strcmp(buf, "ft_rsnxe_used") == 0) {
+ bss->ft_rsnxe_used = atoi(pos);
+ } else if (os_strcmp(buf, "oci_freq_override_eapol_m3") == 0) {
+ bss->oci_freq_override_eapol_m3 = atoi(pos);
+ } else if (os_strcmp(buf, "oci_freq_override_eapol_g1") == 0) {
+ bss->oci_freq_override_eapol_g1 = atoi(pos);
+ } else if (os_strcmp(buf, "oci_freq_override_saquery_req") == 0) {
+ bss->oci_freq_override_saquery_req = atoi(pos);
+ } else if (os_strcmp(buf, "oci_freq_override_saquery_resp") == 0) {
+ bss->oci_freq_override_saquery_resp = atoi(pos);
+ } else if (os_strcmp(buf, "oci_freq_override_ft_assoc") == 0) {
+ bss->oci_freq_override_ft_assoc = atoi(pos);
+ } else if (os_strcmp(buf, "oci_freq_override_fils_assoc") == 0) {
+ bss->oci_freq_override_fils_assoc = atoi(pos);
+ } else if (os_strcmp(buf, "oci_freq_override_wnm_sleep") == 0) {
+ bss->oci_freq_override_wnm_sleep = atoi(pos);
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_SAE
} else if (os_strcmp(buf, "sae_password") == 0) {
@@ -4162,8 +4295,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "assocresp_elements") == 0) {
if (parse_wpabuf_hex(line, buf, &bss->assocresp_elements, pos))
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_anti_clogging_threshold") == 0 ||
+ os_strcmp(buf, "anti_clogging_threshold") == 0) {
+ bss->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) {
@@ -4175,6 +4309,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
}
} else if (os_strcmp(buf, "sae_require_mfp") == 0) {
bss->sae_require_mfp = atoi(pos);
+ } else if (os_strcmp(buf, "sae_confirm_immediate") == 0) {
+ bss->sae_confirm_immediate = atoi(pos);
+ } else if (os_strcmp(buf, "sae_pwe") == 0) {
+ bss->sae_pwe = atoi(pos);
} else if (os_strcmp(buf, "local_pwr_constraint") == 0) {
int val = atoi(pos);
if (val < 0 || val > 255) {
@@ -4318,12 +4456,24 @@ static int hostapd_config_fill(struct hostapd_config *conf,
bss->dhcp_server_port = atoi(pos);
} else if (os_strcmp(buf, "dhcp_relay_port") == 0) {
bss->dhcp_relay_port = atoi(pos);
+ } else if (os_strcmp(buf, "fils_discovery_min_interval") == 0) {
+ bss->fils_discovery_min_int = atoi(pos);
+ } else if (os_strcmp(buf, "fils_discovery_max_interval") == 0) {
+ bss->fils_discovery_max_int = 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);
+ } else if (os_strcmp(buf, "notify_mgmt_frames") == 0) {
+ bss->notify_mgmt_frames = atoi(pos);
#ifdef CONFIG_DPP
+ } else if (os_strcmp(buf, "dpp_name") == 0) {
+ os_free(bss->dpp_name);
+ bss->dpp_name = os_strdup(pos);
+ } else if (os_strcmp(buf, "dpp_mud_url") == 0) {
+ os_free(bss->dpp_mud_url);
+ bss->dpp_mud_url = os_strdup(pos);
} else if (os_strcmp(buf, "dpp_connector") == 0) {
os_free(bss->dpp_connector);
bss->dpp_connector = os_strdup(pos);
@@ -4339,6 +4489,18 @@ static int hostapd_config_fill(struct hostapd_config *conf,
} else if (os_strcmp(buf, "dpp_controller") == 0) {
if (hostapd_dpp_controller_parse(bss, pos))
return 1;
+ } else if (os_strcmp(buf, "dpp_configurator_connectivity") == 0) {
+ bss->dpp_configurator_connectivity = atoi(pos);
+ } else if (os_strcmp(buf, "dpp_pfs") == 0) {
+ int val = atoi(pos);
+
+ if (val < 0 || val > 2) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid dpp_pfs value '%s'",
+ line, pos);
+ return -1;
+ }
+ bss->dpp_pfs = val;
#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
#ifdef CONFIG_OWE
@@ -4372,9 +4534,11 @@ static int hostapd_config_fill(struct hostapd_config *conf,
line, pos);
return 1;
}
+ } else if (os_strcmp(buf, "owe_ptk_workaround") == 0) {
+ bss->owe_ptk_workaround = atoi(pos);
+#endif /* CONFIG_OWE */
} else if (os_strcmp(buf, "coloc_intf_reporting") == 0) {
bss->coloc_intf_reporting = atoi(pos);
-#endif /* CONFIG_OWE */
} else if (os_strcmp(buf, "multi_ap") == 0) {
int val = atoi(pos);
@@ -4389,8 +4553,12 @@ static int hostapd_config_fill(struct hostapd_config *conf,
conf->rssi_reject_assoc_rssi = atoi(pos);
} else if (os_strcmp(buf, "rssi_reject_assoc_timeout") == 0) {
conf->rssi_reject_assoc_timeout = atoi(pos);
+ } else if (os_strcmp(buf, "rssi_ignore_probe_request") == 0) {
+ conf->rssi_ignore_probe_request = atoi(pos);
} else if (os_strcmp(buf, "pbss") == 0) {
bss->pbss = atoi(pos);
+ } else if (os_strcmp(buf, "transition_disable") == 0) {
+ bss->transition_disable = strtol(pos, NULL, 16);
#ifdef CONFIG_AIRTIME_POLICY
} else if (os_strcmp(buf, "airtime_mode") == 0) {
int val = atoi(pos);
@@ -4506,6 +4674,37 @@ static int hostapd_config_fill(struct hostapd_config *conf,
}
bss->mka_psk_set |= MKA_PSK_SET_CKN;
#endif /* CONFIG_MACSEC */
+ } else if (os_strcmp(buf, "disable_11n") == 0) {
+ bss->disable_11n = !!atoi(pos);
+ } else if (os_strcmp(buf, "disable_11ac") == 0) {
+ bss->disable_11ac = !!atoi(pos);
+ } else if (os_strcmp(buf, "disable_11ax") == 0) {
+ bss->disable_11ax = !!atoi(pos);
+#ifdef CONFIG_PASN
+#ifdef CONFIG_TESTING_OPTIONS
+ } else if (os_strcmp(buf, "force_kdk_derivation") == 0) {
+ bss->force_kdk_derivation = atoi(pos);
+ } else if (os_strcmp(buf, "pasn_corrupt_mic") == 0) {
+ bss->pasn_corrupt_mic = atoi(pos);
+#endif /* CONFIG_TESTING_OPTIONS */
+ } else if (os_strcmp(buf, "pasn_groups") == 0) {
+ if (hostapd_parse_intlist(&bss->pasn_groups, pos)) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid pasn_groups value '%s'",
+ line, pos);
+ return 1;
+ }
+ } else if (os_strcmp(buf, "pasn_comeback_after") == 0) {
+ bss->pasn_comeback_after = atoi(pos);
+#endif /* CONFIG_PASN */
+ } else if (os_strcmp(buf, "ext_capa_mask") == 0) {
+ if (get_hex_config(bss->ext_capa_mask, EXT_CAPA_MAX_LEN,
+ line, "ext_capa_mask", pos))
+ return 1;
+ } else if (os_strcmp(buf, "ext_capa") == 0) {
+ if (get_hex_config(bss->ext_capa, EXT_CAPA_MAX_LEN,
+ line, "ext_capa", pos))
+ return 1;
} else {
wpa_printf(MSG_ERROR,
"Line %d: unknown configuration item '%s'",
diff --git a/contrib/wpa/hostapd/ctrl_iface.c b/contrib/wpa/hostapd/ctrl_iface.c
index 0f6dfa13d8f..4a2d6062707 100644
--- a/contrib/wpa/hostapd/ctrl_iface.c
+++ b/contrib/wpa/hostapd/ctrl_iface.c
@@ -11,7 +11,11 @@
#ifndef CONFIG_NATIVE_WINDOWS
#ifdef CONFIG_TESTING_OPTIONS
+#ifdef __NetBSD__
+#include
+#else
#include
+#endif
#include
#endif /* CONFIG_TESTING_OPTIONS */
@@ -33,6 +37,7 @@
#include "common/dpp.h"
#endif /* CONFIG_DPP */
#include "common/wpa_ctrl.h"
+#include "common/ptksa_cache.h"
#include "crypto/tls.h"
#include "drivers/driver.h"
#include "eapol_auth/eapol_auth_sm.h"
@@ -43,6 +48,7 @@
#include "ap/ap_config.h"
#include "ap/ieee802_1x.h"
#include "ap/wpa_auth.h"
+#include "ap/pmksa_cache_auth.h"
#include "ap/ieee802_11.h"
#include "ap/sta_info.h"
#include "ap/wps_hostapd.h"
@@ -55,6 +61,7 @@
#include "ap/neighbor_db.h"
#include "ap/rrm.h"
#include "ap/dpp_hostapd.h"
+#include "ap/dfs.h"
#include "wps/wps_defs.h"
#include "wps/wps.h"
#include "fst/fst_ctrl_iface.h"
@@ -65,9 +72,6 @@
#define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256
#ifdef CONFIG_CTRL_IFACE_UDP
-#define COOKIE_LEN 8
-static unsigned char cookie[COOKIE_LEN];
-static unsigned char gcookie[COOKIE_LEN];
#define HOSTAPD_CTRL_IFACE_PORT 8877
#define HOSTAPD_CTRL_IFACE_PORT_LIMIT 50
#define HOSTAPD_GLOBAL_CTRL_IFACE_PORT 8878
@@ -130,7 +134,6 @@ static int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd,
}
-#ifdef CONFIG_IEEE80211W
#ifdef NEED_AP_MLME
static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd,
const char *txtaddr)
@@ -149,7 +152,6 @@ static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd,
return 0;
}
#endif /* NEED_AP_MLME */
-#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_WPS
@@ -1098,7 +1100,6 @@ static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd,
}
#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 ");
if (os_snprintf_error(end - pos, ret))
@@ -1111,7 +1112,6 @@ static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd,
return pos - buf;
pos += ret;
}
-#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_SAE
if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
ret = os_snprintf(pos, end - pos, "SAE ");
@@ -1223,6 +1223,52 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
return pos - buf;
pos += ret;
}
+
+ if (hapd->conf->multi_ap) {
+ struct hostapd_ssid *ssid = &hapd->conf->multi_ap_backhaul_ssid;
+
+ ret = os_snprintf(pos, end - pos, "multi_ap=%d\n",
+ hapd->conf->multi_ap);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ if (ssid->ssid_len) {
+ ret = os_snprintf(pos, end - pos,
+ "multi_ap_backhaul_ssid=%s\n",
+ wpa_ssid_txt(ssid->ssid,
+ ssid->ssid_len));
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (hapd->conf->wps_state && hapd->conf->wpa &&
+ ssid->wpa_passphrase) {
+ ret = os_snprintf(pos, end - pos,
+ "multi_ap_backhaul_wpa_passphrase=%s\n",
+ ssid->wpa_passphrase);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if (hapd->conf->wps_state && hapd->conf->wpa &&
+ ssid->wpa_psk &&
+ ssid->wpa_psk->group) {
+ char hex[PMK_LEN * 2 + 1];
+
+ wpa_snprintf_hex(hex, sizeof(hex), ssid->wpa_psk->psk,
+ PMK_LEN);
+ ret = os_snprintf(pos, end - pos,
+ "multi_ap_backhaul_wpa_psk=%s\n",
+ hex);
+ forced_memzero(hex, sizeof(hex));
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+ }
#endif /* CONFIG_WPS */
if (hapd->conf->wpa) {
@@ -1290,6 +1336,22 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
pos += ret;
}
+ if (hapd->conf->wpa && hapd->conf->wpa_deny_ptk0_rekey) {
+ ret = os_snprintf(pos, end - pos, "wpa_deny_ptk0_rekey=%d\n",
+ hapd->conf->wpa_deny_ptk0_rekey);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
+ if ((hapd->conf->wpa & WPA_PROTO_RSN) && hapd->conf->extended_key_id) {
+ ret = os_snprintf(pos, end - pos, "extended_key_id=%d\n",
+ hapd->conf->extended_key_id);
+ if (os_snprintf_error(end - pos, ret))
+ return pos - buf;
+ pos += ret;
+ }
+
return pos - buf;
}
@@ -1330,6 +1392,41 @@ static void hostapd_disassoc_deny_mac(struct hostapd_data *hapd)
}
}
+
+static int hostapd_ctrl_iface_set_band(struct hostapd_data *hapd,
+ const char *bands)
+{
+ union wpa_event_data event;
+ u32 setband_mask = WPA_SETBAND_AUTO;
+
+ /*
+ * For example:
+ * SET setband 2G,6G
+ * SET setband 5G
+ * SET setband AUTO
+ */
+ if (!os_strstr(bands, "AUTO")) {
+ if (os_strstr(bands, "5G"))
+ setband_mask |= WPA_SETBAND_5G;
+ if (os_strstr(bands, "6G"))
+ setband_mask |= WPA_SETBAND_6G;
+ if (os_strstr(bands, "2G"))
+ setband_mask |= WPA_SETBAND_2G;
+ if (setband_mask == WPA_SETBAND_AUTO)
+ return -1;
+ }
+
+ if (hostapd_drv_set_band(hapd, setband_mask) == 0) {
+ os_memset(&event, 0, sizeof(event));
+ event.channel_list_changed.initiator = REGDOM_SET_BY_USER;
+ event.channel_list_changed.type = REGDOM_TYPE_UNKNOWN;
+ wpa_supplicant_event(hapd, EVENT_CHANNEL_LIST_CHANGED, &event);
+ }
+
+ return 0;
+}
+
+
static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
{
char *value;
@@ -1372,6 +1469,8 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
hapd->ext_mgmt_frame_handling = atoi(value);
} else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
hapd->ext_eapol_frame_io = atoi(value);
+ } else if (os_strcasecmp(cmd, "force_backlog_bytes") == 0) {
+ hapd->force_backlog_bytes = atoi(value);
#ifdef CONFIG_DPP
} else if (os_strcasecmp(cmd, "dpp_config_obj_override") == 0) {
os_free(hapd->dpp_config_obj_override);
@@ -1387,6 +1486,8 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
hapd->dpp_ignore_netaccesskey_mismatch = atoi(value);
} else if (os_strcasecmp(cmd, "dpp_test") == 0) {
dpp_test = atoi(value);
+ } else if (os_strcasecmp(cmd, "dpp_version_override") == 0) {
+ dpp_version_override = atoi(value);
#endif /* CONFIG_DPP */
#endif /* CONFIG_TESTING_OPTIONS */
#ifdef CONFIG_MBO
@@ -1412,7 +1513,19 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
} else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) {
os_free(hapd->dpp_configurator_params);
hapd->dpp_configurator_params = os_strdup(value);
+ } else if (os_strcasecmp(cmd, "dpp_init_max_tries") == 0) {
+ hapd->dpp_init_max_tries = atoi(value);
+ } else if (os_strcasecmp(cmd, "dpp_init_retry_time") == 0) {
+ hapd->dpp_init_retry_time = atoi(value);
+ } else if (os_strcasecmp(cmd, "dpp_resp_wait_time") == 0) {
+ hapd->dpp_resp_wait_time = atoi(value);
+ } else if (os_strcasecmp(cmd, "dpp_resp_max_tries") == 0) {
+ hapd->dpp_resp_max_tries = atoi(value);
+ } else if (os_strcasecmp(cmd, "dpp_resp_retry_time") == 0) {
+ hapd->dpp_resp_retry_time = atoi(value);
#endif /* CONFIG_DPP */
+ } else if (os_strcasecmp(cmd, "setband") == 0) {
+ ret = hostapd_ctrl_iface_set_band(hapd, value);
} else {
ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value);
if (ret)
@@ -1428,7 +1541,37 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
if (ieee802_11_update_beacons(hapd->iface))
wpa_printf(MSG_DEBUG,
"Failed to update beacons with WMM parameters");
+ } else if (os_strcmp(cmd, "wpa_passphrase") == 0 ||
+ os_strcmp(cmd, "sae_password") == 0 ||
+ os_strcmp(cmd, "sae_pwe") == 0) {
+ if (hapd->started)
+ hostapd_setup_sae_pt(hapd->conf);
+ } else if (os_strcasecmp(cmd, "transition_disable") == 0) {
+ wpa_auth_set_transition_disable(hapd->wpa_auth,
+ hapd->conf->transition_disable);
}
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (os_strcmp(cmd, "ft_rsnxe_used") == 0)
+ wpa_auth_set_ft_rsnxe_used(hapd->wpa_auth,
+ hapd->conf->ft_rsnxe_used);
+ else if (os_strcmp(cmd, "oci_freq_override_eapol_m3") == 0)
+ wpa_auth_set_ocv_override_freq(
+ hapd->wpa_auth, WPA_AUTH_OCV_OVERRIDE_EAPOL_M3,
+ atoi(value));
+ else if (os_strcmp(cmd, "oci_freq_override_eapol_g1") == 0)
+ wpa_auth_set_ocv_override_freq(
+ hapd->wpa_auth, WPA_AUTH_OCV_OVERRIDE_EAPOL_G1,
+ atoi(value));
+ else if (os_strcmp(cmd, "oci_freq_override_ft_assoc") == 0)
+ wpa_auth_set_ocv_override_freq(
+ hapd->wpa_auth, WPA_AUTH_OCV_OVERRIDE_FT_ASSOC,
+ atoi(value));
+ else if (os_strcmp(cmd, "oci_freq_override_fils_assoc") == 0)
+ wpa_auth_set_ocv_override_freq(
+ hapd->wpa_auth,
+ WPA_AUTH_OCV_OVERRIDE_FILS_ASSOC, atoi(value));
+#endif /* CONFIG_TESTING_OPTIONS */
}
return ret;
@@ -1628,7 +1771,7 @@ static int hostapd_ctrl_iface_mgmt_tx(struct hostapd_data *hapd, char *cmd)
return -1;
}
- res = hostapd_drv_send_mlme(hapd, buf, len, 0);
+ res = hostapd_drv_send_mlme(hapd, buf, len, 0, NULL, 0, 0);
os_free(buf);
return res;
}
@@ -1803,6 +1946,52 @@ static int hostapd_ctrl_iface_eapol_rx(struct hostapd_data *hapd, char *cmd)
}
+static int hostapd_ctrl_iface_eapol_tx(struct hostapd_data *hapd, char *cmd)
+{
+ char *pos, *pos2;
+ u8 dst[ETH_ALEN], *buf;
+ int used, ret;
+ size_t len;
+ unsigned int prev;
+ int encrypt = 0;
+
+ wpa_printf(MSG_DEBUG, "External EAPOL TX: %s", cmd);
+
+ pos = cmd;
+ used = hwaddr_aton2(pos, dst);
+ if (used < 0)
+ return -1;
+ pos += used;
+ while (*pos == ' ')
+ pos++;
+
+ pos2 = os_strchr(pos, ' ');
+ if (pos2) {
+ len = pos2 - pos;
+ encrypt = os_strstr(pos2, "encrypt=1") != NULL;
+ } else {
+ len = os_strlen(pos);
+ }
+ if (len & 1)
+ return -1;
+ len /= 2;
+
+ buf = os_malloc(len);
+ if (!buf || hexstr2bin(pos, buf, len) < 0) {
+ os_free(buf);
+ return -1;
+ }
+
+ prev = hapd->ext_eapol_frame_io;
+ hapd->ext_eapol_frame_io = 0;
+ ret = hostapd_wpa_auth_send_eapol(hapd, dst, buf, len, encrypt);
+ hapd->ext_eapol_frame_io = prev;
+ os_free(buf);
+
+ return ret;
+}
+
+
static u16 ipv4_hdr_checksum(const void *buf, size_t len)
{
size_t i;
@@ -1827,7 +2016,7 @@ static void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf,
{
struct hostapd_data *hapd = ctx;
const struct ether_header *eth;
- struct iphdr ip;
+ struct ip ip;
const u8 *pos;
unsigned int i;
char extra[30];
@@ -1843,14 +2032,14 @@ static void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf,
os_memcpy(&ip, eth + 1, sizeof(ip));
pos = &buf[sizeof(*eth) + sizeof(ip)];
- if (ip.ihl != 5 || ip.version != 4 ||
- ntohs(ip.tot_len) > HWSIM_IP_LEN) {
+ if (ip.ip_hl != 5 || ip.ip_v != 4 ||
+ ntohs(ip.ip_len) > HWSIM_IP_LEN) {
wpa_printf(MSG_DEBUG,
- "test data: RX - ignore unexpect IP header");
+ "test data: RX - ignore unexpected IP header");
return;
}
- for (i = 0; i < ntohs(ip.tot_len) - sizeof(ip); i++) {
+ for (i = 0; i < ntohs(ip.ip_len) - sizeof(ip); i++) {
if (*pos != (u8) i) {
wpa_printf(MSG_DEBUG,
"test data: RX - ignore mismatching payload");
@@ -1860,8 +2049,8 @@ static void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf,
}
extra[0] = '\0';
- if (ntohs(ip.tot_len) != HWSIM_IP_LEN)
- os_snprintf(extra, sizeof(extra), " len=%d", ntohs(ip.tot_len));
+ if (ntohs(ip.ip_len) != HWSIM_IP_LEN)
+ os_snprintf(extra, sizeof(extra), " len=%d", ntohs(ip.ip_len));
wpa_msg(hapd->msg_ctx, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR "%s",
MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost), extra);
}
@@ -1914,7 +2103,7 @@ static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd)
u8 tos;
u8 buf[2 + HWSIM_PACKETLEN];
struct ether_header *eth;
- struct iphdr *ip;
+ struct ip *ip;
u8 *dpos;
unsigned int i;
size_t send_len = HWSIM_IP_LEN;
@@ -1953,17 +2142,17 @@ static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd)
os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
os_memcpy(eth->ether_shost, src, ETH_ALEN);
eth->ether_type = htons(ETHERTYPE_IP);
- ip = (struct iphdr *) (eth + 1);
+ ip = (struct ip *) (eth + 1);
os_memset(ip, 0, sizeof(*ip));
- ip->ihl = 5;
- ip->version = 4;
- ip->ttl = 64;
- ip->tos = tos;
- ip->tot_len = htons(send_len);
- ip->protocol = 1;
- ip->saddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1);
- ip->daddr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2);
- ip->check = ipv4_hdr_checksum(ip, sizeof(*ip));
+ ip->ip_hl = 5;
+ ip->ip_v = 4;
+ ip->ip_ttl = 64;
+ ip->ip_tos = tos;
+ ip->ip_len = htons(send_len);
+ ip->ip_p = 1;
+ ip->ip_src.s_addr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 1);
+ ip->ip_dst.s_addr = htonl(192U << 24 | 168 << 16 | 1 << 8 | 2);
+ ip->ip_sum = ipv4_hdr_checksum(ip, sizeof(*ip));
dpos = (u8 *) (ip + 1);
for (i = 0; i < send_len - sizeof(*ip); i++)
*dpos++ = i;
@@ -2109,7 +2298,32 @@ static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd)
if (hwaddr_aton(cmd, addr))
return -1;
-#ifdef CONFIG_IEEE80211W
+ if (is_broadcast_ether_addr(addr) && os_strstr(cmd, " BIGTK")) {
+ if (hapd->last_bigtk_alg == WPA_ALG_NONE)
+ return -1;
+
+ wpa_printf(MSG_INFO, "TESTING: Reset BIPN for BIGTK");
+
+ /* 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_bigtk_alg,
+ broadcast_ether_addr,
+ hapd->last_bigtk_key_idx, 0, 1, NULL, 0,
+ zero, hapd->last_bigtk_len,
+ KEY_FLAG_GROUP_TX_DEFAULT) < 0)
+ return -1;
+
+ /* Set the previously configured key to reset its TSC */
+ return hostapd_drv_set_key(hapd->conf->iface, hapd,
+ hapd->last_bigtk_alg,
+ broadcast_ether_addr,
+ hapd->last_bigtk_key_idx, 0, 1, NULL,
+ 0, hapd->last_bigtk,
+ hapd->last_bigtk_len,
+ KEY_FLAG_GROUP_TX_DEFAULT);
+ }
+
if (is_broadcast_ether_addr(addr) && os_strstr(cmd, "IGTK")) {
if (hapd->last_igtk_alg == WPA_ALG_NONE)
return -1;
@@ -2121,19 +2335,20 @@ static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd)
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)
+ hapd->last_igtk_key_idx, 0, 1, NULL, 0,
+ zero, hapd->last_igtk_len,
+ KEY_FLAG_GROUP_TX_DEFAULT) < 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);
+ hapd->last_igtk_key_idx, 0, 1, NULL,
+ 0, hapd->last_igtk,
+ hapd->last_igtk_len,
+ KEY_FLAG_GROUP_TX_DEFAULT);
}
-#endif /* CONFIG_IEEE80211W */
if (is_broadcast_ether_addr(addr)) {
if (hapd->last_gtk_alg == WPA_ALG_NONE)
@@ -2146,16 +2361,19 @@ static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd)
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)
+ hapd->last_gtk_key_idx, 0, 1, NULL, 0,
+ zero, hapd->last_gtk_len,
+ KEY_FLAG_GROUP_TX_DEFAULT) < 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);
+ hapd->last_gtk_key_idx, 0, 1, NULL,
+ 0, hapd->last_gtk,
+ hapd->last_gtk_len,
+ KEY_FLAG_GROUP_TX_DEFAULT);
}
sta = ap_get_sta(hapd, addr);
@@ -2171,14 +2389,16 @@ static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd)
/* 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)
+ sta->addr, sta->last_tk_key_idx, 0, 1, NULL, 0,
+ zero, sta->last_tk_len,
+ KEY_FLAG_PAIRWISE_RX_TX) < 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);
+ sta->addr, sta->last_tk_key_idx, 0, 1, NULL,
+ 0, sta->last_tk, sta->last_tk_len,
+ KEY_FLAG_PAIRWISE_RX_TX);
}
@@ -2187,11 +2407,12 @@ 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;
+ enum key_flag key_flag;
int idx, set_tx;
u8 seq[6], key[WPA_TK_MAX_LEN];
size_t key_len;
- /* parameters: alg addr idx set_tx seq key */
+ /* parameters: alg addr idx set_tx seq key key_flag */
alg = atoi(pos);
pos = os_strchr(pos, ' ');
@@ -2220,13 +2441,24 @@ static int hostapd_ctrl_set_key(struct hostapd_data *hapd, const char *cmd)
if (*pos != ' ')
return -1;
pos++;
- key_len = os_strlen(pos) / 2;
+ if (!os_strchr(pos, ' '))
+ return -1;
+ key_len = (os_strchr(pos, ' ') - pos) / 2;
if (hexstr2bin(pos, key, key_len) < 0)
return -1;
+ pos += 2 * key_len;
+ if (*pos != ' ')
+ return -1;
+
+ pos++;
+ key_flag = atoi(pos);
+ pos = os_strchr(pos, ' ');
+ if (pos)
+ 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);
+ return hostapd_drv_set_key(hapd->conf->iface, hapd, alg, addr, idx, 0,
+ set_tx, seq, 6, key, key_len, key_flag);
}
@@ -2241,8 +2473,9 @@ static void restore_tk(void *ctx1, void *ctx2)
* 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);
+ sta->addr, sta->last_tk_key_idx, 0, 1, NULL, 0,
+ sta->last_tk, sta->last_tk_len,
+ KEY_FLAG_PAIRWISE_RX_TX);
}
@@ -2265,8 +2498,8 @@ static int hostapd_ctrl_resend_m1(struct hostapd_data *hapd, const char *cmd)
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);
+ sta->addr, sta->last_tk_key_idx, 0, 0, NULL,
+ 0, NULL, 0, KEY_FLAG_PAIRWISE);
}
wpa_printf(MSG_INFO, "TESTING: Send M1 to " MACSTR, MAC2STR(sta->addr));
@@ -2295,8 +2528,8 @@ static int hostapd_ctrl_resend_m3(struct hostapd_data *hapd, const char *cmd)
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);
+ sta->addr, sta->last_tk_key_idx, 0, 0, NULL,
+ 0, NULL, 0, KEY_FLAG_PAIRWISE);
}
wpa_printf(MSG_INFO, "TESTING: Send M3 to " MACSTR, MAC2STR(sta->addr));
@@ -2325,8 +2558,8 @@ static int hostapd_ctrl_resend_group_m1(struct hostapd_data *hapd,
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);
+ sta->addr, sta->last_tk_key_idx, 0, 0, NULL,
+ 0, NULL, 0, KEY_FLAG_PAIRWISE);
}
wpa_printf(MSG_INFO,
@@ -2336,26 +2569,268 @@ static int hostapd_ctrl_resend_group_m1(struct hostapd_data *hapd,
plain ? restore_tk : NULL, hapd, sta);
}
+
+static int hostapd_ctrl_rekey_ptk(struct hostapd_data *hapd, const char *cmd)
+{
+ struct sta_info *sta;
+ u8 addr[ETH_ALEN];
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta || !sta->wpa_sm)
+ return -1;
+
+ return wpa_auth_rekey_ptk(hapd->wpa_auth, sta->wpa_sm);
+}
+
+
+static int hostapd_ctrl_get_pmksa_pmk(struct hostapd_data *hapd, const u8 *addr,
+ char *buf, size_t buflen)
+{
+ struct rsn_pmksa_cache_entry *pmksa;
+
+ pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, addr, NULL);
+ if (!pmksa)
+ return -1;
+
+ return wpa_snprintf_hex(buf, buflen, pmksa->pmk, pmksa->pmk_len);
+}
+
+
+static int hostapd_ctrl_get_pmk(struct hostapd_data *hapd, const char *cmd,
+ char *buf, size_t buflen)
+{
+ struct sta_info *sta;
+ u8 addr[ETH_ALEN];
+ const u8 *pmk;
+ int pmk_len;
+
+ if (hwaddr_aton(cmd, addr))
+ return -1;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta || !sta->wpa_sm) {
+ wpa_printf(MSG_DEBUG, "No STA WPA state machine for " MACSTR,
+ MAC2STR(addr));
+ return hostapd_ctrl_get_pmksa_pmk(hapd, addr, buf, buflen);
+ }
+ pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len);
+ if (!pmk || !pmk_len) {
+ wpa_printf(MSG_DEBUG, "No PMK stored for " MACSTR,
+ MAC2STR(addr));
+ return hostapd_ctrl_get_pmksa_pmk(hapd, addr, buf, buflen);
+ }
+
+ return wpa_snprintf_hex(buf, buflen, pmk, pmk_len);
+}
+
+
+static int hostapd_ctrl_register_frame(struct hostapd_data *hapd,
+ const char *cmd)
+{
+ u16 type;
+ char *pos, *end;
+ u8 match[10];
+ size_t match_len;
+ bool multicast = false;
+
+ type = strtol(cmd, &pos, 16);
+ if (*pos != ' ')
+ return -1;
+ pos++;
+ end = os_strchr(pos, ' ');
+ if (end) {
+ match_len = end - pos;
+ multicast = os_strstr(end, "multicast") != NULL;
+ } else {
+ match_len = os_strlen(pos) / 2;
+ }
+ if (hexstr2bin(pos, match, match_len))
+ return -1;
+
+ return hostapd_drv_register_frame(hapd, type, match, match_len,
+ multicast);
+}
+
#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef NEED_AP_MLME
+static int hostapd_ctrl_check_freq_params(struct hostapd_freq_params *params)
+{
+ switch (params->bandwidth) {
+ case 0:
+ /* bandwidth not specified: use 20 MHz by default */
+ /* fall-through */
+ case 20:
+ if (params->center_freq1 &&
+ params->center_freq1 != params->freq)
+ return -1;
+
+ if (params->center_freq2 || params->sec_channel_offset)
+ return -1;
+ break;
+ case 40:
+ if (params->center_freq2 || !params->sec_channel_offset)
+ return -1;
+
+ if (!params->center_freq1)
+ break;
+ switch (params->sec_channel_offset) {
+ case 1:
+ if (params->freq + 10 != params->center_freq1)
+ return -1;
+ break;
+ case -1:
+ if (params->freq - 10 != params->center_freq1)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ case 80:
+ if (!params->center_freq1 || !params->sec_channel_offset)
+ return 1;
+
+ switch (params->sec_channel_offset) {
+ case 1:
+ if (params->freq - 10 != params->center_freq1 &&
+ params->freq + 30 != params->center_freq1)
+ return 1;
+ break;
+ case -1:
+ if (params->freq + 10 != params->center_freq1 &&
+ params->freq - 30 != params->center_freq1)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+
+ /* Adjacent and overlapped are not allowed for 80+80 */
+ if (params->center_freq2 &&
+ params->center_freq1 - params->center_freq2 <= 80 &&
+ params->center_freq2 - params->center_freq1 <= 80)
+ return 1;
+ break;
+ case 160:
+ if (!params->center_freq1 || params->center_freq2 ||
+ !params->sec_channel_offset)
+ return -1;
+
+ switch (params->sec_channel_offset) {
+ case 1:
+ if (params->freq + 70 != params->center_freq1 &&
+ params->freq + 30 != params->center_freq1 &&
+ params->freq - 10 != params->center_freq1 &&
+ params->freq - 50 != params->center_freq1)
+ return -1;
+ break;
+ case -1:
+ if (params->freq + 50 != params->center_freq1 &&
+ params->freq + 10 != params->center_freq1 &&
+ params->freq - 30 != params->center_freq1 &&
+ params->freq - 70 != params->center_freq1)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+#endif /* NEED_AP_MLME */
+
+
static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
char *pos)
{
#ifdef NEED_AP_MLME
struct csa_settings settings;
int ret;
+ int dfs_range = 0;
unsigned int i;
+ int bandwidth;
+ u8 chan;
ret = hostapd_parse_csa_settings(pos, &settings);
if (ret)
return ret;
+ ret = hostapd_ctrl_check_freq_params(&settings.freq_params);
+ if (ret) {
+ wpa_printf(MSG_INFO,
+ "chanswitch: invalid frequency settings provided");
+ return ret;
+ }
+
+ switch (settings.freq_params.bandwidth) {
+ case 40:
+ bandwidth = CHAN_WIDTH_40;
+ break;
+ case 80:
+ if (settings.freq_params.center_freq2)
+ bandwidth = CHAN_WIDTH_80P80;
+ else
+ bandwidth = CHAN_WIDTH_80;
+ break;
+ case 160:
+ bandwidth = CHAN_WIDTH_160;
+ break;
+ default:
+ bandwidth = CHAN_WIDTH_20;
+ break;
+ }
+
+ if (settings.freq_params.center_freq1)
+ dfs_range += hostapd_is_dfs_overlap(
+ iface, bandwidth, settings.freq_params.center_freq1);
+ else
+ dfs_range += hostapd_is_dfs_overlap(
+ iface, bandwidth, settings.freq_params.freq);
+
+ if (settings.freq_params.center_freq2)
+ dfs_range += hostapd_is_dfs_overlap(
+ iface, bandwidth, settings.freq_params.center_freq2);
+
+ if (dfs_range) {
+ ret = ieee80211_freq_to_chan(settings.freq_params.freq, &chan);
+ if (ret == NUM_HOSTAPD_MODES) {
+ wpa_printf(MSG_ERROR,
+ "Failed to get channel for (freq=%d, sec_channel_offset=%d, bw=%d)",
+ settings.freq_params.freq,
+ settings.freq_params.sec_channel_offset,
+ settings.freq_params.bandwidth);
+ return -1;
+ }
+
+ settings.freq_params.channel = chan;
+
+ wpa_printf(MSG_DEBUG,
+ "DFS/CAC to (channel=%u, freq=%d, sec_channel_offset=%d, bw=%d, center_freq1=%d)",
+ settings.freq_params.channel,
+ settings.freq_params.freq,
+ settings.freq_params.sec_channel_offset,
+ settings.freq_params.bandwidth,
+ settings.freq_params.center_freq1);
+
+ /* Perform CAC and switch channel */
+ hostapd_switch_channel_fallback(iface, &settings.freq_params);
+ return 0;
+ }
+
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);
+ /* Save CHAN_SWITCH VHT and HE config */
+ hostapd_chan_switch_config(iface->bss[i],
+ &settings.freq_params);
ret = hostapd_switch_channel(iface->bss[i], &settings);
if (ret) {
@@ -2389,13 +2864,17 @@ static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd,
char *buf, size_t buflen)
{
int ret;
- char *pos;
+ char *pos, *temp = NULL;
u8 *data = NULL;
unsigned int vendor_id, subcmd;
+ enum nested_attr nested_attr_flag = NESTED_ATTR_UNSPECIFIED;
struct wpabuf *reply;
size_t data_len = 0;
- /* cmd: [] */
+ /**
+ * cmd: []
+ * [nested=<0|1>]
+ */
vendor_id = strtoul(cmd, &pos, 16);
if (!isblank((unsigned char) *pos))
return -EINVAL;
@@ -2405,7 +2884,9 @@ static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd,
if (*pos != '\0') {
if (!isblank((unsigned char) *pos++))
return -EINVAL;
- data_len = os_strlen(pos);
+
+ temp = os_strchr(pos, ' ');
+ data_len = temp ? (size_t) (temp - pos) : os_strlen(pos);
}
if (data_len) {
@@ -2422,6 +2903,11 @@ static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd,
}
}
+ pos = os_strstr(cmd, "nested=");
+ if (pos)
+ nested_attr_flag = atoi(pos + 7) ? NESTED_ATTR_USED :
+ NESTED_ATTR_NOT_USED;
+
reply = wpabuf_alloc((buflen - 1) / 2);
if (!reply) {
os_free(data);
@@ -2429,7 +2915,7 @@ static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd,
}
ret = hostapd_drv_vendor_cmd(hapd, vendor_id, subcmd, data, data_len,
- reply);
+ nested_attr_flag, reply);
if (ret == 0)
ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
@@ -2679,6 +3165,20 @@ static int hostapd_ctrl_iface_req_beacon(struct hostapd_data *hapd,
}
+static int hostapd_ctrl_iface_show_neighbor(struct hostapd_data *hapd,
+ char *buf, size_t buflen)
+{
+ if (!(hapd->conf->radio_measurements[0] &
+ WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
+ wpa_printf(MSG_ERROR,
+ "CTRL: SHOW_NEIGHBOR: Neighbor report is not enabled");
+ return -1;
+ }
+
+ return hostapd_neighbor_show(hapd, buf, buflen);
+}
+
+
static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf)
{
struct wpa_ssid_value ssid;
@@ -2785,6 +3285,7 @@ static int hostapd_ctrl_iface_remove_neighbor(struct hostapd_data *hapd,
char *buf)
{
struct wpa_ssid_value ssid;
+ struct wpa_ssid_value *ssidp = NULL;
u8 bssid[ETH_ALEN];
char *tmp;
@@ -2794,13 +3295,16 @@ static int hostapd_ctrl_iface_remove_neighbor(struct hostapd_data *hapd,
}
tmp = os_strstr(buf, "ssid=");
- if (!tmp || ssid_parse(tmp + 5, &ssid)) {
- wpa_printf(MSG_ERROR,
- "CTRL: REMOVE_NEIGHBORr: Bad or missing SSID");
- return -1;
+ if (tmp) {
+ ssidp = &ssid;
+ if (ssid_parse(tmp + 5, &ssid)) {
+ wpa_printf(MSG_ERROR,
+ "CTRL: REMOVE_NEIGHBOR: Bad SSID");
+ return -1;
+ }
}
- return hostapd_neighbor_remove(hapd, bssid, &ssid);
+ return hostapd_neighbor_remove(hapd, bssid, ssidp);
}
@@ -2832,6 +3336,34 @@ static int hostapd_ctrl_driver_flags(struct hostapd_iface *iface, char *buf,
}
+static int hostapd_ctrl_driver_flags2(struct hostapd_iface *iface, char *buf,
+ size_t buflen)
+{
+ int ret, i;
+ char *pos, *end;
+
+ ret = os_snprintf(buf, buflen, "%016llX:\n",
+ (long long unsigned) iface->drv_flags2);
+ if (os_snprintf_error(buflen, ret))
+ return -1;
+
+ pos = buf + ret;
+ end = buf + buflen;
+
+ for (i = 0; i < 64; i++) {
+ if (iface->drv_flags2 & (1LLU << i)) {
+ ret = os_snprintf(pos, end - pos, "%s\n",
+ driver_flag2_to_string(1LLU << i));
+ if (os_snprintf_error(end - pos, ret))
+ return -1;
+ pos += ret;
+ }
+ }
+
+ return pos - buf;
+}
+
+
static int hostapd_ctrl_iface_acl_del_mac(struct mac_acl_entry **acl, int *num,
const char *txtaddr)
{
@@ -2934,6 +3466,23 @@ static int hostapd_ctrl_iface_get_capability(struct hostapd_data *hapd,
}
+#ifdef ANDROID
+static int hostapd_ctrl_iface_driver_cmd(struct hostapd_data *hapd, char *cmd,
+ char *buf, size_t buflen)
+{
+ int ret;
+
+ ret = hostapd_drv_driver_cmd(hapd, cmd, buf, buflen);
+ if (ret == 0) {
+ ret = os_snprintf(buf, buflen, "%s\n", "OK");
+ if (os_snprintf_error(buflen, ret))
+ ret = -1;
+ }
+ return ret;
+}
+#endif /* ANDROID */
+
+
static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
char *buf, char *reply,
int reply_size,
@@ -3032,13 +3581,11 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
} else if (os_strcmp(buf, "STOP_AP") == 0) {
if (hostapd_ctrl_iface_stop_ap(hapd))
reply_len = -1;
-#ifdef CONFIG_IEEE80211W
#ifdef NEED_AP_MLME
} else if (os_strncmp(buf, "SA_QUERY ", 9) == 0) {
if (hostapd_ctrl_iface_sa_query(hapd, buf + 9))
reply_len = -1;
#endif /* NEED_AP_MLME */
-#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_WPS
} else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8))
@@ -3150,6 +3697,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
} else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) {
if (hostapd_ctrl_iface_eapol_rx(hapd, buf + 9) < 0)
reply_len = -1;
+ } else if (os_strncmp(buf, "EAPOL_TX ", 9) == 0) {
+ if (hostapd_ctrl_iface_eapol_tx(hapd, buf + 9) < 0)
+ reply_len = -1;
} else if (os_strncmp(buf, "DATA_TEST_CONFIG ", 17) == 0) {
if (hostapd_ctrl_iface_data_test_config(hapd, buf + 17) < 0)
reply_len = -1;
@@ -3185,9 +3735,18 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
} 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_strncmp(buf, "REKEY_PTK ", 10) == 0) {
+ if (hostapd_ctrl_rekey_ptk(hapd, buf + 10) < 0)
+ reply_len = -1;
} else if (os_strcmp(buf, "REKEY_GTK") == 0) {
if (wpa_auth_rekey_gtk(hapd->wpa_auth) < 0)
reply_len = -1;
+ } else if (os_strncmp(buf, "GET_PMK ", 8) == 0) {
+ reply_len = hostapd_ctrl_get_pmk(hapd, buf + 8, reply,
+ reply_size);
+ } else if (os_strncmp(buf, "REGISTER_FRAME ", 15) == 0) {
+ if (hostapd_ctrl_register_frame(hapd, buf + 16) < 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))
@@ -3225,6 +3784,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
} else if (os_strncmp(buf, "SET_NEIGHBOR ", 13) == 0) {
if (hostapd_ctrl_iface_set_neighbor(hapd, buf + 13))
reply_len = -1;
+ } else if (os_strcmp(buf, "SHOW_NEIGHBOR") == 0) {
+ reply_len = hostapd_ctrl_iface_show_neighbor(hapd, reply,
+ reply_size);
} else if (os_strncmp(buf, "REMOVE_NEIGHBOR ", 16) == 0) {
if (hostapd_ctrl_iface_remove_neighbor(hapd, buf + 16))
reply_len = -1;
@@ -3240,20 +3802,24 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
} else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) {
reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply,
reply_size);
+ } else if (os_strcmp(buf, "DRIVER_FLAGS2") == 0) {
+ reply_len = hostapd_ctrl_driver_flags2(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(
+ if (hostapd_ctrl_iface_acl_add_mac(
+ &hapd->conf->accept_mac,
+ &hapd->conf->num_accept_mac, buf + 19))
+ reply_len = -1;
+ } else if (os_strncmp((buf + 11), "DEL_MAC ", 8) == 0) {
+ if (!hostapd_ctrl_iface_acl_del_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,
@@ -3262,6 +3828,7 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
hostapd_ctrl_iface_acl_clear_list(
&hapd->conf->accept_mac,
&hapd->conf->num_accept_mac);
+ hostapd_disassoc_accept_mac(hapd);
}
} else if (os_strncmp(buf, "DENY_ACL ", 9) == 0) {
if (os_strncmp(buf + 9, "ADD_MAC ", 8) == 0) {
@@ -3269,10 +3836,13 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
&hapd->conf->deny_mac,
&hapd->conf->num_deny_mac, buf + 17))
hostapd_disassoc_deny_mac(hapd);
+ else
+ reply_len = -1;
} 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);
+ if (hostapd_ctrl_iface_acl_del_mac(
+ &hapd->conf->deny_mac,
+ &hapd->conf->num_deny_mac, buf + 17))
+ reply_len = -1;
} else if (os_strcmp(buf + 9, "SHOW") == 0) {
reply_len = hostapd_ctrl_iface_acl_show_mac(
hapd->conf->deny_mac,
@@ -3292,6 +3862,33 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
if (os_snprintf_error(reply_size, reply_len))
reply_len = -1;
}
+ } else if (os_strncmp(buf, "DPP_NFC_URI ", 12) == 0) {
+ res = hostapd_dpp_nfc_uri(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_NFC_HANDOVER_REQ ", 21) == 0) {
+ res = hostapd_dpp_nfc_handover_req(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_NFC_HANDOVER_SEL ", 21) == 0) {
+ res = hostapd_dpp_nfc_handover_sel(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_BOOTSTRAP_GEN ", 18) == 0) {
res = dpp_bootstrap_gen(hapd->iface->interfaces->dpp, buf + 18);
if (res < 0) {
@@ -3321,6 +3918,11 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
reply_len = dpp_bootstrap_info(hapd->iface->interfaces->dpp,
atoi(buf + 19),
reply, reply_size);
+ } else if (os_strncmp(buf, "DPP_BOOTSTRAP_SET ", 18) == 0) {
+ if (dpp_bootstrap_set(hapd->iface->interfaces->dpp,
+ atoi(buf + 18),
+ os_strchr(buf + 18, ' ')) < 0)
+ reply_len = -1;
} else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) {
if (hostapd_dpp_auth_init(hapd, buf + 13) < 0)
reply_len = -1;
@@ -3364,6 +3966,21 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
} else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) {
if (hostapd_dpp_pkex_remove(hapd, buf + 16) < 0)
reply_len = -1;
+#ifdef CONFIG_DPP2
+ } else if (os_strncmp(buf, "DPP_CONTROLLER_START ", 21) == 0) {
+ if (hostapd_dpp_controller_start(hapd, buf + 20) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "DPP_CONTROLLER_START") == 0) {
+ if (hostapd_dpp_controller_start(hapd, NULL) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "DPP_CONTROLLER_STOP") == 0) {
+ dpp_controller_stop(hapd->iface->interfaces->dpp);
+ } else if (os_strncmp(buf, "DPP_CHIRP ", 10) == 0) {
+ if (hostapd_dpp_chirp(hapd, buf + 9) < 0)
+ reply_len = -1;
+ } else if (os_strcmp(buf, "DPP_STOP_CHIRP") == 0) {
+ hostapd_dpp_chirp_stop(hapd);
+#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
#ifdef RADIUS_SERVER
} else if (os_strncmp(buf, "DAC_REQUEST ", 12) == 0) {
@@ -3373,6 +3990,15 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
} else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) {
reply_len = hostapd_ctrl_iface_get_capability(
hapd, buf + 15, reply, reply_size);
+#ifdef CONFIG_PASN
+ } else if (os_strcmp(buf, "PTKSA_CACHE_LIST") == 0) {
+ reply_len = ptksa_cache_list(hapd->ptksa, reply, reply_size);
+#endif /* CONFIG_PASN */
+#ifdef ANDROID
+ } else if (os_strncmp(buf, "DRIVER ", 7) == 0) {
+ reply_len = hostapd_ctrl_iface_driver_cmd(hapd, buf + 7, reply,
+ reply_size);
+#endif /* ANDROID */
} else {
os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
reply_len = 16;
@@ -3400,7 +4026,7 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
int reply_len;
int level = MSG_DEBUG;
#ifdef CONFIG_CTRL_IFACE_UDP
- unsigned char lcookie[COOKIE_LEN];
+ unsigned char lcookie[CTRL_IFACE_COOKIE_LEN];
#endif /* CONFIG_CTRL_IFACE_UDP */
res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
@@ -3425,28 +4051,30 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
#ifdef CONFIG_CTRL_IFACE_UDP
if (os_strcmp(buf, "GET_COOKIE") == 0) {
os_memcpy(reply, "COOKIE=", 7);
- wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
- cookie, COOKIE_LEN);
- reply_len = 7 + 2 * COOKIE_LEN;
+ wpa_snprintf_hex(reply + 7, 2 * CTRL_IFACE_COOKIE_LEN + 1,
+ hapd->ctrl_iface_cookie,
+ CTRL_IFACE_COOKIE_LEN);
+ reply_len = 7 + 2 * CTRL_IFACE_COOKIE_LEN;
goto done;
}
if (os_strncmp(buf, "COOKIE=", 7) != 0 ||
- hexstr2bin(buf + 7, lcookie, COOKIE_LEN) < 0) {
+ hexstr2bin(buf + 7, lcookie, CTRL_IFACE_COOKIE_LEN) < 0) {
wpa_printf(MSG_DEBUG,
"CTRL: No cookie in the request - drop request");
os_free(reply);
return;
}
- if (os_memcmp(cookie, lcookie, COOKIE_LEN) != 0) {
+ if (os_memcmp(hapd->ctrl_iface_cookie, lcookie,
+ CTRL_IFACE_COOKIE_LEN) != 0) {
wpa_printf(MSG_DEBUG,
"CTRL: Invalid cookie in the request - drop request");
os_free(reply);
return;
}
- pos = buf + 7 + 2 * COOKIE_LEN;
+ pos = buf + 7 + 2 * CTRL_IFACE_COOKIE_LEN;
while (*pos == ' ')
pos++;
#endif /* CONFIG_CTRL_IFACE_UDP */
@@ -3535,7 +4163,7 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
dl_list_init(&hapd->ctrl_dst);
hapd->ctrl_sock = -1;
- os_get_random(cookie, COOKIE_LEN);
+ os_get_random(hapd->ctrl_iface_cookie, CTRL_IFACE_COOKIE_LEN);
#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
hints.ai_flags = AI_PASSIVE;
@@ -3849,6 +4477,11 @@ static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces)
#ifdef CONFIG_TESTING_OPTIONS
#ifdef CONFIG_DPP
dpp_test = DPP_TEST_DISABLED;
+#ifdef CONFIG_DPP2
+ dpp_version_override = 2;
+#else /* CONFIG_DPP2 */
+ dpp_version_override = 1;
+#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
#endif /* CONFIG_TESTING_OPTIONS */
@@ -4114,7 +4747,7 @@ static int hostapd_global_ctrl_iface_ifname(struct hapd_interfaces *interfaces,
static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
void *sock_ctx)
{
- void *interfaces = eloop_ctx;
+ struct hapd_interfaces *interfaces = eloop_ctx;
char buffer[256], *buf = buffer;
int res;
struct sockaddr_storage from;
@@ -4123,7 +4756,7 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
int reply_len;
const int reply_size = 4096;
#ifdef CONFIG_CTRL_IFACE_UDP
- unsigned char lcookie[COOKIE_LEN];
+ unsigned char lcookie[CTRL_IFACE_COOKIE_LEN];
#endif /* CONFIG_CTRL_IFACE_UDP */
res = recvfrom(sock, buffer, sizeof(buffer) - 1, 0,
@@ -4152,28 +4785,30 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
#ifdef CONFIG_CTRL_IFACE_UDP
if (os_strcmp(buf, "GET_COOKIE") == 0) {
os_memcpy(reply, "COOKIE=", 7);
- wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
- gcookie, COOKIE_LEN);
- reply_len = 7 + 2 * COOKIE_LEN;
+ wpa_snprintf_hex(reply + 7, 2 * CTRL_IFACE_COOKIE_LEN + 1,
+ interfaces->ctrl_iface_cookie,
+ CTRL_IFACE_COOKIE_LEN);
+ reply_len = 7 + 2 * CTRL_IFACE_COOKIE_LEN;
goto send_reply;
}
if (os_strncmp(buf, "COOKIE=", 7) != 0 ||
- hexstr2bin(buf + 7, lcookie, COOKIE_LEN) < 0) {
+ hexstr2bin(buf + 7, lcookie, CTRL_IFACE_COOKIE_LEN) < 0) {
wpa_printf(MSG_DEBUG,
"CTRL: No cookie in the request - drop request");
os_free(reply);
return;
}
- if (os_memcmp(gcookie, lcookie, COOKIE_LEN) != 0) {
+ if (os_memcmp(interfaces->ctrl_iface_cookie, lcookie,
+ CTRL_IFACE_COOKIE_LEN) != 0) {
wpa_printf(MSG_DEBUG,
"CTRL: Invalid cookie in the request - drop request");
os_free(reply);
return;
}
- buf += 7 + 2 * COOKIE_LEN;
+ buf += 7 + 2 * CTRL_IFACE_COOKIE_LEN;
while (*buf == ' ')
buf++;
#endif /* CONFIG_CTRL_IFACE_UDP */
@@ -4317,7 +4952,7 @@ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
}
}
- os_get_random(gcookie, COOKIE_LEN);
+ os_get_random(interface->ctrl_iface_cookie, CTRL_IFACE_COOKIE_LEN);
#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
hints.ai_flags = AI_PASSIVE;
@@ -4367,6 +5002,8 @@ try_again:
return -1;
}
+ wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
+
return 0;
fail:
@@ -4469,6 +5106,8 @@ fail:
eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive,
interface, NULL);
+ wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
+
return 0;
fail:
@@ -4538,37 +5177,43 @@ static int hostapd_ctrl_check_event_enabled(struct wpa_ctrl_dst *dst,
}
-static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
- enum wpa_msg_type type,
- const char *buf, size_t len)
+static void hostapd_ctrl_iface_send_internal(int sock, struct dl_list *ctrl_dst,
+ const char *ifname, int level,
+ const char *buf, size_t len)
{
struct wpa_ctrl_dst *dst, *next;
- struct dl_list *ctrl_dst;
struct msghdr msg;
- int idx;
- struct iovec io[2];
+ int idx, res;
+ struct iovec io[5];
char levelstr[10];
- int s;
- if (type != WPA_MSG_ONLY_GLOBAL) {
- s = hapd->ctrl_sock;
- ctrl_dst = &hapd->ctrl_dst;
- } else {
- s = hapd->iface->interfaces->global_ctrl_sock;
- ctrl_dst = &hapd->iface->interfaces->global_ctrl_dst;
- }
-
- if (s < 0 || dl_list_empty(ctrl_dst))
+ if (sock < 0 || dl_list_empty(ctrl_dst))
return;
- os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
- io[0].iov_base = levelstr;
- io[0].iov_len = os_strlen(levelstr);
- io[1].iov_base = (char *) buf;
- io[1].iov_len = len;
+ res = os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
+ if (os_snprintf_error(sizeof(levelstr), res))
+ return;
+ idx = 0;
+ if (ifname) {
+ io[idx].iov_base = "IFNAME=";
+ io[idx].iov_len = 7;
+ idx++;
+ io[idx].iov_base = (char *) ifname;
+ io[idx].iov_len = os_strlen(ifname);
+ idx++;
+ io[idx].iov_base = " ";
+ io[idx].iov_len = 1;
+ idx++;
+ }
+ io[idx].iov_base = levelstr;
+ io[idx].iov_len = os_strlen(levelstr);
+ idx++;
+ io[idx].iov_base = (char *) buf;
+ io[idx].iov_len = len;
+ idx++;
os_memset(&msg, 0, sizeof(msg));
msg.msg_iov = io;
- msg.msg_iovlen = 2;
+ msg.msg_iovlen = idx;
idx = 0;
dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) {
@@ -4578,22 +5223,16 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
&dst->addr, dst->addrlen);
msg.msg_name = &dst->addr;
msg.msg_namelen = dst->addrlen;
- if (sendmsg(s, &msg, 0) < 0) {
+ if (sendmsg(sock, &msg, 0) < 0) {
int _errno = errno;
wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: "
"%d - %s",
idx, errno, strerror(errno));
dst->errors++;
if (dst->errors > 10 || _errno == ENOENT) {
- if (type != WPA_MSG_ONLY_GLOBAL)
- hostapd_ctrl_iface_detach(
- hapd, &dst->addr,
- dst->addrlen);
- else
- hostapd_global_ctrl_iface_detach(
- hapd->iface->interfaces,
- &dst->addr,
- dst->addrlen);
+ ctrl_iface_detach(ctrl_dst,
+ &dst->addr,
+ dst->addrlen);
}
} else
dst->errors = 0;
@@ -4602,4 +5241,25 @@ static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
}
}
+
+static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level,
+ enum wpa_msg_type type,
+ const char *buf, size_t len)
+{
+ if (type != WPA_MSG_NO_GLOBAL) {
+ hostapd_ctrl_iface_send_internal(
+ hapd->iface->interfaces->global_ctrl_sock,
+ &hapd->iface->interfaces->global_ctrl_dst,
+ type != WPA_MSG_PER_INTERFACE ?
+ NULL : hapd->conf->iface,
+ level, buf, len);
+ }
+
+ if (type != WPA_MSG_ONLY_GLOBAL) {
+ hostapd_ctrl_iface_send_internal(
+ hapd->ctrl_sock, &hapd->ctrl_dst,
+ NULL, level, buf, len);
+ }
+}
+
#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/contrib/wpa/hostapd/defconfig b/contrib/wpa/hostapd/defconfig
index 01871c951f3..666447e4ab4 100644
--- a/contrib/wpa/hostapd/defconfig
+++ b/contrib/wpa/hostapd/defconfig
@@ -44,15 +44,9 @@ CONFIG_LIBNL32=y
# Driver interface for no driver (e.g., RADIUS server only)
#CONFIG_DRIVER_NONE=y
-# IEEE 802.11F/IAPP
-CONFIG_IAPP=y
-
# WPA2/IEEE 802.11i RSN pre-authentication
CONFIG_RSN_PREAUTH=y
-# IEEE 802.11w (management frame protection)
-CONFIG_IEEE80211W=y
-
# Support Operating Channel Validation
#CONFIG_OCV=y
@@ -154,9 +148,6 @@ CONFIG_IPV6=y
# the IEEE 802.11 Management capability (e.g., FreeBSD/net80211)
#CONFIG_DRIVER_RADIUS_ACL=y
-# IEEE 802.11n (High Throughput) support
-#CONFIG_IEEE80211N=y
-
# Wireless Network Management (IEEE Std 802.11v-2011)
# Note: This is experimental and not complete implementation.
#CONFIG_WNM=y
@@ -355,12 +346,12 @@ CONFIG_IPV6=y
# * ath10k
#
# For more details refer to:
-# http://wireless.kernel.org/en/users/Documentation/acs
+# https://wireless.wiki.kernel.org/en/users/documentation/acs
#
#CONFIG_ACS=y
# Multiband Operation support
-# These extentions facilitate efficient use of multiple frequency bands
+# These extensions facilitate efficient use of multiple frequency bands
# available to the AP and the devices that may associate with it.
#CONFIG_MBO=y
@@ -389,3 +380,25 @@ CONFIG_IPV6=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
+
+# Wired equivalent privacy (WEP)
+# WEP is an obsolete cryptographic data confidentiality algorithm that is not
+# considered secure. It should not be used for anything anymore. The
+# functionality needed to use WEP is available in the current hostapd
+# release under this optional build parameter. This functionality is subject to
+# be completely removed in a future release.
+#CONFIG_WEP=y
+
+# Remove all TKIP functionality
+# TKIP is an old cryptographic data confidentiality algorithm that is not
+# considered secure. It should not be used anymore. For now, the default hostapd
+# build includes this to allow mixed mode WPA+WPA2 networks to be enabled, but
+# that functionality is subject to be removed in the future.
+#CONFIG_NO_TKIP=y
+
+# Pre-Association Security Negotiation (PASN)
+# Experimental implementation based on IEEE P802.11z/D2.6 and the protocol
+# design is still subject to change. As such, this should not yet be enabled in
+# production use.
+# This requires CONFIG_IEEE80211W=y to be enabled, too.
+#CONFIG_PASN=y
diff --git a/contrib/wpa/hostapd/hostapd.android.rc b/contrib/wpa/hostapd/hostapd.android.rc
new file mode 100644
index 00000000000..26a87b80891
--- /dev/null
+++ b/contrib/wpa/hostapd/hostapd.android.rc
@@ -0,0 +1,19 @@
+#
+# init.rc fragment for hostapd on Android
+# Copyright (c) 2002-2016, Jouni Malinen
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+#
+
+on post-fs-data
+ mkdir /data/misc/wifi/hostapd 0770 wifi wifi
+
+service hostapd /vendor/bin/hostapd \
+ /data/misc/wifi/hostapd.conf
+ class main
+ user wifi
+ writepid /data/misc/wifi/hostapd.pid
+ group wifi
+ disabled
+ oneshot
diff --git a/contrib/wpa/hostapd/hostapd.conf b/contrib/wpa/hostapd/hostapd.conf
index ce3ecdddf15..b5d15061f85 100644
--- a/contrib/wpa/hostapd/hostapd.conf
+++ b/contrib/wpa/hostapd/hostapd.conf
@@ -41,7 +41,6 @@ interface=wlan0
# bit 2 (4) = RADIUS
# bit 3 (8) = WPA
# bit 4 (16) = driver interface
-# bit 5 (32) = IAPP
# bit 6 (64) = MLME
#
# Levels (minimum value for logged events):
@@ -73,7 +72,7 @@ ctrl_interface=/var/run/hostapd
# run as non-root users. However, since the control interface can be used to
# change the network configuration, this access needs to be protected in many
# cases. By default, hostapd is configured to use gid 0 (root). If you
-# want to allow non-root users to use the contron interface, add a new group
+# want to allow non-root users to use the control interface, add a new group
# and change this value to match with that group. Add users that should have
# control interface access to this group.
#
@@ -147,7 +146,8 @@ ssid=test
# Operation mode (a = IEEE 802.11a (5 GHz), b = IEEE 802.11b (2.4 GHz),
# g = IEEE 802.11g (2.4 GHz), ad = IEEE 802.11ad (60 GHz); a/g options are used
# with IEEE 802.11n (HT), too, to specify band). For IEEE 802.11ac (VHT), this
-# needs to be set to hw_mode=a. When using ACS (see channel parameter), a
+# needs to be set to hw_mode=a. For IEEE 802.11ax (HE) on 6 GHz this needs
+# to be set to hw_mode=a. When using ACS (see channel parameter), a
# special value "any" can be used to indicate that any support band can be used.
# This special case is currently supported only with drivers with which
# offloaded ACS is used.
@@ -164,8 +164,14 @@ hw_mode=g
# which will enable the ACS survey based algorithm.
channel=1
+# Global operating class (IEEE 802.11, Annex E, Table E-4)
+# This option allows hostapd to specify the operating class of the channel
+# configured with the channel parameter. channel and op_class together can
+# uniquely identify channels across different bands, including the 6 GHz band.
+#op_class=131
+
# ACS tuning - Automatic Channel Selection
-# See: http://wireless.kernel.org/en/users/Documentation/acs
+# See: https://wireless.wiki.kernel.org/en/users/documentation/acs
#
# You can customize the ACS survey algorithm with following variables:
#
@@ -199,11 +205,26 @@ channel=1
#chanlist=100 104 108 112 116
#chanlist=1 6 11-13
+# Frequency list restriction. This option allows hostapd to select one of the
+# provided frequencies when a frequency should be automatically selected.
+# Frequency list can be provided as range using hyphen ('-') or individual
+# frequencies can be specified by comma (',') separated values
+# Default: all frequencies allowed in selected hw_mode
+#freqlist=2437,5955,5975
+#freqlist=2437,5985-6105
+
# 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
+# Include only preferred scan channels from 6 GHz band for ACS
+# This option can be used to include only preferred scan channels in the 6 GHz
+# band. This can be useful in particular for devices that operate only a 6 GHz
+# BSS without a collocated 2.4/5 GHz BSS.
+# Default behavior is to include all PSC and non-PSC channels.
+#acs_exclude_6ghz_non_psc=1
+
# Beacon interval in kus (1.024 ms) (default: 100; range 15..65535)
beacon_int=100
@@ -258,6 +279,8 @@ fragm_threshold=-1
# beacon_rate=ht:
# VHT:
# beacon_rate=vht:
+# HE:
+# beacon_rate=he:
#
# For example, beacon_rate=10 for 1 Mbps or beacon_rate=60 for 6 Mbps (OFDM).
#beacon_rate=10
@@ -550,6 +573,10 @@ wmm_ac_vo_acm=0
# Default: 1 (enabled)
#broadcast_deauth=1
+# Get notifications for received Management frames on control interface
+# Default: 0 (disabled)
+#notify_mgmt_frames=0
+
##### IEEE 802.11n related configuration ######################################
# ieee80211n: Whether IEEE 802.11n (HT) is enabled
@@ -559,6 +586,9 @@ wmm_ac_vo_acm=0
# Note: hw_mode=g (2.4 GHz) and hw_mode=a (5 GHz) is used to specify the band.
#ieee80211n=1
+# disable_11n: Boolean (0/1) to disable HT for a specific BSS
+#disable_11n=0
+
# ht_capab: HT capabilities (list of flags)
# LDPC coding capability: [LDPC] = supported
# Supported channel width set: [HT40-] = both 20 MHz and 40 MHz with secondary
@@ -577,8 +607,6 @@ wmm_ac_vo_acm=0
# channels if needed or creation of 40 MHz channel maybe rejected based
# on overlapping BSSes. These changes are done automatically when hostapd
# is setting up the 40 MHz channel.
-# Spatial Multiplexing (SM) Power Save: [SMPS-STATIC] or [SMPS-DYNAMIC]
-# (SMPS disabled if neither is set)
# HT-greenfield: [GF] (disabled if not set)
# Short GI for 20 MHz: [SHORT-GI-20] (disabled if not set)
# Short GI for 40 MHz: [SHORT-GI-40] (disabled if not set)
@@ -613,6 +641,9 @@ wmm_ac_vo_acm=0
# Note: hw_mode=a is used to specify that 5 GHz band is used with VHT.
#ieee80211ac=1
+# disable_11ac: Boolean (0/1) to disable VHT for a specific BSS
+#disable_11ac=0
+
# vht_capab: VHT capabilities (list of flags)
#
# vht_max_mpdu_len: [MAX-MPDU-7991] [MAX-MPDU-11454]
@@ -767,6 +798,9 @@ wmm_ac_vo_acm=0
# 1 = enabled
#ieee80211ax=1
+# disable_11ax: Boolean (0/1) to disable HE for a specific BSS
+#disable_11ax=0
+
#he_su_beamformer: HE single user beamformer support
# 0 = not supported (default)
# 1 = supported
@@ -785,6 +819,9 @@ wmm_ac_vo_acm=0
# he_bss_color: BSS color (1-63)
#he_bss_color=1
+# he_bss_color_partial: BSS color AID equation
+#he_bss_color_partial=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
@@ -794,12 +831,27 @@ wmm_ac_vo_acm=0
# 1 = required
#he_twt_required=0
+#he_twt_responder: Whether TWT (HE) responder is enabled
+# 0 = disabled
+# 1 = enabled if supported by the driver (default)
+#he_twt_responder=1
+
#he_rts_threshold: Duration of STA transmission
# 0 = not set (default)
# unsigned integer = duration in units of 16 us
#he_rts_threshold=0
# HE operating channel information; see matching vht_* parameters for details.
+# he_oper_centr_freq_seg0_idx field is used to indicate center frequency of 80
+# and 160 MHz bandwidth operation. In 80+80 MHz operation, it is the center
+# frequency of the lower frequency segment. he_oper_centr_freq_seg1_idx field
+# is used only with 80+80 MHz bandwidth operation and it is used to transmit
+# the center frequency of the second segment.
+# On the 6 GHz band the center freq calculation starts from 5.950 GHz offset.
+# For example idx=3 would result in 5965 MHz center frequency. In addition,
+# he_oper_chwidth is ignored, and the channel width is derived from the
+# configured operating class or center frequency indexes (see
+# IEEE P802.11ax/D6.1 Annex E, Table E-4).
#he_oper_chwidth
#he_oper_centr_freq_seg0_idx
#he_oper_centr_freq_seg1_idx
@@ -835,10 +887,82 @@ wmm_ac_vo_acm=0
#he_mu_edca_ac_vo_timer=255
# Spatial Reuse Parameter Set
+#
+# SR Control field value
+# B0 = PSR Disallowed
+# B1 = Non-SRG OBSS PD SR Disallowed
+# B2 = Non-SRG Offset Present
+# B3 = SRG Information Present
+# B4 = HESIGA_Spatial_reuse_value15_allowed
#he_spr_sr_control
+#
+# Non-SRG OBSS PD Max Offset (included if he_spr_sr_control B2=1)
#he_spr_non_srg_obss_pd_max_offset
+
+# SRG OBSS PD Min Offset (included if he_spr_sr_control B3=1)
#he_spr_srg_obss_pd_min_offset
+#
+# SRG OBSS PD Max Offset (included if he_spr_sr_control B3=1)
#he_spr_srg_obss_pd_max_offset
+#
+# SPR SRG BSS Color (included if he_spr_sr_control B3=1)
+# This config represents SRG BSS Color Bitmap field of Spatial Reuse Parameter
+# Set element that indicates the BSS color values used by members of the
+# SRG of which the transmitting STA is a member. The value is in range of 0-63.
+#he_spr_srg_bss_colors=1 2 10 63
+#
+# SPR SRG Partial BSSID (included if he_spr_sr_control B3=1)
+# This config represents SRG Partial BSSID Bitmap field of Spatial Reuse
+# Parameter Set element that indicates the Partial BSSID values used by members
+# of the SRG of which the transmitting STA is a member. The value range
+# corresponds to one of the 64 possible values of BSSID[39:44], where the lowest
+# numbered bit corresponds to Partial BSSID value 0 and the highest numbered bit
+# corresponds to Partial BSSID value 63.
+#he_spr_srg_partial_bssid=0 1 3 63
+#
+#he_6ghz_max_mpdu: Maximum MPDU Length of HE 6 GHz band capabilities.
+# Indicates maximum MPDU length
+# 0 = 3895 octets
+# 1 = 7991 octets
+# 2 = 11454 octets (default)
+#he_6ghz_max_mpdu=2
+#
+#he_6ghz_max_ampdu_len_exp: Maximum A-MPDU Length Exponent of HE 6 GHz band
+# capabilities. Indicates the maximum length of A-MPDU pre-EOF padding that
+# the STA can receive. This field is an integer in the range of 0 to 7.
+# The length defined by this field is equal to
+# 2 pow(13 + Maximum A-MPDU Length Exponent) -1 octets
+# 0 = AMPDU length of 8k
+# 1 = AMPDU length of 16k
+# 2 = AMPDU length of 32k
+# 3 = AMPDU length of 65k
+# 4 = AMPDU length of 131k
+# 5 = AMPDU length of 262k
+# 6 = AMPDU length of 524k
+# 7 = AMPDU length of 1048k (default)
+#he_6ghz_max_ampdu_len_exp=7
+#
+#he_6ghz_rx_ant_pat: Rx Antenna Pattern Consistency of HE 6 GHz capability.
+# Indicates the possibility of Rx antenna pattern change
+# 0 = Rx antenna pattern might change during the lifetime of an association
+# 1 = Rx antenna pattern does not change during the lifetime of an association
+# (default)
+#he_6ghz_rx_ant_pat=1
+#
+#he_6ghz_tx_ant_pat: Tx Antenna Pattern Consistency of HE 6 GHz capability.
+# Indicates the possibility of Tx antenna pattern change
+# 0 = Tx antenna pattern might change during the lifetime of an association
+# 1 = Tx antenna pattern does not change during the lifetime of an association
+# (default)
+#he_6ghz_tx_ant_pat=1
+
+# Unsolicited broadcast Probe Response transmission settings
+# This is for the 6 GHz band only. If the interval is set to a non-zero value,
+# the AP schedules unsolicited broadcast Probe Response frames to be
+# transmitted for in-band discovery. Refer to
+# IEEE P802.11ax/D8.0 26.17.2.3.2, AP behavior for fast passive scanning.
+# Valid range: 0..20 TUs; default is 0 (disabled)
+#unsol_bcast_probe_resp_interval=0
##### IEEE 802.1X-2004 related configuration ##################################
@@ -877,6 +1001,8 @@ eapol_key_index_workaround=0
# EAP reauthentication period in seconds (default: 3600 seconds; 0 = disable
# reauthentication).
+# Note: Reauthentications may enforce a disconnection, check the related
+# parameter wpa_deny_ptk0_rekey for details.
#eap_reauth_period=3600
# Use PAE group address (01:80:c2:00:00:03) instead of individual target
@@ -1012,7 +1138,7 @@ eap_server=0
#check_crl=1
# Specify whether to ignore certificate CRL validity time mismatches with
-# errors X509_V_ERR_CERT_HAS_EXPIRED and X509_V_ERR_CERT_NOT_YET_VALID.
+# errors X509_V_ERR_CRL_HAS_EXPIRED and X509_V_ERR_CRL_NOT_YET_VALID.
#
# 0 = ignore errors
# 1 = do not ignore errors (default)
@@ -1081,6 +1207,12 @@ eap_server=0
# [ENABLE-TLSv1.3] = enable TLSv1.3 (experimental - disabled by default)
#tls_flags=[flag1][flag2]...
+# Maximum number of EAP message rounds with data (default: 100)
+#max_auth_rounds=100
+
+# Maximum number of short EAP message rounds (default: 50)
+#max_auth_rounds_short=50
+
# Cached OCSP stapling response (DER encoded)
# If set, this file is sent as a certificate status response by the EAP server
# if the EAP peer requests certificate status in the ClientHello message.
@@ -1167,7 +1299,7 @@ eap_server=0
# should be unique across all issuing servers. In theory, this is a variable
# length field, but due to some existing implementations requiring A-ID to be
# 16 octets in length, it is strongly recommended to use that length for the
-# field to provid interoperability with deployed peer implementations. This
+# field to provide interoperability with deployed peer implementations. This
# field is configured in hex format.
#eap_fast_a_id=101112131415161718191a1b1c1d1e1f
@@ -1194,6 +1326,8 @@ eap_server=0
# EAP-TEAP authentication type
# 0 = inner EAP (default)
# 1 = Basic-Password-Auth
+# 2 = Do not require Phase 2 authentication if client can be authenticated
+# during Phase 1
#eap_teap_auth=0
# EAP-TEAP authentication behavior when using PAC
@@ -1201,6 +1335,20 @@ eap_server=0
# 1 = skip inner authentication (inner EAP/Basic-Password-Auth)
#eap_teap_pac_no_inner=0
+# EAP-TEAP behavior with Result TLV
+# 0 = include with Intermediate-Result TLV (default)
+# 1 = send in a separate message (for testing purposes)
+#eap_teap_separate_result=0
+
+# EAP-TEAP identities
+# 0 = allow any identity type (default)
+# 1 = require user identity
+# 2 = require machine identity
+# 3 = request user identity; accept either user or machine identity
+# 4 = request machine identity; accept either user or machine identity
+# 5 = require both user and machine identity
+#eap_teap_id=0
+
# EAP-SIM and EAP-AKA protected success/failure indication using AT_RESULT_IND
# (default: 0 = disabled).
#eap_sim_aka_result_ind=1
@@ -1223,11 +1371,6 @@ eap_server=0
# Whether to enable ERP on the EAP server.
#eap_server_erp=1
-##### IEEE 802.11f - Inter-Access Point Protocol (IAPP) #######################
-
-# Interface to be used for IAPP broadcast packets
-#iapp_interface=eth0
-
##### RADIUS client configuration #############################################
# for IEEE 802.1X with external Authentication Server, IEEE 802.11
@@ -1261,6 +1404,12 @@ own_ip_addr=127.0.0.1
# used, e.g., when the device has multiple IP addresses.
#radius_client_addr=127.0.0.1
+# RADIUS client forced local interface. Helps run properly with VRF
+# Default is none set which allows the network stack to pick the appropriate
+# interface automatically.
+# Example below binds to eth0
+#radius_client_dev=eth0
+
# RADIUS authentication server
#auth_server_addr=127.0.0.1
#auth_server_port=1812
@@ -1466,6 +1615,17 @@ own_ip_addr=127.0.0.1
# wpa_key_mgmt=SAE for WPA3-Personal instead of wpa_key_mgmt=WPA-PSK).
#wpa=2
+# Extended Key ID support for Individually Addressed frames
+#
+# Extended Key ID allows to rekey PTK keys without the impacts the "normal"
+# PTK rekeying with only a single Key ID 0 has. It can only be used when the
+# driver supports it and RSN/WPA2 is used with a CCMP/GCMP pairwise cipher.
+#
+# 0 = force off, i.e., use only Key ID 0 (default)
+# 1 = enable and use Extended Key ID support when possible
+# 2 = identical to 1 but start with Key ID 1 when possible
+#extended_key_id=0
+
# 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
# (8..63 characters) that will be converted to PSK. This conversion uses SSID
@@ -1566,8 +1726,26 @@ own_ip_addr=127.0.0.1
# Maximum lifetime for PTK in seconds. This can be used to enforce rekeying of
# PTK to mitigate some attacks against TKIP deficiencies.
+# Warning: PTK rekeying is buggy with many drivers/devices and with such
+# devices, the only secure method to rekey the PTK without Extended Key ID
+# support requires a disconnection. Check the related parameter
+# wpa_deny_ptk0_rekey for details.
#wpa_ptk_rekey=600
+# Workaround for PTK rekey issues
+#
+# PTK0 rekeys (rekeying the PTK without "Extended Key ID for Individually
+# Addressed Frames") can degrade the security and stability with some cards.
+# To avoid such issues hostapd can replace those PTK rekeys (including EAP
+# reauthentications) with disconnects.
+#
+# Available options:
+# 0 = always rekey when configured/instructed (default)
+# 1 = only rekey when the local driver is explicitly indicating it can perform
+# this operation without issues
+# 2 = never allow PTK0 rekeys
+#wpa_deny_ptk0_rekey=0
+
# 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)
@@ -1618,6 +1796,12 @@ own_ip_addr=127.0.0.1
# 1 = optional
# 2 = required
#ieee80211w=0
+# The most common configuration options for this based on the PMF (protected
+# management frames) certification program are:
+# PMF enabled: ieee80211w=1 and wpa_key_mgmt=WPA-EAP WPA-EAP-SHA256
+# PMF required: ieee80211w=2 and wpa_key_mgmt=WPA-EAP-SHA256
+# (and similarly for WPA-PSK and WPA-PSK-SHA256 if WPA2-Personal is used)
+# WPA3-Personal-only mode: ieee80211w=2 and wpa_key_mgmt=SAE
# Group management cipher suite
# Default: AES-128-CMAC (BIP)
@@ -1630,6 +1814,13 @@ own_ip_addr=127.0.0.1
# available in deployed devices.
#group_mgmt_cipher=AES-128-CMAC
+# Beacon Protection (management frame protection for Beacon frames)
+# This depends on management frame protection being enabled (ieee80211w != 0)
+# and beacon protection support indication from the driver.
+# 0 = disabled (default)
+# 1 = enabled
+#beacon_prot=0
+
# Association SA Query maximum timeout (in TU = 1.024 ms; for MFP)
# (maximum time to wait for a SA Query response)
# dot11AssociationSAQueryMaximumTimeout, 1...4294967295
@@ -1642,9 +1833,25 @@ own_ip_addr=127.0.0.1
# ocv: Operating Channel Validation
# This is a countermeasure against multi-channel man-in-the-middle attacks.
+# Enabling this depends on the driver's support for OCV when the driver SME is
+# used. If hostapd SME is used, this will be enabled just based on this
+# configuration.
# Enabling this automatically also enables ieee80211w, if not yet enabled.
# 0 = disabled (default)
# 1 = enabled
+# 2 = enabled in workaround mode - Allow STA that claims OCV capability to
+# connect even if the STA doesn't send OCI or negotiate PMF. This
+# workaround is to improve interoperability with legacy STAs which are
+# wrongly copying reserved bits of RSN capabilities from the AP's
+# RSNE into (Re)Association Request frames. When this configuration is
+# enabled, the AP considers STA is OCV capable only when the STA indicates
+# MFP capability in (Re)Association Request frames and sends OCI in
+# EAPOL-Key msg 2/4/FT Reassociation Request frame/FILS (Re)Association
+# Request frame; otherwise, the AP disables OCV for the current connection
+# with the STA. Enabling this workaround mode reduced OCV protection to
+# some extend since it allows misbehavior to go through. As such, this
+# should be enabled only if interoperability with misbehaving STAs is
+# needed.
#ocv=1
# disable_pmksa_caching: Disable PMKSA caching
@@ -1676,7 +1883,7 @@ own_ip_addr=127.0.0.1
# be followed by optional peer MAC address (dot11RSNAConfigPasswordPeerMac) and
# by optional password identifier (dot11RSNAConfigPasswordIdentifier). In
# addition, an optional VLAN ID specification can be used to bind the station
-# to the specified VLAN whenver the specific SAE password entry is used.
+# to the specified VLAN whenever the specific SAE password entry is used.
#
# 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
@@ -1691,7 +1898,8 @@ own_ip_addr=127.0.0.1
# special meaning of removing all previously added entries.
#
# sae_password uses the following encoding:
-#[|mac=][|vlanid=][|id=]
+#[|mac=][|vlanid=]
+#[|pk=][|id=]
# Examples:
#sae_password=secret
#sae_password=really secret|mac=ff:ff:ff:ff:ff:ff
@@ -1701,10 +1909,11 @@ own_ip_addr=127.0.0.1
# 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
+#sae_anti_clogging_threshold=5 (deprecated)
+#anti_clogging_threshold=5
# Maximum number of SAE synchronization errors (dot11RSNASAESync)
-# The offending SAe peer will be disconnected if more than this many
+# The offending SAE peer will be disconnected if more than this many
# synchronization errors happen.
#sae_sync=5
@@ -1729,6 +1938,23 @@ own_ip_addr=127.0.0.1
# MFP while SAE stations are required to negotiate MFP if sae_require_mfp=1.
#sae_require_mfp=0
+# SAE Confirm behavior
+# By default, AP will send out only SAE Commit message in response to a received
+# SAE Commit message. This parameter can be set to 1 to override that behavior
+# to send both SAE Commit and SAE Confirm messages without waiting for the STA
+# to send its SAE Confirm message first.
+#sae_confirm_immediate=0
+
+# SAE mechanism for PWE derivation
+# 0 = hunting-and-pecking loop only (default without password identifier)
+# 1 = hash-to-element only (default with password identifier)
+# 2 = both hunting-and-pecking loop and hash-to-element enabled
+# Note: The default value is likely to change from 0 to 2 once the new
+# hash-to-element mechanism has received more interoperability testing.
+# When using SAE password identifier, the hash-to-element mechanism is used
+# regardless of the sae_pwe parameter value.
+#sae_pwe=0
+
# FILS Cache Identifier (16-bit value in hexdump format)
#fils_cache_id=0011
@@ -1753,6 +1979,19 @@ own_ip_addr=127.0.0.1
# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-10
#owe_groups=19 20 21
+# OWE PTK derivation workaround
+# Initial OWE implementation used SHA256 when deriving the PTK for all OWE
+# groups. This was supposed to change to SHA384 for group 20 and SHA512 for
+# group 21. This parameter can be used to enable workaround for interoperability
+# with stations that use SHA256 with groups 20 and 21. By default (0) only the
+# appropriate hash function is accepted. When workaround is enabled (1), the
+# appropriate hash function is tried first and if that fails, SHA256-based PTK
+# derivation is attempted. This workaround can result in reduced security for
+# groups 20 and 21, but is required for interoperability with older
+# implementations. There is no impact to group 19 behavior. The workaround is
+# disabled by default and can be enabled by uncommenting the following line.
+#owe_ptk_workaround=1
+
# OWE transition mode configuration
# Pointer to the matching open/OWE BSS
#owe_transition_bssid=
@@ -1790,6 +2029,45 @@ own_ip_addr=127.0.0.1
# default: 30 TUs (= 30.72 milliseconds)
#fils_hlp_wait_time=30
+# FILS Discovery frame transmission minimum and maximum interval settings.
+# If fils_discovery_max_interval is non-zero, the AP enables FILS Discovery
+# frame transmission. These values use TUs as the unit and have allowed range
+# of 0-10000. fils_discovery_min_interval defaults to 20.
+#fils_discovery_min_interval=20
+#fils_discovery_max_interval=0
+
+# Transition Disable indication
+# The AP can notify authenticated stations to disable transition mode in their
+# network profiles when the network has completed transition steps, i.e., once
+# sufficiently large number of APs in the ESS have been updated to support the
+# more secure alternative. When this indication is used, the stations are
+# expected to automatically disable transition mode and less secure security
+# options. This includes use of WEP, TKIP (including use of TKIP as the group
+# cipher), and connections without PMF.
+# Bitmap bits:
+# bit 0 (0x01): WPA3-Personal (i.e., disable WPA2-Personal = WPA-PSK and only
+# allow SAE to be used)
+# bit 1 (0x02): SAE-PK (disable SAE without use of SAE-PK)
+# bit 2 (0x04): WPA3-Enterprise (move to requiring PMF)
+# bit 3 (0x08): Enhanced Open (disable use of open network; require OWE)
+# (default: 0 = do not include Transition Disable KDE)
+#transition_disable=0x01
+
+# PASN ECDH groups
+# PASN implementations are required to support group 19 (NIST P-256). If this
+# parameter is not set, only group 19 is supported 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
+#pasn_groups=19 20 21
+
+# PASN comeback after time in TUs
+# In case the AP is temporarily unable to handle a PASN authentication exchange
+# due to a too large number of parallel operations, this value indicates to the
+# peer after how many TUs it can try the PASN exchange again.
+# (default: 10 TUs)
+#pasn_comeback_after=10
+
##### IEEE 802.11r configuration ##############################################
# Mobility Domain identifier (dot11FTMobilityDomainID, MDID)
@@ -1833,7 +2111,7 @@ own_ip_addr=127.0.0.1
# 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.
+# temporarily blocked (see rkh_neg_timeout).
#r0kh=ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff
# List of R1KHs in the same Mobility Domain
@@ -1889,7 +2167,7 @@ own_ip_addr=127.0.0.1
#ft_psk_generate_local=0
##### Neighbor table ##########################################################
-# Maximum number of entries kept in AP table (either for neigbor table or for
+# Maximum number of entries kept in AP table (either for neighbor table or for
# detecting Overlapping Legacy BSS Condition). The oldest entry will be
# removed when adding a new entry that would make the list grow over this
# limit. Note! WFA certification for IEEE 802.11g requires that OLBC is
@@ -2143,6 +2421,13 @@ own_ip_addr=127.0.0.1
#wps_nfc_dh_privkey: Hexdump of DH Private Key
#wps_nfc_dev_pw: Hexdump of Device Password
+# Application Extension attribute for Beacon and Probe Response frames
+# This parameter can be used to add application extension into WPS IE. The
+# contents of this parameter starts with 16-octet (32 hexdump characters) of
+# UUID to identify the specific application and that is followed by the actual
+# application specific data.
+#wps_application_ext=
+
##### Wi-Fi Direct (P2P) ######################################################
# Enable P2P Device management
@@ -2151,6 +2436,31 @@ own_ip_addr=127.0.0.1
# Allow cross connection
#allow_cross_connection=1
+##### Device Provisioning Protocol (DPP) ######################################
+
+# Name for Enrollee's DPP Configuration Request
+#dpp_name=Test
+
+# MUD URL for Enrollee's DPP Configuration Request (optional)
+#dpp_mud_url=https://example.com/mud
+
+#dpp_connector
+#dpp_netaccesskey
+#dpp_netaccesskey_expiry
+#dpp_csign
+#dpp_controller
+
+# Configurator Connectivity indication
+# 0: no Configurator is currently connected (default)
+# 1: advertise that a Configurator is available
+#dpp_configurator_connectivity=0
+
+# DPP PFS
+# 0: allow PFS to be used or not used (default)
+# 1: require PFS to be used (note: not compatible with DPP R1)
+# 2: do not allow PFS to be used
+#dpp_pfs=0
+
#### TDLS (IEEE 802.11z-2010) #################################################
# Prohibit use of TDLS in this BSS
@@ -2531,7 +2841,7 @@ own_ip_addr=127.0.0.1
# Default is 0 = OCE disabled
#oce=0
-# RSSI-based assocition rejection
+# RSSI-based association rejection
#
# Reject STA association if RSSI is below given threshold (in dBm)
# Allowed range: -60 to -90 dBm; default = 0 (rejection disabled)
@@ -2546,6 +2856,10 @@ own_ip_addr=127.0.0.1
# threshold (range: 0..255, default=30).
#rssi_reject_assoc_timeout=30
+# Ignore Probe Request frames if RSSI is below given threshold (in dBm)
+# Allowed range: -60 to -90 dBm; default = 0 (rejection disabled)
+#rssi_ignore_probe_request=-75
+
##### Fast Session Transfer (FST) support #####################################
#
# The options in this section are only available when the build configuration
@@ -2638,6 +2952,19 @@ own_ip_addr=127.0.0.1
# airtime.
#airtime_bss_limit=1
+##### EDMG support ############################################################
+#
+# Enable EDMG capability for AP mode in the 60 GHz band. Default value is false.
+# To configure channel bonding for an EDMG AP use edmg_channel below.
+# If enable_edmg is set and edmg_channel is not set, EDMG CB1 will be
+# configured.
+#enable_edmg=1
+#
+# Configure channel bonding for AP mode in the 60 GHz band.
+# This parameter is relevant only if enable_edmg is set.
+# Default value is 0 (no channel bonding).
+#edmg_channel=9
+
##### TESTING OPTIONS #########################################################
#
# The options in this section are only available when the build configuration
diff --git a/contrib/wpa/hostapd/hostapd.wpa_psk b/contrib/wpa/hostapd/hostapd.wpa_psk
index 166e59e9c64..2ce5ff2346d 100644
--- a/contrib/wpa/hostapd/hostapd.wpa_psk
+++ b/contrib/wpa/hostapd/hostapd.wpa_psk
@@ -7,9 +7,15 @@
# keyid=
# An optional VLAN ID can be specified by prefixing the line with
# vlanid=.
+# An optional WPS tag can be added by prefixing the line with
+# wps=<0/1> (default: 0). Any matching entry with that tag will be used when
+# generating a PSK for a WPS Enrollee instead of generating a new random
+# per-Enrollee PSK.
00:00:00:00:00:00 secret passphrase
00:11:22:33:44:55 another passphrase
00:22:33:44:55:66 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
keyid=example_id 00:11:22:33:44:77 passphrase with keyid
vlanid=3 00:00:00:00:00:00 passphrase with vlanid
+wps=1 00:00:00:00:00:00 passphrase for WPS
+wps=1 11:22:33:44:55:00 dev-specific passphrase for WPS
00:00:00:00:00:00 another passphrase for all STAs
diff --git a/contrib/wpa/hostapd/hostapd_cli.c b/contrib/wpa/hostapd/hostapd_cli.c
index 046024390f8..eaa628ad067 100644
--- a/contrib/wpa/hostapd/hostapd_cli.c
+++ b/contrib/wpa/hostapd/hostapd_cli.c
@@ -54,7 +54,7 @@ static void usage(void)
fprintf(stderr, "%s\n", hostapd_cli_version);
fprintf(stderr,
"\n"
- "usage: hostapd_cli [-p] [-i] [-hvB] "
+ "usage: hostapd_cli [-p] [-i] [-hvBr] "
"[-a] \\\n"
" [-P] [-G] [command..]\n"
"\n"
@@ -68,6 +68,9 @@ static void usage(void)
" -a run in daemon mode executing the action file "
"based on events\n"
" from hostapd\n"
+ " -r try to reconnect when client socket is "
+ "disconnected.\n"
+ " This is useful only when used with -a.\n"
" -B run a daemon in the background\n"
" -i Interface to listen on (default: first "
"interface found in the\n"
@@ -401,7 +404,6 @@ static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
#endif /* CONFIG_TAXONOMY */
-#ifdef CONFIG_IEEE80211W
static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -414,7 +416,6 @@ static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]);
return wpa_ctrl_command(ctrl, buf);
}
-#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_WPS
@@ -974,7 +975,7 @@ static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
dir = opendir(ctrl_iface_dir);
if (dir == NULL) {
printf("Control interface directory '%s' could not be "
- "openned.\n", ctrl_iface_dir);
+ "opened.\n", ctrl_iface_dir);
return;
}
@@ -1226,14 +1227,15 @@ static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
char cmd[256];
int res;
- if (argc < 2 || argc > 3) {
+ if (argc < 2 || argc > 4) {
printf("Invalid vendor command\n"
- "usage: []\n");
+ "usage: [] [nested=<0|1>]\n");
return -1;
}
- res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s", argv[0], argv[1],
- argc == 3 ? argv[2] : "");
+ res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s%s%s", argv[0],
+ argv[1], argc >= 3 ? argv[2] : "",
+ argc == 4 ? " " : "", argc == 4 ? argv[3] : "");
if (os_snprintf_error(sizeof(cmd), res)) {
printf("Too long VENDOR command.\n");
return -1;
@@ -1311,24 +1313,17 @@ static int hostapd_cli_cmd_set_neighbor(struct wpa_ctrl *ctrl, int argc,
}
+static int hostapd_cli_cmd_show_neighbor(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "SHOW_NEIGHBOR");
+}
+
+
static int hostapd_cli_cmd_remove_neighbor(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
- char cmd[400];
- int res;
-
- if (argc != 2) {
- printf("Invalid remove_neighbor command: needs 2 arguments\n");
- return -1;
- }
-
- res = os_snprintf(cmd, sizeof(cmd), "REMOVE_NEIGHBOR %s %s",
- argv[0], argv[1]);
- if (os_snprintf_error(sizeof(cmd), res)) {
- printf("Too long REMOVE_NEIGHBOR command.\n");
- return -1;
- }
- return wpa_ctrl_command(ctrl, cmd);
+ return hostapd_cli_cmd(ctrl, "REMOVE_NEIGHBOR", 1, argc, argv);
}
@@ -1408,6 +1403,13 @@ static int hostapd_cli_cmd_dpp_bootstrap_info(struct wpa_ctrl *ctrl, int argc,
}
+static int hostapd_cli_cmd_dpp_bootstrap_set(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_SET", 1, argc, argv);
+}
+
+
static int hostapd_cli_cmd_dpp_auth_init(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
@@ -1470,6 +1472,37 @@ static int hostapd_cli_cmd_dpp_pkex_remove(struct wpa_ctrl *ctrl, int argc,
return hostapd_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv);
}
+
+#ifdef CONFIG_DPP2
+
+static int hostapd_cli_cmd_dpp_controller_start(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "DPP_CONTROLLER_START", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_controller_stop(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "DPP_CONTROLLER_STOP");
+}
+
+
+static int hostapd_cli_cmd_dpp_chirp(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "DPP_CHIRP", 1, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_dpp_stop_chirp(struct wpa_ctrl *ctrl, int argc,
+ char *argv[])
+{
+ return wpa_ctrl_command(ctrl, "DPP_STOP_CHIRP");
+}
+
+#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
@@ -1508,6 +1541,14 @@ static int hostapd_cli_cmd_reload_wpa_psk(struct wpa_ctrl *ctrl, int argc,
}
+#ifdef ANDROID
+static int hostapd_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+ return hostapd_cli_cmd(ctrl, "DRIVER", 1, argc, argv);
+}
+#endif /* ANDROID */
+
+
struct hostapd_cli_cmd {
const char *cmd;
int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
@@ -1542,10 +1583,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
{ "signature", hostapd_cli_cmd_signature, hostapd_complete_stations,
" = get taxonomy signature for a station" },
#endif /* CONFIG_TAXONOMY */
-#ifdef CONFIG_IEEE80211W
{ "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations,
" = send SA Query to a station" },
-#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_WPS
{ "wps_pin", hostapd_cli_cmd_wps_pin, NULL,
" [timeout] [addr] = add WPS Enrollee PIN" },
@@ -1637,8 +1676,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
{ "set_neighbor", hostapd_cli_cmd_set_neighbor, NULL,
" [lci=] [civic=] [stat]\n"
" = add AP to neighbor database" },
+ { "show_neighbor", hostapd_cli_cmd_show_neighbor, NULL,
+ " = show neighbor database entries" },
{ "remove_neighbor", hostapd_cli_cmd_remove_neighbor, NULL,
- " = remove AP from neighbor database" },
+ " [ssid=] = remove AP from neighbor database" },
{ "req_lci", hostapd_cli_cmd_req_lci, hostapd_complete_stations,
" = send LCI request to a station"},
{ "req_range", hostapd_cli_cmd_req_range, NULL,
@@ -1656,6 +1697,8 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
" = get DPP bootstrap URI" },
{ "dpp_bootstrap_info", hostapd_cli_cmd_dpp_bootstrap_info, NULL,
" = show DPP bootstrap information" },
+ { "dpp_bootstrap_set", hostapd_cli_cmd_dpp_bootstrap_set, NULL,
+ " [conf=..] [ssid=] [ssid_charset=#] [psk=] [pass=] [configurator=] [conn_status=#] [akm_use_selector=<0|1>] [group_id=..] [expiry=#] [csrattrs=..] = set DPP configurator parameters" },
{ "dpp_auth_init", hostapd_cli_cmd_dpp_auth_init, NULL,
"peer= [own=] = initiate DPP bootstrapping" },
{ "dpp_listen", hostapd_cli_cmd_dpp_listen, NULL,
@@ -1676,6 +1719,16 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
"add PKEX code" },
{ "dpp_pkex_remove", hostapd_cli_cmd_dpp_pkex_remove, NULL,
"*| = remove DPP pkex information" },
+#ifdef CONFIG_DPP2
+ { "dpp_controller_start", hostapd_cli_cmd_dpp_controller_start, NULL,
+ "[tcp_port=] [role=..] = start DPP controller" },
+ { "dpp_controller_stop", hostapd_cli_cmd_dpp_controller_stop, NULL,
+ "= stop DPP controller" },
+ { "dpp_chirp", hostapd_cli_cmd_dpp_chirp, NULL,
+ "own= iter= = start DPP chirp" },
+ { "dpp_stop_chirp", hostapd_cli_cmd_dpp_stop_chirp, NULL,
+ "= stop DPP chirp" },
+#endif /* CONFIG_DPP2 */
#endif /* CONFIG_DPP */
{ "accept_acl", hostapd_cli_cmd_accept_macacl, NULL,
"=Add/Delete/Show/Clear accept MAC ACL" },
@@ -1687,6 +1740,10 @@ static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
" [req_mode=] = send a Beacon report request to a station" },
{ "reload_wpa_psk", hostapd_cli_cmd_reload_wpa_psk, NULL,
"= reload wpa_psk_file only" },
+#ifdef ANDROID
+ { "driver", hostapd_cli_cmd_driver, NULL,
+ " [] = send driver command data" },
+#endif /* ANDROID */
{ NULL, NULL, NULL, NULL }
};
@@ -2011,12 +2068,13 @@ int main(int argc, char *argv[])
int warning_displayed = 0;
int c;
int daemonize = 0;
+ int reconnect = 0;
if (os_program_init())
return -1;
for (;;) {
- c = getopt(argc, argv, "a:BhG:i:p:P:s:v");
+ c = getopt(argc, argv, "a:BhG:i:p:P:rs:v");
if (c < 0)
break;
switch (c) {
@@ -2045,6 +2103,9 @@ int main(int argc, char *argv[])
case 'P':
pid_file = optarg;
break;
+ case 'r':
+ reconnect = 1;
+ break;
case 's':
client_socket_dir = optarg;
break;
@@ -2087,8 +2148,7 @@ int main(int argc, char *argv[])
printf("Connection established.\n");
break;
}
-
- if (!interactive) {
+ if (!interactive && !reconnect) {
perror("Failed to connect to hostapd - "
"wpa_ctrl_open");
return -1;
@@ -2106,8 +2166,14 @@ int main(int argc, char *argv[])
return -1;
if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue())
return -1;
-
- if (interactive)
+ if (reconnect && action_file && ctrl_ifname) {
+ while (!hostapd_cli_quit) {
+ if (ctrl_conn)
+ hostapd_cli_action(ctrl_conn);
+ os_sleep(1, 0);
+ hostapd_cli_reconnect(ctrl_ifname);
+ }
+ } else if (interactive)
hostapd_cli_interactive();
else if (action_file)
hostapd_cli_action(ctrl_conn);
diff --git a/contrib/wpa/hostapd/main.c b/contrib/wpa/hostapd/main.c
index 08896ffe2a7..4f2d1f21659 100644
--- a/contrib/wpa/hostapd/main.c
+++ b/contrib/wpa/hostapd/main.c
@@ -81,9 +81,6 @@ static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
case HOSTAPD_MODULE_DRIVER:
module_str = "DRIVER";
break;
- case HOSTAPD_MODULE_IAPP:
- module_str = "IAPP";
- break;
case HOSTAPD_MODULE_MLME:
module_str = "MLME";
break;
@@ -221,7 +218,7 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
struct wowlan_triggers *triggs;
iface->drv_flags = capa.flags;
- iface->smps_modes = capa.smps_modes;
+ iface->drv_flags2 = capa.flags2;
iface->probe_resp_offloads = capa.probe_resp_offloads;
/*
* Use default extended capa values from per-radio information
@@ -263,7 +260,7 @@ hostapd_interface_init(struct hapd_interfaces *interfaces, const char *if_name,
struct hostapd_iface *iface;
int k;
- wpa_printf(MSG_ERROR, "Configuration file: %s", config_fname);
+ wpa_printf(MSG_DEBUG, "Configuration file: %s", config_fname);
iface = hostapd_init(interfaces, config_fname);
if (!iface)
return NULL;
@@ -454,11 +451,12 @@ static int hostapd_global_run(struct hapd_interfaces *ifaces, int daemonize,
static void show_version(void)
{
fprintf(stderr,
- "hostapd v" VERSION_STR "\n"
+ "hostapd v%s\n"
"User space daemon for IEEE 802.11 AP management,\n"
"IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n"
"Copyright (c) 2002-2019, Jouni Malinen "
- "and contributors\n");
+ "and contributors\n",
+ VERSION_STR);
}
@@ -676,7 +674,10 @@ int main(int argc, char *argv[])
#endif /* CONFIG_ETH_P_OUI */
#ifdef CONFIG_DPP
os_memset(&dpp_conf, 0, sizeof(dpp_conf));
- /* TODO: dpp_conf.msg_ctx? */
+ dpp_conf.cb_ctx = &interfaces;
+#ifdef CONFIG_DPP2
+ dpp_conf.remove_bi = hostapd_dpp_remove_bi;
+#endif /* CONFIG_DPP2 */
interfaces.dpp = dpp_global_init(&dpp_conf);
if (!interfaces.dpp)
return -1;
@@ -771,7 +772,7 @@ int main(int argc, char *argv[])
if (log_file)
wpa_debug_open_file(log_file);
- else
+ if (!log_file && !wpa_debug_syslog)
wpa_debug_setup_stdout();
#ifdef CONFIG_DEBUG_SYSLOG
if (wpa_debug_syslog)
@@ -905,8 +906,11 @@ int main(int argc, char *argv[])
!!(interfaces.iface[i]->drv_flags &
WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
hostapd_interface_deinit_free(interfaces.iface[i]);
+ interfaces.iface[i] = NULL;
}
os_free(interfaces.iface);
+ interfaces.iface = NULL;
+ interfaces.count = 0;
#ifdef CONFIG_DPP
dpp_global_deinit(interfaces.dpp);
diff --git a/contrib/wpa/hostapd/sae_pk_gen.c b/contrib/wpa/hostapd/sae_pk_gen.c
new file mode 100644
index 00000000000..c31eff75b53
--- /dev/null
+++ b/contrib/wpa/hostapd/sae_pk_gen.c
@@ -0,0 +1,196 @@
+/*
+ * SAE-PK password/modifier generator
+ * Copyright (c) 2020, 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/base64.h"
+#include "crypto/crypto.h"
+#include "common/sae.h"
+
+
+int main(int argc, char *argv[])
+{
+ char *der = NULL;
+ size_t der_len;
+ struct crypto_ec_key *key = NULL;
+ struct wpabuf *pub = NULL;
+ u8 *data = NULL, *m;
+ size_t data_len;
+ char *b64 = NULL, *pw = NULL, *pos, *src;
+ int sec, j;
+ int ret = -1;
+ u8 hash[SAE_MAX_HASH_LEN];
+ char hash_hex[2 * SAE_MAX_HASH_LEN + 1];
+ u8 pw_base_bin[SAE_MAX_HASH_LEN];
+ u8 *dst;
+ int group;
+ size_t hash_len;
+ unsigned long long i, expected;
+ char m_hex[2 * SAE_PK_M_LEN + 1];
+ u32 sec_1b, val20;
+
+ wpa_debug_level = MSG_INFO;
+ if (os_program_init() < 0)
+ goto fail;
+
+ if (argc != 4) {
+ fprintf(stderr,
+ "usage: sae_pk_gen \n");
+ goto fail;
+ }
+
+ sec = atoi(argv[2]);
+ if (sec != 3 && sec != 5) {
+ fprintf(stderr,
+ "Invalid Sec value (allowed values: 3 and 5)\n");
+ goto fail;
+ }
+ sec_1b = sec == 3;
+ expected = 1;
+ for (j = 0; j < sec; j++)
+ expected *= 256;
+
+ der = os_readfile(argv[1], &der_len);
+ if (!der) {
+ fprintf(stderr, "Could not read %s: %s\n",
+ argv[1], strerror(errno));
+ goto fail;
+ }
+
+ key = crypto_ec_key_parse_priv((u8 *) der, der_len);
+ if (!key) {
+ fprintf(stderr, "Could not parse ECPrivateKey\n");
+ goto fail;
+ }
+
+ pub = crypto_ec_key_get_subject_public_key(key);
+ if (!pub) {
+ fprintf(stderr, "Failed to build SubjectPublicKey\n");
+ goto fail;
+ }
+
+ group = crypto_ec_key_group(key);
+ switch (group) {
+ case 19:
+ hash_len = 32;
+ break;
+ case 20:
+ hash_len = 48;
+ break;
+ case 21:
+ hash_len = 64;
+ break;
+ default:
+ fprintf(stderr, "Unsupported private key group\n");
+ goto fail;
+ }
+
+ data_len = os_strlen(argv[3]) + SAE_PK_M_LEN + wpabuf_len(pub);
+ data = os_malloc(data_len);
+ if (!data) {
+ fprintf(stderr, "No memory for data buffer\n");
+ goto fail;
+ }
+ os_memcpy(data, argv[3], os_strlen(argv[3]));
+ m = data + os_strlen(argv[3]);
+ if (os_get_random(m, SAE_PK_M_LEN) < 0) {
+ fprintf(stderr, "Could not generate random Modifier M\n");
+ goto fail;
+ }
+ os_memcpy(m + SAE_PK_M_LEN, wpabuf_head(pub), wpabuf_len(pub));
+
+ fprintf(stderr, "Searching for a suitable Modifier M value\n");
+ for (i = 0;; i++) {
+ if (sae_hash(hash_len, data, data_len, hash) < 0) {
+ fprintf(stderr, "Hash failed\n");
+ goto fail;
+ }
+ if (hash[0] == 0 && hash[1] == 0) {
+ if ((hash[2] & 0xf0) == 0)
+ fprintf(stderr, "\r%3.2f%%",
+ 100.0 * (double) i / (double) expected);
+ for (j = 2; j < sec; j++) {
+ if (hash[j])
+ break;
+ }
+ if (j == sec)
+ break;
+ }
+ inc_byte_array(m, SAE_PK_M_LEN);
+ }
+
+ if (wpa_snprintf_hex(m_hex, sizeof(m_hex), m, SAE_PK_M_LEN) < 0 ||
+ wpa_snprintf_hex(hash_hex, sizeof(hash_hex), hash, hash_len) < 0)
+ goto fail;
+ fprintf(stderr, "\nFound a valid hash in %llu iterations: %s\n",
+ i + 1, hash_hex);
+
+ b64 = base64_encode(der, der_len, NULL);
+ if (!b64)
+ goto fail;
+ src = pos = b64;
+ while (*src) {
+ if (*src != '\n')
+ *pos++ = *src;
+ src++;
+ }
+ *pos = '\0';
+
+ /* Skip 8*Sec bits and add Sec_1b as the every 20th bit starting with
+ * one. */
+ os_memset(pw_base_bin, 0, sizeof(pw_base_bin));
+ dst = pw_base_bin;
+ for (j = 0; j < 8 * (int) hash_len / 20; j++) {
+ val20 = sae_pk_get_be19(hash + sec);
+ val20 |= sec_1b << 19;
+ sae_pk_buf_shift_left_19(hash + sec, hash_len - sec);
+
+ if (j & 1) {
+ *dst |= (val20 >> 16) & 0x0f;
+ dst++;
+ *dst++ = (val20 >> 8) & 0xff;
+ *dst++ = val20 & 0xff;
+ } else {
+ *dst++ = (val20 >> 12) & 0xff;
+ *dst++ = (val20 >> 4) & 0xff;
+ *dst = (val20 << 4) & 0xf0;
+ }
+ }
+ if (wpa_snprintf_hex(hash_hex, sizeof(hash_hex),
+ pw_base_bin, hash_len - sec) >= 0)
+ fprintf(stderr, "PasswordBase binary data for base32: %s",
+ hash_hex);
+
+ pw = sae_pk_base32_encode(pw_base_bin, 20 * 3 - 5);
+ if (!pw)
+ goto fail;
+
+ printf("# SAE-PK password/M/private key for Sec=%d.\n", sec);
+ printf("sae_password=%s|pk=%s:%s\n", pw, m_hex, b64);
+ printf("# Longer passwords can be used for improved security at the cost of usability:\n");
+ for (j = 4; j <= ((int) hash_len * 8 + 5 - 8 * sec) / 19; j++) {
+ os_free(pw);
+ pw = sae_pk_base32_encode(pw_base_bin, 20 * j - 5);
+ if (pw)
+ printf("# %s\n", pw);
+ }
+
+ ret = 0;
+fail:
+ os_free(der);
+ wpabuf_free(pub);
+ crypto_ec_key_deinit(key);
+ os_free(data);
+ os_free(b64);
+ os_free(pw);
+
+ os_program_deinit();
+
+ return ret;
+}
diff --git a/contrib/wpa/hs20/client/Makefile b/contrib/wpa/hs20/client/Makefile
index 67f6f55c526..4dcfe2d3bf2 100644
--- a/contrib/wpa/hs20/client/Makefile
+++ b/contrib/wpa/hs20/client/Makefile
@@ -1,28 +1,6 @@
-all: hs20-osu-client
+ALL=hs20-osu-client
-ifndef CC
-CC=gcc
-endif
-
-ifndef LDO
-LDO=$(CC)
-endif
-
-ifeq ($(QUIET), 1)
-Q=@
-E=true
-else
-Q=@
-E=echo
-ifeq ($(V), 1)
-Q=
-E=true
-endif
-endif
-
-ifndef CFLAGS
-CFLAGS = -MMD -O2 -Wall -g
-endif
+include ../../src/build.rules
CFLAGS += -I../../src/utils
CFLAGS += -I../../src/common
@@ -30,8 +8,17 @@ CFLAGS += -I../../src
ifndef CONFIG_NO_BROWSER
ifndef CONFIG_BROWSER_SYSTEM
+TEST_WK := $(shell pkg-config --silence-errors --cflags webkitgtk-3.0)
+ifeq ($(TEST_WK),)
+# Try webkit2
+GTKCFLAGS := $(shell pkg-config --cflags gtk+-3.0 webkit2gtk-4.0)
+GTKLIBS := $(shell pkg-config --libs gtk+-3.0 webkit2gtk-4.0)
+CFLAGS += -DUSE_WEBKIT2
+else
GTKCFLAGS := $(shell pkg-config --cflags gtk+-3.0 webkitgtk-3.0)
GTKLIBS := $(shell pkg-config --libs gtk+-3.0 webkitgtk-3.0)
+endif
+
CFLAGS += $(GTKCFLAGS)
LIBS += $(GTKLIBS)
endif
@@ -84,23 +71,11 @@ CFLAGS += -DEAP_TLS_OPENSSL
OBJS += ../../src/crypto/tls_openssl_ocsp.o
LIBS += -lssl -lcrypto
+_OBJS_VAR := OBJS
+include ../../src/objs.mk
hs20-osu-client: $(OBJS)
$(Q)$(LDO) $(LDFLAGS) -o hs20-osu-client $(OBJS) $(LIBS)
@$(E) " LD " $@
-%.o: %.c
- $(Q)$(CC) -c -o $@ $(CFLAGS) $<
- @$(E) " CC " $<
-
-clean:
- rm -f core *~ *.o *.d hs20-osu-client
- rm -f ../../src/utils/*.o
- rm -f ../../src/utils/*.d
- rm -f ../../src/common/*.o
- rm -f ../../src/common/*.d
- rm -f ../../src/crypto/*.o
- rm -f ../../src/crypto/*.d
- rm -f ../../src/wps/*.o
- rm -f ../../src/wps/*.d
-
--include $(OBJS:%.o=%.d)
+clean: common-clean
+ rm -f core *~
diff --git a/contrib/wpa/hs20/client/est.c b/contrib/wpa/hs20/client/est.c
index db65334b20f..97f9132100c 100644
--- a/contrib/wpa/hs20/client/est.c
+++ b/contrib/wpa/hs20/client/est.c
@@ -158,7 +158,7 @@ int est_load_cacerts(struct hs20_osu_client *ctx, const char *url)
return -1;
}
- pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len);
+ pkcs7 = base64_decode(resp, resp_len, &pkcs7_len);
if (pkcs7 && pkcs7_len < resp_len / 2) {
wpa_printf(MSG_INFO, "Too short base64 decode (%u bytes; downloaded %u bytes) - assume this was binary",
(unsigned int) pkcs7_len, (unsigned int) resp_len);
@@ -639,8 +639,7 @@ int est_build_csr(struct hs20_osu_client *ctx, const char *url)
return -1;
}
- attrs = base64_decode((unsigned char *) resp, resp_len,
- &attrs_len);
+ attrs = base64_decode(resp, resp_len, &attrs_len);
os_free(resp);
if (attrs == NULL) {
@@ -734,7 +733,7 @@ int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
}
wpa_printf(MSG_DEBUG, "EST simpleenroll response: %s", resp);
- pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len);
+ pkcs7 = base64_decode(resp, resp_len, &pkcs7_len);
if (pkcs7 == NULL) {
wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
pkcs7 = os_malloc(resp_len);
diff --git a/contrib/wpa/hs20/client/oma_dm_client.c b/contrib/wpa/hs20/client/oma_dm_client.c
index d75c84562ac..bcd68b8775d 100644
--- a/contrib/wpa/hs20/client/oma_dm_client.c
+++ b/contrib/wpa/hs20/client/oma_dm_client.c
@@ -407,7 +407,7 @@ static int oma_dm_exec_browser(struct hs20_osu_client *ctx, xml_node_t *exec)
wpa_printf(MSG_INFO, "Data: %s", data);
wpa_printf(MSG_INFO, "Launch browser to URI '%s'", data);
write_summary(ctx, "Launch browser to URI '%s'", data);
- res = hs20_web_browser(data);
+ res = hs20_web_browser(data, 1);
xml_node_get_text_free(ctx->xml, data);
if (res > 0) {
wpa_printf(MSG_INFO, "User response in browser completed successfully");
diff --git a/contrib/wpa/hs20/client/osu_client.c b/contrib/wpa/hs20/client/osu_client.c
index fd99600da78..11bf0db35e9 100644
--- a/contrib/wpa/hs20/client/osu_client.c
+++ b/contrib/wpa/hs20/client/osu_client.c
@@ -310,7 +310,7 @@ static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params,
size_t len;
u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN];
int res;
- unsigned char *b64;
+ char *b64;
FILE *f;
url_node = get_node(ctx->xml, params, "CertURL");
@@ -364,7 +364,7 @@ static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params,
return -1;
}
- b64 = base64_encode((unsigned char *) cert, len, NULL);
+ b64 = base64_encode(cert, len, NULL);
os_free(cert);
if (b64 == NULL)
return -1;
@@ -2233,7 +2233,7 @@ static int osu_connect(struct hs20_osu_client *ctx, const char *bssid,
wpa_ctrl_close(mon);
if (res < 0) {
- wpa_printf(MSG_INFO, "Could not connect");
+ wpa_printf(MSG_INFO, "Could not connect to OSU network");
write_summary(ctx, "Could not connect to OSU network");
wpa_printf(MSG_INFO, "Remove OSU network connection");
snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id);
@@ -2406,7 +2406,7 @@ static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir,
snprintf(fname, sizeof(fname), "file://%s/osu-providers.html", dir);
write_summary(ctx, "Start web browser with OSU provider selection page");
- ret = hs20_web_browser(fname);
+ ret = hs20_web_browser(fname, 0);
selected:
if (ret > 0 && (size_t) ret <= osu_count) {
@@ -2907,7 +2907,7 @@ static char * get_hostname(const char *url)
static int osu_cert_cb(void *_ctx, struct http_cert *cert)
{
struct hs20_osu_client *ctx = _ctx;
- unsigned int i, j;
+ size_t i, j;
int found;
char *host = NULL;
@@ -3002,7 +3002,7 @@ static int osu_cert_cb(void *_ctx, struct http_cert *cert)
size_t name_len = os_strlen(name);
wpa_printf(MSG_INFO,
- "[%i] Looking for icon file name '%s' match",
+ "[%zu] Looking for icon file name '%s' match",
j, name);
for (i = 0; i < cert->num_logo; i++) {
struct http_logo *logo = &cert->logo[i];
@@ -3010,7 +3010,7 @@ static int osu_cert_cb(void *_ctx, struct http_cert *cert)
char *pos;
wpa_printf(MSG_INFO,
- "[%i] Comparing to '%s' uri_len=%d name_len=%d",
+ "[%zu] Comparing to '%s' uri_len=%d name_len=%d",
i, logo->uri, (int) uri_len, (int) name_len);
if (uri_len < 1 + name_len) {
wpa_printf(MSG_INFO, "URI Length is too short");
@@ -3044,7 +3044,7 @@ static int osu_cert_cb(void *_ctx, struct http_cert *cert)
if (logo->hash_len != 32) {
wpa_printf(MSG_INFO,
- "[%i][%i] Icon hash length invalid (should be 32): %d",
+ "[%zu][%zu] Icon hash length invalid (should be 32): %d",
j, i, (int) logo->hash_len);
continue;
}
@@ -3054,7 +3054,7 @@ static int osu_cert_cb(void *_ctx, struct http_cert *cert)
}
wpa_printf(MSG_DEBUG,
- "[%u][%u] Icon hash did not match", j, i);
+ "[%zu][%zu] Icon hash did not match", j, i);
wpa_hexdump_ascii(MSG_DEBUG, "logo->hash",
logo->hash, 32);
wpa_hexdump_ascii(MSG_DEBUG, "ctx->icon_hash[j]",
@@ -3152,7 +3152,7 @@ static void check_workarounds(struct hs20_osu_client *ctx)
static void usage(void)
{
- printf("usage: hs20-osu-client [-dddqqKt] [-S] \\\n"
+ printf("usage: hs20-osu-client [-dddqqKtT] [-S] \\\n"
" [-w] "
"[-r] [-f] \\\n"
" [-s] \\\n"
@@ -3198,7 +3198,7 @@ int main(int argc, char *argv[])
return -1;
for (;;) {
- c = getopt(argc, argv, "df:hKNo:O:qr:s:S:tw:x:");
+ c = getopt(argc, argv, "df:hKNo:O:qr:s:S:tTw:x:");
if (c < 0)
break;
switch (c) {
@@ -3236,6 +3236,9 @@ int main(int argc, char *argv[])
case 't':
wpa_debug_timestamp++;
break;
+ case 'T':
+ ctx.ignore_tls = 1;
+ break;
case 'w':
wpas_ctrl_path = optarg;
break;
@@ -3403,7 +3406,7 @@ int main(int argc, char *argv[])
wpa_printf(MSG_INFO, "Launch web browser to URL %s",
argv[optind + 1]);
- ret = hs20_web_browser(argv[optind + 1]);
+ ret = hs20_web_browser(argv[optind + 1], ctx.ignore_tls);
wpa_printf(MSG_INFO, "Web browser result: %d", ret);
} else if (strcmp(argv[optind], "parse_cert") == 0) {
if (argc - optind < 2) {
diff --git a/contrib/wpa/hs20/client/osu_client.h b/contrib/wpa/hs20/client/osu_client.h
index 5c8e6d00b6b..9b45b03febe 100644
--- a/contrib/wpa/hs20/client/osu_client.h
+++ b/contrib/wpa/hs20/client/osu_client.h
@@ -50,6 +50,8 @@ struct hs20_osu_client {
const char *osu_ssid; /* Enforced OSU_SSID for testing purposes */
#define WORKAROUND_OCSP_OPTIONAL 0x00000001
unsigned long int workarounds;
+ int ignore_tls; /* whether to ignore TLS validation issues with HTTPS
+ * server certificate */
};
diff --git a/contrib/wpa/hs20/client/spp_client.c b/contrib/wpa/hs20/client/spp_client.c
index c619541ae28..39d10e0362f 100644
--- a/contrib/wpa/hs20/client/spp_client.c
+++ b/contrib/wpa/hs20/client/spp_client.c
@@ -547,7 +547,7 @@ static int hs20_spp_exec(struct hs20_osu_client *ctx, xml_node_t *exec,
}
wpa_printf(MSG_INFO, "Launch browser to URI '%s'", uri);
write_summary(ctx, "Launch browser to URI '%s'", uri);
- res = hs20_web_browser(uri);
+ res = hs20_web_browser(uri, 1);
xml_node_get_text_free(ctx->xml, uri);
if (res > 0) {
wpa_printf(MSG_INFO, "User response in browser completed successfully - sessionid='%s'",
diff --git a/contrib/wpa/hs20/server/Makefile b/contrib/wpa/hs20/server/Makefile
new file mode 100644
index 00000000000..0cab6d6b010
--- /dev/null
+++ b/contrib/wpa/hs20/server/Makefile
@@ -0,0 +1,42 @@
+ALL=hs20_spp_server
+
+include ../../src/build.rules
+
+CFLAGS += -I../../src
+CFLAGS += -I../../src/utils
+CFLAGS += -I../../src/crypto
+
+LIBS += -lsqlite3
+
+# Using glibc < 2.17 requires -lrt for clock_gettime()
+LIBS += -lrt
+
+ifndef CONFIG_NO_GITVER
+# Add VERSION_STR postfix for builds from a git repository
+ifeq ($(wildcard ../../.git),../../.git)
+GITVER := $(shell git describe --dirty=+)
+ifneq ($(GITVER),)
+CFLAGS += -DGIT_VERSION_STR_POSTFIX=\"-$(GITVER)\"
+endif
+endif
+endif
+
+OBJS=spp_server.o
+OBJS += hs20_spp_server.o
+OBJS += ../../src/utils/xml-utils.o
+OBJS += ../../src/utils/base64.o
+OBJS += ../../src/utils/common.o
+OBJS += ../../src/utils/os_unix.o
+OBJS += ../../src/utils/wpa_debug.o
+OBJS += ../../src/crypto/md5-internal.o
+CFLAGS += $(shell xml2-config --cflags)
+LIBS += $(shell xml2-config --libs)
+OBJS += ../../src/utils/xml_libxml2.o
+
+_OBJS_VAR := OBJS
+include ../../src/objs.mk
+hs20_spp_server: $(OBJS)
+ $(LDO) $(LDFLAGS) -o hs20_spp_server $(OBJS) $(LIBS)
+
+clean: common-clean
+ rm -f core *~
diff --git a/contrib/wpa/hs20/server/ca/clean.sh b/contrib/wpa/hs20/server/ca/clean.sh
new file mode 100755
index 00000000000..c72dcbda45e
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/clean.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+for i in server-client server server-revoked user ocsp; do
+ rm -f $i.csr $i.key $i.pem
+done
+
+rm -f openssl.cnf.tmp
+if [ -d demoCA ]; then
+ rm -r demoCA
+fi
+rm -f ca.pem logo.asn1 logo.der server.der ocsp-server-cache.der
+rm -f my-openssl.cnf my-openssl-root.cnf
+#rm -r rootCA
diff --git a/contrib/wpa/hs20/server/ca/est-csrattrs.cnf b/contrib/wpa/hs20/server/ca/est-csrattrs.cnf
new file mode 100644
index 00000000000..b50ea00d0b7
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/est-csrattrs.cnf
@@ -0,0 +1,17 @@
+asn1 = SEQUENCE:attrs
+
+[attrs]
+#oid1 = OID:challengePassword
+attr1 = SEQUENCE:extreq
+oid2 = OID:sha256WithRSAEncryption
+
+[extreq]
+oid = OID:extensionRequest
+vals = SET:extreqvals
+
+[extreqvals]
+
+oid1 = OID:macAddress
+#oid2 = OID:imei
+#oid3 = OID:meid
+#oid4 = OID:DevId
diff --git a/contrib/wpa/hs20/server/ca/est-csrattrs.sh b/contrib/wpa/hs20/server/ca/est-csrattrs.sh
new file mode 100755
index 00000000000..0b73a040828
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/est-csrattrs.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+openssl asn1parse -genconf est-csrattrs.cnf -out est-csrattrs.der -oid hs20.oid
+base64 est-csrattrs.der > est-attrs.b64
diff --git a/contrib/wpa/hs20/server/ca/hs20.oid b/contrib/wpa/hs20/server/ca/hs20.oid
new file mode 100644
index 00000000000..a829ff29bf4
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/hs20.oid
@@ -0,0 +1,7 @@
+1.3.6.1.1.1.1.22 macAddress
+1.2.840.113549.1.9.14 extensionRequest
+1.3.6.1.4.1.40808.1.1.1 id-wfa-hotspot-friendlyName
+1.3.6.1.4.1.40808.1.1.2 id-kp-HS2.0Auth
+1.3.6.1.4.1.40808.1.1.3 imei
+1.3.6.1.4.1.40808.1.1.4 meid
+1.3.6.1.4.1.40808.1.1.5 DevId
diff --git a/contrib/wpa/hs20/server/ca/ocsp-req.sh b/contrib/wpa/hs20/server/ca/ocsp-req.sh
new file mode 100755
index 00000000000..931a20696d0
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/ocsp-req.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+for i in *.pem; do
+ echo "===[ $i ]==================="
+ openssl ocsp -text -CAfile ca.pem -verify_other demoCA/cacert.pem -trust_other -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
+
+# openssl ocsp -text -CAfile rootCA/cacert.pem -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
+
+# openssl ocsp -text -CAfile rootCA/cacert.pem -verify_other demoCA/cacert.pem -trust_other -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
+# openssl ocsp -text -CAfile rootCA/cacert.pem -VAfile ca.pem -trust_other -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
+done
diff --git a/contrib/wpa/hs20/server/ca/ocsp-responder-ica.sh b/contrib/wpa/hs20/server/ca/ocsp-responder-ica.sh
new file mode 100755
index 00000000000..116c6e1c3d0
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/ocsp-responder-ica.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+openssl ocsp -index demoCA/index.txt -port 8888 -nmin 5 -rsigner demoCA/cacert.pem -rkey demoCA/private/cakey-plain.pem -CA demoCA/cacert.pem -resp_no_certs -text
diff --git a/contrib/wpa/hs20/server/ca/ocsp-responder.sh b/contrib/wpa/hs20/server/ca/ocsp-responder.sh
new file mode 100755
index 00000000000..620947d01af
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/ocsp-responder.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+openssl ocsp -index demoCA/index.txt -port 8888 -nmin 5 -rsigner ocsp.pem -rkey ocsp.key -CA demoCA/cacert.pem -text -ignore_err
diff --git a/contrib/wpa/hs20/server/ca/ocsp-update-cache.sh b/contrib/wpa/hs20/server/ca/ocsp-update-cache.sh
new file mode 100755
index 00000000000..f2b23250cad
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/ocsp-update-cache.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# NOTE: You may need to replace 'localhost' with your OCSP server hostname.
+openssl ocsp \
+ -no_nonce \
+ -CAfile ca.pem \
+ -verify_other demoCA/cacert.pem \
+ -issuer demoCA/cacert.pem \
+ -cert server.pem \
+ -url http://localhost:8888/ \
+ -respout ocsp-server-cache.der
diff --git a/contrib/wpa/hs20/server/ca/openssl-root.cnf b/contrib/wpa/hs20/server/ca/openssl-root.cnf
new file mode 100644
index 00000000000..5bc50be1dbc
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/openssl-root.cnf
@@ -0,0 +1,125 @@
+# OpenSSL configuration file for Hotspot 2.0 PKI (Root CA)
+
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+oid_section = new_oids
+
+[ new_oids ]
+
+#logotypeoid=1.3.6.1.5.5.7.1.12
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = ./rootCA # Where everything is kept
+certs = $dir/certs # Where the issued certs are kept
+crl_dir = $dir/crl # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+#unique_subject = no # Set to 'no' to allow creation of
+ # several certificates with same subject
+new_certs_dir = $dir/newcerts # default place for new certs.
+
+certificate = $dir/cacert.pem # The CA certificate
+serial = $dir/serial # The current serial number
+crlnumber = $dir/crlnumber # the current crl number
+ # must be commented out to leave a V1 CRL
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/private/cakey.pem# The private key
+RANDFILE = $dir/private/.rand # private random number file
+
+x509_extensions = usr_cert # The extentions to add to the cert
+
+name_opt = ca_default # Subject Name options
+cert_opt = ca_default # Certificate field options
+
+default_days = 365 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = default # use public key default MD
+preserve = no # keep passed DN ordering
+
+policy = policy_match
+
+# For the CA policy
+[ policy_match ]
+countryName = match
+stateOrProvinceName = optional
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = 2048
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+input_password = @PASSWORD@
+output_password = @PASSWORD@
+
+string_mask = utf8only
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = US
+countryName_min = 2
+countryName_max = 2
+
+localityName = Locality Name (eg, city)
+localityName_default = Tuusula
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = WFA Hotspot 2.0
+
+##organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+#@OU@
+
+commonName = Common Name (e.g. server FQDN or YOUR name)
+#@CN@
+commonName_max = 64
+
+emailAddress = Email Address
+emailAddress_max = 64
+
+[ req_attributes ]
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectAltName=DNS:example.com,DNS:another.example.com
+
+[ v3_ca ]
+
+# Hotspot 2.0 PKI requirements
+subjectKeyIdentifier=hash
+basicConstraints = critical,CA:true
+keyUsage = critical, cRLSign, keyCertSign
+
+[ crl_ext ]
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always
+
+[ v3_OCSP ]
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage = OCSPSigning
diff --git a/contrib/wpa/hs20/server/ca/openssl.cnf b/contrib/wpa/hs20/server/ca/openssl.cnf
new file mode 100644
index 00000000000..61410138340
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/openssl.cnf
@@ -0,0 +1,200 @@
+# OpenSSL configuration file for Hotspot 2.0 PKI (Intermediate CA)
+
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+oid_section = new_oids
+
+[ new_oids ]
+
+#logotypeoid=1.3.6.1.5.5.7.1.12
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = ./demoCA # Where everything is kept
+certs = $dir/certs # Where the issued certs are kept
+crl_dir = $dir/crl # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+#unique_subject = no # Set to 'no' to allow creation of
+ # several certificates with same subject
+new_certs_dir = $dir/newcerts # default place for new certs.
+
+certificate = $dir/cacert.pem # The CA certificate
+serial = $dir/serial # The current serial number
+crlnumber = $dir/crlnumber # the current crl number
+ # must be commented out to leave a V1 CRL
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/private/cakey.pem# The private key
+RANDFILE = $dir/private/.rand # private random number file
+
+x509_extensions = ext_client # The extentions to add to the cert
+
+name_opt = ca_default # Subject Name options
+cert_opt = ca_default # Certificate field options
+
+# Extension copying option: use with caution.
+copy_extensions = copy
+
+default_days = 365 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = default # use public key default MD
+preserve = no # keep passed DN ordering
+
+policy = policy_match
+
+# For the CA policy
+[ policy_match ]
+countryName = supplied
+stateOrProvinceName = optional
+organizationName = supplied
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ policy_osu_server ]
+countryName = match
+stateOrProvinceName = optional
+organizationName = match
+organizationalUnitName = supplied
+commonName = supplied
+emailAddress = optional
+
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = 2048
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+input_password = @PASSWORD@
+output_password = @PASSWORD@
+
+string_mask = utf8only
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = FI
+countryName_min = 2
+countryName_max = 2
+
+localityName = Locality Name (eg, city)
+localityName_default = Tuusula
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = @DOMAIN@
+
+##organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+#@OU@
+
+commonName = Common Name (e.g. server FQDN or YOUR name)
+#@CN@
+commonName_max = 64
+
+emailAddress = Email Address
+emailAddress_max = 64
+
+[ req_attributes ]
+
+[ v3_ca ]
+
+# Hotspot 2.0 PKI requirements
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always,issuer
+basicConstraints = critical, CA:true, pathlen:0
+keyUsage = critical, cRLSign, keyCertSign
+authorityInfoAccess = OCSP;URI:@OCSP_URI@
+# For SP intermediate CA
+#subjectAltName=critical,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:engExample OSU
+#nameConstraints=permitted;DNS:.@DOMAIN@
+#1.3.6.1.5.5.7.1.12=ASN1:SEQUENCE:LogotypeExtn
+
+[ v3_osu_server ]
+
+basicConstraints = critical, CA:true, pathlen:0
+keyUsage = critical, keyEncipherment
+#@ALTNAME@
+
+#logotypeoid=ASN1:SEQUENCE:LogotypeExtn
+1.3.6.1.5.5.7.1.12=ASN1:SEQUENCE:LogotypeExtn
+[LogotypeExtn]
+communityLogos=EXP:0,SEQUENCE:LogotypeInfo
+[LogotypeInfo]
+# note: implicit tag converted to explicit for CHOICE
+direct=EXP:0,SEQUENCE:LogotypeData
+[LogotypeData]
+image=SEQUENCE:LogotypeImage
+[LogotypeImage]
+imageDetails=SEQUENCE:LogotypeDetails
+imageInfo=SEQUENCE:LogotypeImageInfo
+[LogotypeDetails]
+mediaType=IA5STRING:image/png
+logotypeHash=SEQUENCE:HashAlgAndValues
+logotypeURI=SEQUENCE:URI
+[HashAlgAndValues]
+value1=SEQUENCE:HashAlgAndValueSHA256
+#value2=SEQUENCE:HashAlgAndValueSHA1
+[HashAlgAndValueSHA256]
+hashAlg=SEQUENCE:sha256_alg
+hashValue=FORMAT:HEX,OCTETSTRING:@LOGO_HASH256@
+[HashAlgAndValueSHA1]
+hashAlg=SEQUENCE:sha1_alg
+hashValue=FORMAT:HEX,OCTETSTRING:@LOGO_HASH1@
+[sha256_alg]
+algorithm=OID:sha256
+[sha1_alg]
+algorithm=OID:sha1
+[URI]
+uri=IA5STRING:@LOGO_URI@
+[LogotypeImageInfo]
+# default value color(1), component optional
+#type=IMP:0,INTEGER:1
+fileSize=INTEGER:7549
+xSize=INTEGER:128
+ySize=INTEGER:80
+language=IMP:4,IA5STRING:zxx
+
+[ crl_ext ]
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always
+
+[ v3_OCSP ]
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage = OCSPSigning
+
+[ ext_client ]
+
+basicConstraints=CA:FALSE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+authorityInfoAccess = OCSP;URI:@OCSP_URI@
+#@ALTNAME@
+extendedKeyUsage = clientAuth
+
+[ ext_server ]
+
+# Hotspot 2.0 PKI requirements
+basicConstraints=critical, CA:FALSE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+authorityInfoAccess = OCSP;URI:@OCSP_URI@
+#@ALTNAME@
+extendedKeyUsage = critical, serverAuth
+keyUsage = critical, keyEncipherment
diff --git a/contrib/wpa/hs20/server/ca/setup.sh b/contrib/wpa/hs20/server/ca/setup.sh
new file mode 100755
index 00000000000..78abcccff45
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/setup.sh
@@ -0,0 +1,209 @@
+#!/bin/sh
+
+if [ -z "$OPENSSL" ]; then
+ OPENSSL=openssl
+fi
+export OPENSSL_CONF=$PWD/openssl.cnf
+PASS=whatever
+if [ -z "$DOMAIN" ]; then
+ DOMAIN=w1.fi
+fi
+COMPANY=w1.fi
+OPER_ENG="engw1.fi TESTING USE"
+OPER_FI="finw1.fi TESTIKÄYTTÖ"
+CNR="Hotspot 2.0 Trust Root CA - 99"
+CNO="ocsp.$DOMAIN"
+CNV="osu-revoked.$DOMAIN"
+CNOC="osu-client.$DOMAIN"
+OSU_SERVER_HOSTNAME="osu.$DOMAIN"
+DEBUG=0
+OCSP_URI="http://$CNO:8888/"
+LOGO_URI="http://osu.w1.fi/w1fi_logo.png"
+LOGO_HASH256="4532f7ec36424381617c03c6ce87b55a51d6e7177ffafda243cebf280a68954d"
+LOGO_HASH1="5e1d5085676eede6b02da14d31c523ec20ffba0b"
+
+# Command line overrides
+USAGE=$( cat < my-openssl-root.cnf
+
+cat openssl.cnf | sed "s/@PASSWORD@/$PASS/" |
+sed "s,@OCSP_URI@,$OCSP_URI," |
+sed "s,@LOGO_URI@,$LOGO_URI," |
+sed "s,@LOGO_HASH1@,$LOGO_HASH1," |
+sed "s,@LOGO_HASH256@,$LOGO_HASH256," |
+sed "s/@DOMAIN@/$DOMAIN/" \
+ > my-openssl.cnf
+
+
+cat my-openssl-root.cnf | sed "s/#@CN@/commonName_default = $CNR/" > openssl.cnf.tmp
+mkdir -p rootCA/certs rootCA/crl rootCA/newcerts rootCA/private
+touch rootCA/index.txt
+if [ -e rootCA/private/cakey.pem ]; then
+ echo " * Use existing Root CA"
+else
+ echo " * Generate Root CA private key"
+ $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:4096 -keyout rootCA/private/cakey.pem -out rootCA/careq.pem || fail "Failed to generate Root CA private key"
+ echo " * Sign Root CA certificate"
+ $OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out rootCA/cacert.pem -days 10957 -batch -keyfile rootCA/private/cakey.pem -passin pass:$PASS -selfsign -extensions v3_ca -outdir rootCA/newcerts -infiles rootCA/careq.pem || fail "Failed to sign Root CA certificate"
+ $OPENSSL x509 -in rootCA/cacert.pem -out rootCA/cacert.der -outform DER || fail "Failed to create rootCA DER"
+ sha256sum rootCA/cacert.der > rootCA/cacert.fingerprint || fail "Failed to create rootCA fingerprint"
+fi
+if [ ! -e rootCA/crlnumber ]; then
+ echo 00 > rootCA/crlnumber
+fi
+
+echo
+echo "---[ Intermediate CA ]--------------------------------------------------"
+echo
+
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $COMPANY Hotspot 2.0 Intermediate CA/" > openssl.cnf.tmp
+mkdir -p demoCA/certs demoCA/crl demoCA/newcerts demoCA/private
+touch demoCA/index.txt
+if [ -e demoCA/private/cakey.pem ]; then
+ echo " * Use existing Intermediate CA"
+else
+ echo " * Generate Intermediate CA private key"
+ $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -keyout demoCA/private/cakey.pem -out demoCA/careq.pem || fail "Failed to generate Intermediate CA private key"
+ echo " * Sign Intermediate CA certificate"
+ $OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out demoCA/cacert.pem -days 3652 -batch -keyfile rootCA/private/cakey.pem -cert rootCA/cacert.pem -passin pass:$PASS -extensions v3_ca -infiles demoCA/careq.pem || fail "Failed to sign Intermediate CA certificate"
+ # horrible from security view point, but for testing purposes since OCSP responder does not seem to support -passin
+ openssl rsa -in demoCA/private/cakey.pem -out demoCA/private/cakey-plain.pem -passin pass:$PASS
+ $OPENSSL x509 -in demoCA/cacert.pem -out demoCA/cacert.der -outform DER || fail "Failed to create demoCA DER."
+ sha256sum demoCA/cacert.der > demoCA/cacert.fingerprint || fail "Failed to create demoCA fingerprint"
+fi
+if [ ! -e demoCA/crlnumber ]; then
+ echo 00 > demoCA/crlnumber
+fi
+
+echo
+echo "OCSP responder"
+echo
+
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNO/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out ocsp.csr -keyout ocsp.key -extensions v3_OCSP
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -keyfile demoCA/private/cakey.pem -passin pass:$PASS -in ocsp.csr -out ocsp.pem -days 730 -extensions v3_OCSP || fail "Could not generate ocsp.pem"
+
+echo
+echo "---[ Server - to be revoked ] ------------------------------------------"
+echo
+
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNV/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out server-revoked.csr -keyout server-revoked.key
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server-revoked.csr -out server-revoked.pem -key $PASS -days 730 -extensions ext_server
+$OPENSSL ca -revoke server-revoked.pem -key $PASS
+
+echo
+echo "---[ Server - with client ext key use ] ---------------------------------"
+echo "---[ Only used for negative-testing for OSU-client implementation ] -----"
+echo
+
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNOC/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out server-client.csr -keyout server-client.key || fail "Could not create server-client.key"
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server-client.csr -out server-client.pem -key $PASS -days 730 -extensions ext_client || fail "Could not create server-client.pem"
+
+echo
+echo "---[ User ]-------------------------------------------------------------"
+echo
+
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = User/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out user.csr -keyout user.key || fail "Could not create user.key"
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in user.csr -out user.pem -key $PASS -days 730 -extensions ext_client || fail "Could not create user.pem"
+
+echo
+echo "---[ Server ]-----------------------------------------------------------"
+echo
+
+ALT="DNS:$OSU_SERVER_HOSTNAME"
+ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:$OPER_ENG"
+ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:$OPER_FI"
+
+cat my-openssl.cnf |
+ sed "s/#@CN@/commonName_default = $OSU_SERVER_HOSTNAME/" |
+ sed "s/^##organizationalUnitName/organizationalUnitName/" |
+ sed "s/#@OU@/organizationalUnitName_default = Hotspot 2.0 Online Sign Up Server/" |
+ sed "s/#@ALTNAME@/subjectAltName=critical,$ALT/" \
+ > openssl.cnf.tmp
+echo $OPENSSL req -config $PWD/openssl.cnf.tmp -batch -sha256 -new -newkey rsa:2048 -nodes -out server.csr -keyout server.key -reqexts v3_osu_server
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -sha256 -new -newkey rsa:2048 -nodes -out server.csr -keyout server.key -reqexts v3_osu_server || fail "Failed to generate server request"
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server.csr -out server.pem -key $PASS -days 730 -extensions ext_server -policy policy_osu_server || fail "Failed to sign server certificate"
+
+#dump logotype details for debugging
+$OPENSSL x509 -in server.pem -out server.der -outform DER
+openssl asn1parse -in server.der -inform DER | grep HEX | tail -1 | sed 's/.*://' | xxd -r -p > logo.der
+openssl asn1parse -in logo.der -inform DER > logo.asn1
+
+
+echo
+echo "---[ CRL ]---------------------------------------------------------------"
+echo
+
+$OPENSSL ca -config $PWD/my-openssl.cnf -gencrl -md sha256 -out demoCA/crl/crl.pem -passin pass:$PASS
+
+echo
+echo "---[ Verify ]------------------------------------------------------------"
+echo
+
+$OPENSSL verify -CAfile rootCA/cacert.pem demoCA/cacert.pem
+$OPENSSL verify -CAfile rootCA/cacert.pem -untrusted demoCA/cacert.pem *.pem
+
+cat rootCA/cacert.pem demoCA/cacert.pem > ca.pem
diff --git a/contrib/wpa/hs20/server/ca/w1fi_logo.png b/contrib/wpa/hs20/server/ca/w1fi_logo.png
new file mode 100644
index 00000000000..ac7c259fff2
Binary files /dev/null and b/contrib/wpa/hs20/server/ca/w1fi_logo.png differ
diff --git a/contrib/wpa/hs20/server/hs20-osu-server.txt b/contrib/wpa/hs20/server/hs20-osu-server.txt
new file mode 100644
index 00000000000..22478ad9d2c
--- /dev/null
+++ b/contrib/wpa/hs20/server/hs20-osu-server.txt
@@ -0,0 +1,262 @@
+Hotspot 2.0 OSU server
+======================
+
+The information in this document is based on the assumption that Ubuntu
+16.04 server (64-bit) distribution is used and the web server is
+Apache2. Neither of these are requirements for the installation, but if
+other combinations are used, the package names and configuration
+parameters may need to be adjusted.
+
+NOTE: This implementation and the example configuration here is meant
+only for testing purposes in a lab environment. This design is not
+secure to be installed in a publicly available Internet server without
+considerable amount of modification and review for security issues.
+
+
+Build dependencies
+------------------
+
+Ubuntu 16.04 server
+- default installation
+- upgraded to latest package versions
+ sudo apt-get update
+ sudo apt-get upgrade
+
+Packages needed for running the service:
+ sudo apt-get install sqlite3
+ sudo apt-get install apache2
+ sudo apt-get install php-sqlite3 php-xml libapache2-mod-php
+
+Additional packages needed for building the components:
+ sudo apt-get install build-essential
+ sudo apt-get install libsqlite3-dev
+ sudo apt-get install libssl-dev
+ sudo apt-get install libxml2-dev
+
+
+Installation location
+---------------------
+
+Select a location for the installation root directory. The example here
+assumes /home/user/hs20-server to be used, but this can be changed by
+editing couple of files as indicated below.
+
+sudo mkdir -p /home/user/hs20-server
+sudo chown $USER /home/user/hs20-server
+mkdir -p /home/user/hs20-server/spp
+mkdir -p /home/user/hs20-server/AS
+
+
+Build
+-----
+
+# hostapd as RADIUS server
+cd hostapd
+
+#example build configuration
+cat > .config < /home/user/hs20-server/terms-and-conditions <Terms and conditions..
+EOF
+
+# Build local keys and certs
+cd ca
+# Display help options.
+./setup.sh -h
+
+# Remove old keys, fill in appropriate values, and generate your keys.
+# For instance:
+./clean.sh
+rm -fr rootCA"
+old_hostname=myserver.local
+./setup.sh -C "Hotspot 2.0 Trust Root CA - CT" \
+ -o $old_hostname-osu-client \
+ -O $old_hostname-oscp -p lanforge -S $old_hostname \
+ -V $old_hostname-osu-revoked \
+ -m local -u http://$old_hostname:8888/
+
+# Configure subscription policies
+mkdir -p /home/user/hs20-server/spp/policy
+cat > /home/user/hs20-server/spp/policy/default.xml <
+
+ 30
+ ClientInitiated
+ Unrestricted
+ https://policy-server.osu.example.com/hs20/spp.php
+
+
+EOF
+
+
+# Install Hotspot 2.0 SPP and OMA DM XML schema/DTD files
+
+# XML schema for SPP
+# Copy the latest XML schema into /home/user/hs20-server/spp/spp.xsd
+
+# OMA DM Device Description Framework DTD
+# Copy into /home/user/hs20-server/spp/dm_ddf-v1_2.dtd
+# http://www.openmobilealliance.org/tech/DTD/dm_ddf-v1_2.dtd
+
+
+# Configure RADIUS authentication service
+# Note: Change the URL to match the setup
+# Note: Install AAA server key/certificate and root CA in Key directory
+
+cat > /home/user/hs20-server/AS/as-sql.conf < /home/user/hs20-server/AS/as.radius_clients <
+ Options Indexes MultiViews FollowSymLinks
+ AllowOverride None
+ Require all granted
+ SSLOptions +StdEnvVars
+
+
+Update SSL configuration to use the OSU server certificate/key.
+They keys and certs are called 'server.key' and 'server.pem' from
+ca/setup.sh.
+
+To support subscription remediation using client certificates, set
+"SSLVerifyClient optional" and configure the trust root CA(s) for the
+client certificates with SSLCACertificateFile.
+
+Enable default-ssl site and restart Apache2:
+ sudo a2ensite default-ssl
+ sudo a2enmod ssl
+ sudo service apache2 restart
+
+
+Management UI
+-------------
+
+The sample PHP scripts include a management UI for testing
+purposes. That is available at https:///hs20/users.php
+
+
+AP configuration
+----------------
+
+APs can now be configured to use the OSU server as the RADIUS
+authentication server. In addition, the OSU Provider List ANQP element
+should be configured to use the SPP (SOAP+XML) option and with the
+following Server URL:
+https:///hs20/spp.php/signup?realm=example.com
diff --git a/contrib/wpa/hs20/server/hs20_spp_server.c b/contrib/wpa/hs20/server/hs20_spp_server.c
new file mode 100644
index 00000000000..347c40a73d6
--- /dev/null
+++ b/contrib/wpa/hs20/server/hs20_spp_server.c
@@ -0,0 +1,207 @@
+/*
+ * Hotspot 2.0 SPP server - standalone version
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include
+#include
+
+#include "common.h"
+#include "common/version.h"
+#include "xml-utils.h"
+#include "spp_server.h"
+
+
+static void write_timestamp(FILE *f)
+{
+ time_t t;
+ struct tm *tm;
+
+ time(&t);
+ tm = localtime(&t);
+
+ fprintf(f, "%04u-%02u-%02u %02u:%02u:%02u ",
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+}
+
+
+void debug_print(struct hs20_svc *ctx, int print, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (ctx->debug_log == NULL)
+ return;
+
+ write_timestamp(ctx->debug_log);
+ va_start(ap, fmt);
+ vfprintf(ctx->debug_log, fmt, ap);
+ va_end(ap);
+
+ fprintf(ctx->debug_log, "\n");
+}
+
+
+void debug_dump_node(struct hs20_svc *ctx, const char *title, xml_node_t *node)
+{
+ char *str;
+
+ if (ctx->debug_log == NULL)
+ return;
+ str = xml_node_to_str(ctx->xml, node);
+ if (str == NULL)
+ return;
+
+ write_timestamp(ctx->debug_log);
+ fprintf(ctx->debug_log, "%s: '%s'\n", title, str);
+ os_free(str);
+}
+
+
+static int process(struct hs20_svc *ctx)
+{
+ int dmacc = 0;
+ xml_node_t *soap, *spp, *resp;
+ char *user, *realm, *post, *str;
+
+ ctx->addr = getenv("HS20ADDR");
+ if (ctx->addr)
+ debug_print(ctx, 1, "Connection from %s", ctx->addr);
+ ctx->test = getenv("HS20TEST");
+ if (ctx->test)
+ debug_print(ctx, 1, "Requested test functionality: %s",
+ ctx->test);
+
+ user = getenv("HS20USER");
+ if (user && strlen(user) == 0)
+ user = NULL;
+ realm = getenv("HS20REALM");
+ if (realm == NULL) {
+ debug_print(ctx, 1, "HS20REALM not set");
+ return -1;
+ }
+ post = getenv("HS20POST");
+ if (post == NULL) {
+ debug_print(ctx, 1, "HS20POST not set");
+ return -1;
+ }
+
+ ctx->imsi = getenv("HS20IMSI");
+ if (ctx->imsi)
+ debug_print(ctx, 1, "IMSI %s", ctx->imsi);
+
+ ctx->eap_method = getenv("HS20EAPMETHOD");
+ if (ctx->eap_method)
+ debug_print(ctx, 1, "EAP method %s", ctx->eap_method);
+
+ ctx->id_hash = getenv("HS20IDHASH");
+ if (ctx->id_hash)
+ debug_print(ctx, 1, "ID-HASH %s", ctx->id_hash);
+
+ soap = xml_node_from_buf(ctx->xml, post);
+ if (soap == NULL) {
+ debug_print(ctx, 1, "Could not parse SOAP data");
+ return -1;
+ }
+ debug_dump_node(ctx, "Received SOAP message", soap);
+ spp = soap_get_body(ctx->xml, soap);
+ if (spp == NULL) {
+ debug_print(ctx, 1, "Could not get SPP message");
+ xml_node_free(ctx->xml, soap);
+ return -1;
+ }
+ debug_dump_node(ctx, "Received SPP message", spp);
+
+ resp = hs20_spp_server_process(ctx, spp, user, realm, dmacc);
+ xml_node_free(ctx->xml, soap);
+ if (resp == NULL && user == NULL) {
+ debug_print(ctx, 1, "Request HTTP authentication");
+ return 2; /* Request authentication */
+ }
+ if (resp == NULL) {
+ debug_print(ctx, 1, "No response");
+ return -1;
+ }
+
+ soap = soap_build_envelope(ctx->xml, resp);
+ if (soap == NULL) {
+ debug_print(ctx, 1, "SOAP envelope building failed");
+ return -1;
+ }
+ str = xml_node_to_str(ctx->xml, soap);
+ xml_node_free(ctx->xml, soap);
+ if (str == NULL) {
+ debug_print(ctx, 1, "Could not get node string");
+ return -1;
+ }
+ printf("%s", str);
+ free(str);
+
+ return 0;
+}
+
+
+static void usage(void)
+{
+ printf("usage:\n"
+ "hs20_spp_server -r [-f]\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+ struct hs20_svc ctx;
+ int ret;
+
+ os_memset(&ctx, 0, sizeof(ctx));
+ for (;;) {
+ int c = getopt(argc, argv, "f:r:v");
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'f':
+ if (ctx.debug_log)
+ break;
+ ctx.debug_log = fopen(optarg, "a");
+ if (ctx.debug_log == NULL) {
+ printf("Could not write to %s\n", optarg);
+ return -1;
+ }
+ break;
+ case 'r':
+ ctx.root_dir = optarg;
+ break;
+ case 'v':
+ printf("hs20_spp_server v%s\n", VERSION_STR);
+ return 0;
+ default:
+ usage();
+ return -1;
+ }
+ }
+ if (ctx.root_dir == NULL) {
+ usage();
+ return -1;
+ }
+ ctx.xml = xml_node_init_ctx(&ctx, NULL);
+ if (ctx.xml == NULL)
+ return -1;
+ if (hs20_spp_server_init(&ctx) < 0) {
+ xml_node_deinit_ctx(ctx.xml);
+ return -1;
+ }
+
+ ret = process(&ctx);
+ debug_print(&ctx, 1, "process() --> %d", ret);
+
+ xml_node_deinit_ctx(ctx.xml);
+ hs20_spp_server_deinit(&ctx);
+ if (ctx.debug_log)
+ fclose(ctx.debug_log);
+
+ return ret;
+}
diff --git a/contrib/wpa/hs20/server/spp_server.c b/contrib/wpa/hs20/server/spp_server.c
new file mode 100644
index 00000000000..a50e9074f7b
--- /dev/null
+++ b/contrib/wpa/hs20/server/spp_server.c
@@ -0,0 +1,2933 @@
+/*
+ * Hotspot 2.0 SPP server
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "common.h"
+#include "base64.h"
+#include "md5_i.h"
+#include "xml-utils.h"
+#include "spp_server.h"
+
+
+#define SPP_NS_URI "http://www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp"
+
+#define URN_OMA_DM_DEVINFO "urn:oma:mo:oma-dm-devinfo:1.0"
+#define URN_OMA_DM_DEVDETAIL "urn:oma:mo:oma-dm-devdetail:1.0"
+#define URN_OMA_DM_DMACC "urn:oma:mo:oma-dm-dmacc:1.0"
+#define URN_HS20_PPS "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"
+
+
+/* TODO: timeout to expire sessions */
+
+enum hs20_session_operation {
+ NO_OPERATION,
+ UPDATE_PASSWORD,
+ CONTINUE_SUBSCRIPTION_REMEDIATION,
+ CONTINUE_POLICY_UPDATE,
+ USER_REMEDIATION,
+ SUBSCRIPTION_REGISTRATION,
+ POLICY_REMEDIATION,
+ POLICY_UPDATE,
+ FREE_REMEDIATION,
+ CLEAR_REMEDIATION,
+ CERT_REENROLL,
+};
+
+
+static char * db_get_session_val(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *session_id,
+ const char *field);
+static char * db_get_osu_config_val(struct hs20_svc *ctx, const char *realm,
+ const char *field);
+static xml_node_t * build_policy(struct hs20_svc *ctx, const char *user,
+ const char *realm, int use_dmacc);
+static xml_node_t * spp_exec_get_certificate(struct hs20_svc *ctx,
+ const char *session_id,
+ const char *user,
+ const char *realm,
+ int add_est_user);
+
+
+static int db_add_session(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *sessionid, const char *pw,
+ const char *redirect_uri,
+ enum hs20_session_operation operation,
+ const u8 *mac_addr)
+{
+ char *sql;
+ int ret = 0;
+ char addr[20];
+
+ if (mac_addr)
+ snprintf(addr, sizeof(addr), MACSTR, MAC2STR(mac_addr));
+ else
+ addr[0] = '\0';
+ sql = sqlite3_mprintf("INSERT INTO sessions(timestamp,id,user,realm,"
+ "operation,password,redirect_uri,mac_addr,test) "
+ "VALUES "
+ "(strftime('%%Y-%%m-%%d %%H:%%M:%%f','now'),"
+ "%Q,%Q,%Q,%d,%Q,%Q,%Q,%Q)",
+ sessionid, user ? user : "", realm ? realm : "",
+ operation, pw ? pw : "",
+ redirect_uri ? redirect_uri : "",
+ addr, ctx->test);
+ if (sql == NULL)
+ return -1;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session entry into sqlite "
+ "database: %s", sqlite3_errmsg(ctx->db));
+ ret = -1;
+ }
+ sqlite3_free(sql);
+ return ret;
+}
+
+
+static void db_update_session_password(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *sessionid,
+ const char *pw)
+{
+ char *sql;
+
+ sql = sqlite3_mprintf("UPDATE sessions SET password=%Q WHERE id=%Q AND "
+ "user=%Q AND realm=%Q",
+ pw, sessionid, user, realm);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to update session password: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_update_session_machine_managed(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm,
+ const char *sessionid,
+ const int pw_mm)
+{
+ char *sql;
+
+ sql = sqlite3_mprintf("UPDATE sessions SET machine_managed=%Q WHERE id=%Q AND user=%Q AND realm=%Q",
+ pw_mm ? "1" : "0", sessionid, user, realm);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1,
+ "Failed to update session machine_managed: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_pps(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *sessionid,
+ xml_node_t *node)
+{
+ char *str;
+ char *sql;
+
+ str = xml_node_to_str(ctx->xml, node);
+ if (str == NULL)
+ return;
+ sql = sqlite3_mprintf("UPDATE sessions SET pps=%Q WHERE id=%Q AND "
+ "user=%Q AND realm=%Q",
+ str, sessionid, user, realm);
+ free(str);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session pps: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_devinfo(struct hs20_svc *ctx, const char *sessionid,
+ xml_node_t *node)
+{
+ char *str;
+ char *sql;
+
+ str = xml_node_to_str(ctx->xml, node);
+ if (str == NULL)
+ return;
+ sql = sqlite3_mprintf("UPDATE sessions SET devinfo=%Q WHERE id=%Q",
+ str, sessionid);
+ free(str);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session devinfo: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_devdetail(struct hs20_svc *ctx,
+ const char *sessionid,
+ xml_node_t *node)
+{
+ char *str;
+ char *sql;
+
+ str = xml_node_to_str(ctx->xml, node);
+ if (str == NULL)
+ return;
+ sql = sqlite3_mprintf("UPDATE sessions SET devdetail=%Q WHERE id=%Q",
+ str, sessionid);
+ free(str);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session devdetail: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_dmacc(struct hs20_svc *ctx, const char *sessionid,
+ const char *username, const char *password)
+{
+ char *sql;
+
+ sql = sqlite3_mprintf("UPDATE sessions SET osu_user=%Q, osu_password=%Q WHERE id=%Q",
+ username, password, sessionid);
+ if (!sql)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session DMAcc: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_eap_method(struct hs20_svc *ctx,
+ const char *sessionid,
+ const char *method)
+{
+ char *sql;
+
+ sql = sqlite3_mprintf("UPDATE sessions SET eap_method=%Q WHERE id=%Q",
+ method, sessionid);
+ if (!sql)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session EAP method: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_id_hash(struct hs20_svc *ctx, const char *sessionid,
+ const char *id_hash)
+{
+ char *sql;
+
+ sql = sqlite3_mprintf("UPDATE sessions SET mobile_identifier_hash=%Q WHERE id=%Q",
+ id_hash, sessionid);
+ if (!sql)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session ID hash: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_remove_session(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *sessionid)
+{
+ char *sql;
+
+ if (user == NULL || realm == NULL) {
+ sql = sqlite3_mprintf("DELETE FROM sessions WHERE "
+ "id=%Q", sessionid);
+ } else {
+ sql = sqlite3_mprintf("DELETE FROM sessions WHERE "
+ "user=%Q AND realm=%Q AND id=%Q",
+ user, realm, sessionid);
+ }
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to delete session entry from "
+ "sqlite database: %s", sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void hs20_eventlog(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *sessionid, const char *notes,
+ const char *dump)
+{
+ char *sql;
+ char *user_buf = NULL, *realm_buf = NULL;
+
+ debug_print(ctx, 1, "eventlog: %s", notes);
+
+ if (user == NULL) {
+ user_buf = db_get_session_val(ctx, NULL, NULL, sessionid,
+ "user");
+ user = user_buf;
+ realm_buf = db_get_session_val(ctx, NULL, NULL, sessionid,
+ "realm");
+ realm = realm_buf;
+ }
+
+ sql = sqlite3_mprintf("INSERT INTO eventlog"
+ "(user,realm,sessionid,timestamp,notes,dump,addr)"
+ " VALUES (%Q,%Q,%Q,"
+ "strftime('%%Y-%%m-%%d %%H:%%M:%%f','now'),"
+ "%Q,%Q,%Q)",
+ user, realm, sessionid, notes,
+ dump ? dump : "", ctx->addr ? ctx->addr : "");
+ free(user_buf);
+ free(realm_buf);
+ if (sql == NULL)
+ return;
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add eventlog entry into sqlite "
+ "database: %s", sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void hs20_eventlog_node(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *sessionid, const char *notes,
+ xml_node_t *node)
+{
+ char *str;
+
+ if (node)
+ str = xml_node_to_str(ctx->xml, node);
+ else
+ str = NULL;
+ hs20_eventlog(ctx, user, realm, sessionid, notes, str);
+ free(str);
+}
+
+
+static void db_update_mo_str(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *name,
+ const char *str)
+{
+ char *sql;
+ if (user == NULL || realm == NULL || name == NULL)
+ return;
+ sql = sqlite3_mprintf("UPDATE users SET %s=%Q WHERE identity=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')",
+ name, str, user, realm);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to update user MO entry in sqlite "
+ "database: %s", sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_update_mo(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *name, xml_node_t *mo)
+{
+ char *str;
+
+ str = xml_node_to_str(ctx->xml, mo);
+ if (str == NULL)
+ return;
+
+ db_update_mo_str(ctx, user, realm, name, str);
+ free(str);
+}
+
+
+static void add_text_node(struct hs20_svc *ctx, xml_node_t *parent,
+ const char *name, const char *value)
+{
+ xml_node_create_text(ctx->xml, parent, NULL, name, value ? value : "");
+}
+
+
+static void add_text_node_conf(struct hs20_svc *ctx, const char *realm,
+ xml_node_t *parent, const char *name,
+ const char *field)
+{
+ char *val;
+ val = db_get_osu_config_val(ctx, realm, field);
+ xml_node_create_text(ctx->xml, parent, NULL, name, val ? val : "");
+ os_free(val);
+}
+
+
+static void add_text_node_conf_corrupt(struct hs20_svc *ctx, const char *realm,
+ xml_node_t *parent, const char *name,
+ const char *field)
+{
+ char *val;
+
+ val = db_get_osu_config_val(ctx, realm, field);
+ if (val) {
+ size_t len;
+
+ len = os_strlen(val);
+ if (len > 0) {
+ if (val[len - 1] == '0')
+ val[len - 1] = '1';
+ else
+ val[len - 1] = '0';
+ }
+ }
+ xml_node_create_text(ctx->xml, parent, NULL, name, val ? val : "");
+ os_free(val);
+}
+
+
+static int new_password(char *buf, int buflen)
+{
+ int i;
+
+ if (buflen < 1)
+ return -1;
+ buf[buflen - 1] = '\0';
+ if (os_get_random((unsigned char *) buf, buflen - 1) < 0)
+ return -1;
+
+ for (i = 0; i < buflen - 1; i++) {
+ unsigned char val = buf[i];
+ val %= 2 * 26 + 10;
+ if (val < 26)
+ buf[i] = 'a' + val;
+ else if (val < 2 * 26)
+ buf[i] = 'A' + val - 26;
+ else
+ buf[i] = '0' + val - 2 * 26;
+ }
+
+ return 0;
+}
+
+
+struct get_db_field_data {
+ const char *field;
+ char *value;
+};
+
+
+static int get_db_field(void *ctx, int argc, char *argv[], char *col[])
+{
+ struct get_db_field_data *data = ctx;
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ if (os_strcmp(col[i], data->field) == 0 && argv[i]) {
+ os_free(data->value);
+ data->value = os_strdup(argv[i]);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+static char * db_get_val(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *field, int dmacc)
+{
+ char *cmd;
+ struct get_db_field_data data;
+
+ cmd = sqlite3_mprintf("SELECT %s FROM users WHERE %s=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')",
+ field, dmacc ? "osu_user" : "identity",
+ user, realm);
+ if (cmd == NULL)
+ return NULL;
+ memset(&data, 0, sizeof(data));
+ data.field = field;
+ if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
+ {
+ debug_print(ctx, 1, "Could not find user '%s'", user);
+ sqlite3_free(cmd);
+ return NULL;
+ }
+ sqlite3_free(cmd);
+
+ debug_print(ctx, 1, "DB: user='%s' realm='%s' field='%s' dmacc=%d --> "
+ "value='%s'", user, realm, field, dmacc, data.value);
+
+ return data.value;
+}
+
+
+static int db_update_val(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *field,
+ const char *val, int dmacc)
+{
+ char *cmd;
+ int ret;
+
+ cmd = sqlite3_mprintf("UPDATE users SET %s=%Q WHERE %s=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')",
+ field, val, dmacc ? "osu_user" : "identity", user,
+ realm);
+ if (cmd == NULL)
+ return -1;
+ debug_print(ctx, 1, "DB: %s", cmd);
+ if (sqlite3_exec(ctx->db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1,
+ "Failed to update user in sqlite database: %s",
+ sqlite3_errmsg(ctx->db));
+ ret = -1;
+ } else {
+ debug_print(ctx, 1,
+ "DB: user='%s' realm='%s' field='%s' set to '%s'",
+ user, realm, field, val);
+ ret = 0;
+ }
+ sqlite3_free(cmd);
+
+ return ret;
+}
+
+
+static char * db_get_session_val(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *session_id,
+ const char *field)
+{
+ char *cmd;
+ struct get_db_field_data data;
+
+ if (user == NULL || realm == NULL) {
+ cmd = sqlite3_mprintf("SELECT %s FROM sessions WHERE "
+ "id=%Q", field, session_id);
+ } else {
+ cmd = sqlite3_mprintf("SELECT %s FROM sessions WHERE "
+ "user=%Q AND realm=%Q AND id=%Q",
+ field, user, realm, session_id);
+ }
+ if (cmd == NULL)
+ return NULL;
+ debug_print(ctx, 1, "DB: %s", cmd);
+ memset(&data, 0, sizeof(data));
+ data.field = field;
+ if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
+ {
+ debug_print(ctx, 1, "DB: Could not find session %s: %s",
+ session_id, sqlite3_errmsg(ctx->db));
+ sqlite3_free(cmd);
+ return NULL;
+ }
+ sqlite3_free(cmd);
+
+ debug_print(ctx, 1, "DB: return '%s'", data.value);
+ return data.value;
+}
+
+
+static int update_password(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *pw, int dmacc)
+{
+ char *cmd;
+
+ cmd = sqlite3_mprintf("UPDATE users SET password=%Q, "
+ "remediation='' "
+ "WHERE %s=%Q AND phase2=1",
+ pw, dmacc ? "osu_user" : "identity",
+ user);
+ if (cmd == NULL)
+ return -1;
+ debug_print(ctx, 1, "DB: %s", cmd);
+ if (sqlite3_exec(ctx->db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to update database for user '%s'",
+ user);
+ }
+ sqlite3_free(cmd);
+
+ return 0;
+}
+
+
+static int clear_remediation(struct hs20_svc *ctx, const char *user,
+ const char *realm, int dmacc)
+{
+ char *cmd;
+
+ cmd = sqlite3_mprintf("UPDATE users SET remediation='' WHERE %s=%Q",
+ dmacc ? "osu_user" : "identity",
+ user);
+ if (cmd == NULL)
+ return -1;
+ debug_print(ctx, 1, "DB: %s", cmd);
+ if (sqlite3_exec(ctx->db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to update database for user '%s'",
+ user);
+ }
+ sqlite3_free(cmd);
+
+ return 0;
+}
+
+
+static int add_eap_ttls(struct hs20_svc *ctx, xml_node_t *parent)
+{
+ xml_node_t *node;
+
+ node = xml_node_create(ctx->xml, parent, NULL, "EAPMethod");
+ if (node == NULL)
+ return -1;
+
+ add_text_node(ctx, node, "EAPType", "21");
+ add_text_node(ctx, node, "InnerMethod", "MS-CHAP-V2");
+
+ return 0;
+}
+
+
+static xml_node_t * build_username_password(struct hs20_svc *ctx,
+ xml_node_t *parent,
+ const char *user, const char *pw)
+{
+ xml_node_t *node;
+ char *b64;
+ size_t len;
+
+ node = xml_node_create(ctx->xml, parent, NULL, "UsernamePassword");
+ if (node == NULL)
+ return NULL;
+
+ add_text_node(ctx, node, "Username", user);
+
+ b64 = base64_encode(pw, strlen(pw), NULL);
+ if (b64 == NULL)
+ return NULL;
+ len = os_strlen(b64);
+ if (len > 0 && b64[len - 1] == '\n')
+ b64[len - 1] = '\0';
+ add_text_node(ctx, node, "Password", b64);
+ free(b64);
+
+ return node;
+}
+
+
+static int add_username_password(struct hs20_svc *ctx, xml_node_t *cred,
+ const char *user, const char *pw,
+ int machine_managed)
+{
+ xml_node_t *node;
+
+ node = build_username_password(ctx, cred, user, pw);
+ if (node == NULL)
+ return -1;
+
+ add_text_node(ctx, node, "MachineManaged",
+ machine_managed ? "TRUE" : "FALSE");
+ add_text_node(ctx, node, "SoftTokenApp", "");
+ add_eap_ttls(ctx, node);
+
+ return 0;
+}
+
+
+static void add_creation_date(struct hs20_svc *ctx, xml_node_t *cred)
+{
+ char str[30];
+ time_t now;
+ struct tm tm;
+
+ time(&now);
+ gmtime_r(&now, &tm);
+ snprintf(str, sizeof(str), "%04u-%02u-%02uT%02u:%02u:%02uZ",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ xml_node_create_text(ctx->xml, cred, NULL, "CreationDate", str);
+}
+
+
+static xml_node_t * build_credential_pw(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *pw, int machine_managed)
+{
+ xml_node_t *cred;
+
+ cred = xml_node_create_root(ctx->xml, NULL, NULL, NULL, "Credential");
+ if (cred == NULL) {
+ debug_print(ctx, 1, "Failed to create Credential node");
+ return NULL;
+ }
+ add_creation_date(ctx, cred);
+ if (add_username_password(ctx, cred, user, pw, machine_managed) < 0) {
+ xml_node_free(ctx->xml, cred);
+ return NULL;
+ }
+ add_text_node(ctx, cred, "Realm", realm);
+
+ return cred;
+}
+
+
+static xml_node_t * build_credential(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ char *new_pw, size_t new_pw_len)
+{
+ if (new_password(new_pw, new_pw_len) < 0)
+ return NULL;
+ debug_print(ctx, 1, "Update password to '%s'", new_pw);
+ return build_credential_pw(ctx, user, realm, new_pw, 1);
+}
+
+
+static xml_node_t * build_credential_cert(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *cert_fingerprint)
+{
+ xml_node_t *cred, *cert;
+
+ cred = xml_node_create_root(ctx->xml, NULL, NULL, NULL, "Credential");
+ if (cred == NULL) {
+ debug_print(ctx, 1, "Failed to create Credential node");
+ return NULL;
+ }
+ add_creation_date(ctx, cred);
+ cert = xml_node_create(ctx->xml, cred, NULL, "DigitalCertificate");
+ add_text_node(ctx, cert, "CertificateType", "x509v3");
+ add_text_node(ctx, cert, "CertSHA256Fingerprint", cert_fingerprint);
+ add_text_node(ctx, cred, "Realm", realm);
+
+ return cred;
+}
+
+
+static xml_node_t * build_post_dev_data_response(struct hs20_svc *ctx,
+ xml_namespace_t **ret_ns,
+ const char *session_id,
+ const char *status,
+ const char *error_code)
+{
+ xml_node_t *spp_node = NULL;
+ xml_namespace_t *ns;
+
+ spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
+ "sppPostDevDataResponse");
+ if (spp_node == NULL)
+ return NULL;
+ if (ret_ns)
+ *ret_ns = ns;
+
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", status);
+
+ if (error_code) {
+ xml_node_t *node;
+ node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
+ if (node)
+ xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
+ error_code);
+ }
+
+ return spp_node;
+}
+
+
+static int add_update_node(struct hs20_svc *ctx, xml_node_t *spp_node,
+ xml_namespace_t *ns, const char *uri,
+ xml_node_t *upd_node)
+{
+ xml_node_t *node, *tnds;
+ char *str;
+
+ tnds = mo_to_tnds(ctx->xml, upd_node, 0, NULL, NULL);
+ if (!tnds)
+ return -1;
+
+ str = xml_node_to_str(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (str == NULL)
+ return -1;
+ node = xml_node_create_text(ctx->xml, spp_node, ns, "updateNode", str);
+ free(str);
+
+ xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", uri);
+
+ return 0;
+}
+
+
+static xml_node_t * read_subrem_file(struct hs20_svc *ctx,
+ const char *subrem_id,
+ char *uri, size_t uri_size)
+{
+ char fname[200];
+ char *buf, *buf2, *pos;
+ size_t len;
+ xml_node_t *node;
+
+ os_snprintf(fname, sizeof(fname), "%s/spp/subrem/%s",
+ ctx->root_dir, subrem_id);
+ debug_print(ctx, 1, "Use subrem file %s", fname);
+
+ buf = os_readfile(fname, &len);
+ if (!buf)
+ return NULL;
+ buf2 = os_realloc(buf, len + 1);
+ if (!buf2) {
+ os_free(buf);
+ return NULL;
+ }
+ buf = buf2;
+ buf[len] = '\0';
+
+ pos = os_strchr(buf, '\n');
+ if (!pos) {
+ os_free(buf);
+ return NULL;
+ }
+ *pos++ = '\0';
+ os_strlcpy(uri, buf, uri_size);
+
+ node = xml_node_from_buf(ctx->xml, pos);
+ os_free(buf);
+
+ return node;
+}
+
+
+static xml_node_t * build_sub_rem_resp(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id,
+ int machine_rem, int dmacc)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *cred;
+ char buf[400];
+ char new_pw[33];
+ char *status;
+ char *cert;
+
+ cert = db_get_val(ctx, user, realm, "cert", dmacc);
+ if (cert && cert[0] == '\0') {
+ os_free(cert);
+ cert = NULL;
+ }
+ if (cert) {
+ char *subrem;
+
+ /* No change needed in PPS MO unless specifically asked to */
+ cred = NULL;
+ buf[0] = '\0';
+
+ subrem = db_get_val(ctx, user, realm, "subrem", dmacc);
+ if (subrem && subrem[0]) {
+ cred = read_subrem_file(ctx, subrem, buf, sizeof(buf));
+ if (!cred) {
+ debug_print(ctx, 1,
+ "Could not create updateNode from subrem file");
+ os_free(subrem);
+ os_free(cert);
+ return NULL;
+ }
+ }
+ os_free(subrem);
+ } else {
+ char *real_user = NULL;
+ char *pw;
+
+ if (dmacc) {
+ real_user = db_get_val(ctx, user, realm, "identity",
+ dmacc);
+ if (!real_user) {
+ debug_print(ctx, 1,
+ "Could not find user identity for dmacc user '%s'",
+ user);
+ return NULL;
+ }
+ }
+
+ pw = db_get_session_val(ctx, user, realm, session_id,
+ "password");
+ if (pw && pw[0]) {
+ debug_print(ctx, 1, "New password from the user: '%s'",
+ pw);
+ snprintf(new_pw, sizeof(new_pw), "%s", pw);
+ free(pw);
+ cred = build_credential_pw(ctx,
+ real_user ? real_user : user,
+ realm, new_pw, 0);
+ } else {
+ cred = build_credential(ctx,
+ real_user ? real_user : user,
+ realm, new_pw, sizeof(new_pw));
+ }
+
+ free(real_user);
+ if (!cred) {
+ debug_print(ctx, 1, "Could not build credential");
+ os_free(cert);
+ return NULL;
+ }
+
+ snprintf(buf, sizeof(buf),
+ "./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential",
+ realm);
+ }
+
+ status = "Remediation complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL) {
+ debug_print(ctx, 1, "Could not build sppPostDevDataResponse");
+ os_free(cert);
+ return NULL;
+ }
+
+ if ((cred && add_update_node(ctx, spp_node, ns, buf, cred) < 0) ||
+ (!cred && !xml_node_create(ctx->xml, spp_node, ns, "noMOUpdate"))) {
+ debug_print(ctx, 1, "Could not add update node");
+ xml_node_free(ctx->xml, spp_node);
+ os_free(cert);
+ return NULL;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ machine_rem ? "machine remediation" :
+ "user remediation", cred);
+ xml_node_free(ctx->xml, cred);
+
+ if (cert) {
+ debug_print(ctx, 1, "Request DB remediation clearing on success notification (certificate credential)");
+ db_add_session(ctx, user, realm, session_id, NULL, NULL,
+ CLEAR_REMEDIATION, NULL);
+ } else {
+ debug_print(ctx, 1, "Request DB password update on success "
+ "notification");
+ db_add_session(ctx, user, realm, session_id, new_pw, NULL,
+ UPDATE_PASSWORD, NULL);
+ }
+ os_free(cert);
+
+ return spp_node;
+}
+
+
+static xml_node_t * machine_remediation(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm,
+ const char *session_id, int dmacc)
+{
+ return build_sub_rem_resp(ctx, user, realm, session_id, 1, dmacc);
+}
+
+
+static xml_node_t * cert_reenroll(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm,
+ const char *session_id)
+{
+ db_add_session(ctx, user, realm, session_id, NULL, NULL,
+ CERT_REENROLL, NULL);
+ return spp_exec_get_certificate(ctx, session_id, user, realm, 0);
+}
+
+
+static xml_node_t * policy_remediation(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id, int dmacc)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *policy;
+ char buf[400];
+ const char *status;
+
+ hs20_eventlog(ctx, user, realm, session_id,
+ "requires policy remediation", NULL);
+
+ db_add_session(ctx, user, realm, session_id, NULL, NULL,
+ POLICY_REMEDIATION, NULL);
+
+ policy = build_policy(ctx, user, realm, dmacc);
+ if (!policy) {
+ return build_post_dev_data_response(
+ ctx, NULL, session_id,
+ "No update available at this time", NULL);
+ }
+
+ status = "Remediation complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ snprintf(buf, sizeof(buf),
+ "./Wi-Fi/%s/PerProviderSubscription/Cred01/Policy",
+ realm);
+
+ if (add_update_node(ctx, spp_node, ns, buf, policy) < 0) {
+ xml_node_free(ctx->xml, spp_node);
+ xml_node_free(ctx->xml, policy);
+ return NULL;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "policy update (sub rem)", policy);
+ xml_node_free(ctx->xml, policy);
+
+ return spp_node;
+}
+
+
+static xml_node_t * browser_remediation(struct hs20_svc *ctx,
+ const char *session_id,
+ const char *redirect_uri,
+ const char *uri)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *exec_node;
+
+ if (redirect_uri == NULL) {
+ debug_print(ctx, 1, "Missing redirectURI attribute for user "
+ "remediation");
+ return NULL;
+ }
+ debug_print(ctx, 1, "redirectURI %s", redirect_uri);
+
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+ xml_node_create_text(ctx->xml, exec_node, ns, "launchBrowserToURI",
+ uri);
+ return spp_node;
+}
+
+
+static xml_node_t * user_remediation(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *session_id,
+ const char *redirect_uri)
+{
+ char uri[300], *val;
+
+ hs20_eventlog(ctx, user, realm, session_id,
+ "requires user remediation", NULL);
+ val = db_get_osu_config_val(ctx, realm, "remediation_url");
+ if (val == NULL)
+ return NULL;
+
+ db_add_session(ctx, user, realm, session_id, NULL, redirect_uri,
+ USER_REMEDIATION, NULL);
+
+ snprintf(uri, sizeof(uri), "%s%s", val, session_id);
+ os_free(val);
+ return browser_remediation(ctx, session_id, redirect_uri, uri);
+}
+
+
+static xml_node_t * free_remediation(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id,
+ const char *redirect_uri)
+{
+ char uri[300], *val;
+
+ hs20_eventlog(ctx, user, realm, session_id,
+ "requires free/public account remediation", NULL);
+ val = db_get_osu_config_val(ctx, realm, "free_remediation_url");
+ if (val == NULL)
+ return NULL;
+
+ db_add_session(ctx, user, realm, session_id, NULL, redirect_uri,
+ FREE_REMEDIATION, NULL);
+
+ snprintf(uri, sizeof(uri), "%s%s", val, session_id);
+ os_free(val);
+ return browser_remediation(ctx, session_id, redirect_uri, uri);
+}
+
+
+static xml_node_t * no_sub_rem(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id)
+{
+ const char *status;
+
+ hs20_eventlog(ctx, user, realm, session_id,
+ "no subscription mediation available", NULL);
+
+ status = "No update available at this time";
+ return build_post_dev_data_response(ctx, NULL, session_id, status,
+ NULL);
+}
+
+
+static xml_node_t * hs20_subscription_remediation(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm,
+ const char *session_id,
+ int dmacc,
+ const char *redirect_uri)
+{
+ char *type, *identity;
+ xml_node_t *ret;
+ char *free_account;
+
+ identity = db_get_val(ctx, user, realm, "identity", dmacc);
+ if (identity == NULL || strlen(identity) == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "user not found in database for remediation",
+ NULL);
+ os_free(identity);
+ return build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred",
+ "Not found");
+ }
+ os_free(identity);
+
+ free_account = db_get_osu_config_val(ctx, realm, "free_account");
+ if (free_account && strcmp(free_account, user) == 0) {
+ free(free_account);
+ return no_sub_rem(ctx, user, realm, session_id);
+ }
+ free(free_account);
+
+ type = db_get_val(ctx, user, realm, "remediation", dmacc);
+ if (type && strcmp(type, "free") != 0) {
+ char *val;
+ int shared = 0;
+ val = db_get_val(ctx, user, realm, "shared", dmacc);
+ if (val)
+ shared = atoi(val);
+ free(val);
+ if (shared) {
+ free(type);
+ return no_sub_rem(ctx, user, realm, session_id);
+ }
+ }
+ if (type && strcmp(type, "user") == 0)
+ ret = user_remediation(ctx, user, realm, session_id,
+ redirect_uri);
+ else if (type && strcmp(type, "free") == 0)
+ ret = free_remediation(ctx, user, realm, session_id,
+ redirect_uri);
+ else if (type && strcmp(type, "policy") == 0)
+ ret = policy_remediation(ctx, user, realm, session_id, dmacc);
+ else if (type && strcmp(type, "machine") == 0)
+ ret = machine_remediation(ctx, user, realm, session_id, dmacc);
+ else if (type && strcmp(type, "reenroll") == 0)
+ ret = cert_reenroll(ctx, user, realm, session_id);
+ else
+ ret = no_sub_rem(ctx, user, realm, session_id);
+ free(type);
+
+ return ret;
+}
+
+
+static xml_node_t * read_policy_file(struct hs20_svc *ctx,
+ const char *policy_id)
+{
+ char fname[200];
+
+ snprintf(fname, sizeof(fname), "%s/spp/policy/%s.xml",
+ ctx->root_dir, policy_id);
+ debug_print(ctx, 1, "Use policy file %s", fname);
+
+ return node_from_file(ctx->xml, fname);
+}
+
+
+static void update_policy_update_uri(struct hs20_svc *ctx, const char *realm,
+ xml_node_t *policy)
+{
+ xml_node_t *node;
+ char *url;
+
+ node = get_node_uri(ctx->xml, policy, "Policy/PolicyUpdate/URI");
+ if (!node)
+ return;
+
+ url = db_get_osu_config_val(ctx, realm, "policy_url");
+ if (!url)
+ return;
+ xml_node_set_text(ctx->xml, node, url);
+ free(url);
+}
+
+
+static xml_node_t * build_policy(struct hs20_svc *ctx, const char *user,
+ const char *realm, int use_dmacc)
+{
+ char *policy_id;
+ xml_node_t *policy, *node;
+
+ policy_id = db_get_val(ctx, user, realm, "policy", use_dmacc);
+ if (policy_id == NULL || strlen(policy_id) == 0) {
+ free(policy_id);
+ policy_id = strdup("default");
+ if (policy_id == NULL)
+ return NULL;
+ }
+ policy = read_policy_file(ctx, policy_id);
+ free(policy_id);
+ if (policy == NULL)
+ return NULL;
+
+ update_policy_update_uri(ctx, realm, policy);
+
+ node = get_node_uri(ctx->xml, policy, "Policy/PolicyUpdate");
+ if (node && use_dmacc) {
+ char *pw;
+ pw = db_get_val(ctx, user, realm, "osu_password", use_dmacc);
+ if (pw == NULL ||
+ build_username_password(ctx, node, user, pw) == NULL) {
+ debug_print(ctx, 1, "Failed to add Policy/PolicyUpdate/"
+ "UsernamePassword");
+ free(pw);
+ xml_node_free(ctx->xml, policy);
+ return NULL;
+ }
+ free(pw);
+ }
+
+ return policy;
+}
+
+
+static xml_node_t * hs20_policy_update(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id, int dmacc)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node;
+ xml_node_t *policy;
+ char buf[400];
+ const char *status;
+ char *identity;
+
+ identity = db_get_val(ctx, user, realm, "identity", dmacc);
+ if (identity == NULL || strlen(identity) == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "user not found in database for policy update",
+ NULL);
+ os_free(identity);
+ return build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred",
+ "Not found");
+ }
+ os_free(identity);
+
+ policy = build_policy(ctx, user, realm, dmacc);
+ if (!policy) {
+ return build_post_dev_data_response(
+ ctx, NULL, session_id,
+ "No update available at this time", NULL);
+ }
+
+ db_add_session(ctx, user, realm, session_id, NULL, NULL, POLICY_UPDATE,
+ NULL);
+
+ status = "Update complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ snprintf(buf, sizeof(buf),
+ "./Wi-Fi/%s/PerProviderSubscription/Cred01/Policy",
+ realm);
+
+ if (add_update_node(ctx, spp_node, ns, buf, policy) < 0) {
+ xml_node_free(ctx->xml, spp_node);
+ xml_node_free(ctx->xml, policy);
+ return NULL;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id, "policy update",
+ policy);
+ xml_node_free(ctx->xml, policy);
+
+ return spp_node;
+}
+
+
+static xml_node_t * spp_get_mo(struct hs20_svc *ctx, xml_node_t *node,
+ const char *urn, int *valid, char **ret_err)
+{
+ xml_node_t *child, *tnds, *mo;
+ const char *name;
+ char *mo_urn;
+ char *str;
+ char fname[200];
+
+ *valid = -1;
+ if (ret_err)
+ *ret_err = NULL;
+
+ 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 (strcmp(name, "moContainer") != 0)
+ continue;
+ mo_urn = xml_node_get_attr_value_ns(ctx->xml, child, SPP_NS_URI,
+ "moURN");
+ if (strcasecmp(urn, mo_urn) == 0) {
+ xml_node_get_attr_value_free(ctx->xml, mo_urn);
+ break;
+ }
+ xml_node_get_attr_value_free(ctx->xml, mo_urn);
+ }
+
+ if (child == NULL)
+ return NULL;
+
+ debug_print(ctx, 1, "moContainer text for %s", urn);
+ debug_dump_node(ctx, "moContainer", child);
+
+ str = xml_node_get_text(ctx->xml, child);
+ debug_print(ctx, 1, "moContainer payload: '%s'", str);
+ tnds = xml_node_from_buf(ctx->xml, str);
+ xml_node_get_text_free(ctx->xml, str);
+ if (tnds == NULL) {
+ debug_print(ctx, 1, "could not parse moContainer text");
+ return NULL;
+ }
+
+ snprintf(fname, sizeof(fname), "%s/spp/dm_ddf-v1_2.dtd", ctx->root_dir);
+ if (xml_validate_dtd(ctx->xml, tnds, fname, ret_err) == 0)
+ *valid = 1;
+ else if (ret_err && *ret_err &&
+ os_strcmp(*ret_err, "No declaration for attribute xmlns of element MgmtTree\n") == 0) {
+ free(*ret_err);
+ debug_print(ctx, 1, "Ignore OMA-DM DDF DTD validation error for MgmtTree namespace declaration with xmlns attribute");
+ *ret_err = NULL;
+ *valid = 1;
+ } else
+ *valid = 0;
+
+ mo = tnds_to_mo(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (mo == NULL) {
+ debug_print(ctx, 1, "invalid moContainer for %s", urn);
+ }
+
+ return mo;
+}
+
+
+static xml_node_t * spp_exec_upload_mo(struct hs20_svc *ctx,
+ const char *session_id, const char *urn)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *node, *exec_node;
+
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+
+ node = xml_node_create(ctx->xml, exec_node, ns, "uploadMO");
+ xml_node_add_attr(ctx->xml, node, ns, "moURN", urn);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_subscription_registration(struct hs20_svc *ctx,
+ const char *realm,
+ const char *session_id,
+ const char *redirect_uri,
+ const u8 *mac_addr)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *exec_node;
+ char uri[300], *val;
+
+ if (db_add_session(ctx, NULL, realm, session_id, NULL, redirect_uri,
+ SUBSCRIPTION_REGISTRATION, mac_addr) < 0)
+ return NULL;
+ val = db_get_osu_config_val(ctx, realm, "signup_url");
+ if (val == NULL)
+ return NULL;
+
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+
+ snprintf(uri, sizeof(uri), "%s%s", val, session_id);
+ os_free(val);
+ xml_node_create_text(ctx->xml, exec_node, ns, "launchBrowserToURI",
+ uri);
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_remediation(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm, int dmacc,
+ const char *session_id)
+{
+ return build_sub_rem_resp(ctx, user, realm, session_id, 0, dmacc);
+}
+
+
+static char * db_get_osu_config_val(struct hs20_svc *ctx, const char *realm,
+ const char *field)
+{
+ char *cmd;
+ struct get_db_field_data data;
+
+ cmd = sqlite3_mprintf("SELECT value FROM osu_config WHERE realm=%Q AND "
+ "field=%Q", realm, field);
+ if (cmd == NULL)
+ return NULL;
+ debug_print(ctx, 1, "DB: %s", cmd);
+ memset(&data, 0, sizeof(data));
+ data.field = "value";
+ if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
+ {
+ debug_print(ctx, 1, "DB: Could not find osu_config %s: %s",
+ realm, sqlite3_errmsg(ctx->db));
+ sqlite3_free(cmd);
+ return NULL;
+ }
+ sqlite3_free(cmd);
+
+ debug_print(ctx, 1, "DB: return '%s'", data.value);
+ return data.value;
+}
+
+
+static xml_node_t * build_pps(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *pw, const char *cert,
+ int machine_managed, const char *test,
+ const char *imsi, const char *dmacc_username,
+ const char *dmacc_password,
+ xml_node_t *policy_node)
+{
+ xml_node_t *pps, *c, *trust, *aaa, *aaa1, *upd, *homesp, *p;
+ xml_node_t *cred, *eap, *userpw;
+
+ pps = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+ "PerProviderSubscription");
+ if (!pps) {
+ xml_node_free(ctx->xml, policy_node);
+ return NULL;
+ }
+
+ add_text_node(ctx, pps, "UpdateIdentifier", "1");
+
+ c = xml_node_create(ctx->xml, pps, NULL, "Cred01");
+
+ add_text_node(ctx, c, "CredentialPriority", "1");
+
+ if (imsi)
+ goto skip_aaa_trust_root;
+ aaa = xml_node_create(ctx->xml, c, NULL, "AAAServerTrustRoot");
+ aaa1 = xml_node_create(ctx->xml, aaa, NULL, "AAA1");
+ add_text_node_conf(ctx, realm, aaa1, "CertURL",
+ "aaa_trust_root_cert_url");
+ if (test && os_strcmp(test, "corrupt_aaa_hash") == 0) {
+ debug_print(ctx, 1,
+ "TEST: Corrupt PPS/Cred*/AAAServerTrustRoot/Root*/CertSHA256FingerPrint");
+ add_text_node_conf_corrupt(ctx, realm, aaa1,
+ "CertSHA256Fingerprint",
+ "aaa_trust_root_cert_fingerprint");
+ } else {
+ add_text_node_conf(ctx, realm, aaa1, "CertSHA256Fingerprint",
+ "aaa_trust_root_cert_fingerprint");
+ }
+
+ if (test && os_strcmp(test, "corrupt_polupd_hash") == 0) {
+ debug_print(ctx, 1,
+ "TEST: Corrupt PPS/Cred*/Policy/PolicyUpdate/Trustroot/CertSHA256FingerPrint");
+ p = xml_node_create(ctx->xml, c, NULL, "Policy");
+ upd = xml_node_create(ctx->xml, p, NULL, "PolicyUpdate");
+ add_text_node(ctx, upd, "UpdateInterval", "30");
+ add_text_node(ctx, upd, "UpdateMethod", "SPP-ClientInitiated");
+ add_text_node(ctx, upd, "Restriction", "Unrestricted");
+ add_text_node_conf(ctx, realm, upd, "URI", "policy_url");
+ trust = xml_node_create(ctx->xml, upd, NULL, "TrustRoot");
+ add_text_node_conf(ctx, realm, trust, "CertURL",
+ "policy_trust_root_cert_url");
+ add_text_node_conf_corrupt(ctx, realm, trust,
+ "CertSHA256Fingerprint",
+ "policy_trust_root_cert_fingerprint");
+ }
+skip_aaa_trust_root:
+
+ upd = xml_node_create(ctx->xml, c, NULL, "SubscriptionUpdate");
+ add_text_node(ctx, upd, "UpdateInterval", "4294967295");
+ add_text_node(ctx, upd, "UpdateMethod", "SPP-ClientInitiated");
+ add_text_node(ctx, upd, "Restriction", "HomeSP");
+ add_text_node_conf(ctx, realm, upd, "URI", "spp_http_auth_url");
+ trust = xml_node_create(ctx->xml, upd, NULL, "TrustRoot");
+ add_text_node_conf(ctx, realm, trust, "CertURL", "trust_root_cert_url");
+ if (test && os_strcmp(test, "corrupt_subrem_hash") == 0) {
+ debug_print(ctx, 1,
+ "TEST: Corrupt PPS/Cred*/SubscriptionUpdate/Trustroot/CertSHA256FingerPrint");
+ add_text_node_conf_corrupt(ctx, realm, trust,
+ "CertSHA256Fingerprint",
+ "trust_root_cert_fingerprint");
+ } else {
+ add_text_node_conf(ctx, realm, trust, "CertSHA256Fingerprint",
+ "trust_root_cert_fingerprint");
+ }
+
+ if (dmacc_username &&
+ !build_username_password(ctx, upd, dmacc_username,
+ dmacc_password)) {
+ xml_node_free(ctx->xml, pps);
+ xml_node_free(ctx->xml, policy_node);
+ return NULL;
+ }
+
+ if (policy_node)
+ xml_node_add_child(ctx->xml, c, policy_node);
+
+ homesp = xml_node_create(ctx->xml, c, NULL, "HomeSP");
+ add_text_node_conf(ctx, realm, homesp, "FriendlyName", "friendly_name");
+ add_text_node_conf(ctx, realm, homesp, "FQDN", "fqdn");
+
+ xml_node_create(ctx->xml, c, NULL, "SubscriptionParameters");
+
+ cred = xml_node_create(ctx->xml, c, NULL, "Credential");
+ add_creation_date(ctx, cred);
+ if (imsi) {
+ xml_node_t *sim;
+ const char *type = "18"; /* default to EAP-SIM */
+
+ sim = xml_node_create(ctx->xml, cred, NULL, "SIM");
+ add_text_node(ctx, sim, "IMSI", imsi);
+ if (ctx->eap_method && os_strcmp(ctx->eap_method, "AKA") == 0)
+ type = "23";
+ else if (ctx->eap_method &&
+ os_strcmp(ctx->eap_method, "AKA'") == 0)
+ type = "50";
+ add_text_node(ctx, sim, "EAPType", type);
+ } else if (cert) {
+ xml_node_t *dc;
+ dc = xml_node_create(ctx->xml, cred, NULL,
+ "DigitalCertificate");
+ add_text_node(ctx, dc, "CertificateType", "x509v3");
+ add_text_node(ctx, dc, "CertSHA256Fingerprint", cert);
+ } else {
+ userpw = build_username_password(ctx, cred, user, pw);
+ add_text_node(ctx, userpw, "MachineManaged",
+ machine_managed ? "TRUE" : "FALSE");
+ eap = xml_node_create(ctx->xml, userpw, NULL, "EAPMethod");
+ add_text_node(ctx, eap, "EAPType", "21");
+ add_text_node(ctx, eap, "InnerMethod", "MS-CHAP-V2");
+ }
+ add_text_node(ctx, cred, "Realm", realm);
+
+ return pps;
+}
+
+
+static xml_node_t * spp_exec_get_certificate(struct hs20_svc *ctx,
+ const char *session_id,
+ const char *user,
+ const char *realm,
+ int add_est_user)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *enroll, *exec_node;
+ char *val;
+ char password[11];
+ char *b64;
+
+ if (add_est_user && new_password(password, sizeof(password)) < 0)
+ return NULL;
+
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+
+ enroll = xml_node_create(ctx->xml, exec_node, ns, "getCertificate");
+ xml_node_add_attr(ctx->xml, enroll, NULL, "enrollmentProtocol", "EST");
+
+ val = db_get_osu_config_val(ctx, realm, "est_url");
+ xml_node_create_text(ctx->xml, enroll, ns, "enrollmentServerURI",
+ val ? val : "");
+ os_free(val);
+
+ if (!add_est_user)
+ return spp_node;
+
+ xml_node_create_text(ctx->xml, enroll, ns, "estUserID", user);
+
+ b64 = base64_encode(password, strlen(password), NULL);
+ if (b64 == NULL) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+ xml_node_create_text(ctx->xml, enroll, ns, "estPassword", b64);
+ free(b64);
+
+ db_update_session_password(ctx, user, realm, session_id, password);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_registration(struct hs20_svc *ctx,
+ const char *session_id,
+ int enrollment_done)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *node = NULL;
+ xml_node_t *pps, *tnds;
+ char buf[400];
+ char *str;
+ char *user, *realm, *pw, *type, *mm, *test;
+ const char *status;
+ int cert = 0;
+ int machine_managed = 0;
+ char *fingerprint;
+
+ user = db_get_session_val(ctx, NULL, NULL, session_id, "user");
+ realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm");
+ pw = db_get_session_val(ctx, NULL, NULL, session_id, "password");
+
+ if (!user || !realm || !pw) {
+ debug_print(ctx, 1, "Could not find session info from DB for "
+ "the new subscription");
+ free(user);
+ free(realm);
+ free(pw);
+ return NULL;
+ }
+
+ mm = db_get_session_val(ctx, NULL, NULL, session_id, "machine_managed");
+ if (mm && atoi(mm))
+ machine_managed = 1;
+ free(mm);
+
+ type = db_get_session_val(ctx, NULL, NULL, session_id, "type");
+ if (type && strcmp(type, "cert") == 0)
+ cert = 1;
+ free(type);
+
+ if (cert && !enrollment_done) {
+ xml_node_t *ret;
+ hs20_eventlog(ctx, user, realm, session_id,
+ "request client certificate enrollment", NULL);
+ ret = spp_exec_get_certificate(ctx, session_id, user, realm, 1);
+ free(user);
+ free(realm);
+ free(pw);
+ return ret;
+ }
+
+ if (!cert && strlen(pw) == 0) {
+ machine_managed = 1;
+ free(pw);
+ pw = malloc(11);
+ if (pw == NULL || new_password(pw, 11) < 0) {
+ free(user);
+ free(realm);
+ free(pw);
+ return NULL;
+ }
+ }
+
+ status = "Provisioning complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ fingerprint = db_get_session_val(ctx, NULL, NULL, session_id, "cert");
+ test = db_get_session_val(ctx, NULL, NULL, session_id, "test");
+ if (test)
+ debug_print(ctx, 1, "TEST: Requested special behavior: %s",
+ test);
+ pps = build_pps(ctx, user, realm, pw,
+ fingerprint ? fingerprint : NULL, machine_managed,
+ test, NULL, NULL, NULL, NULL);
+ free(fingerprint);
+ free(test);
+ if (!pps) {
+ xml_node_free(ctx->xml, spp_node);
+ free(user);
+ free(realm);
+ free(pw);
+ return NULL;
+ }
+
+ debug_print(ctx, 1, "Request DB subscription registration on success "
+ "notification");
+ if (machine_managed) {
+ db_update_session_password(ctx, user, realm, session_id, pw);
+ db_update_session_machine_managed(ctx, user, realm, session_id,
+ machine_managed);
+ }
+ db_add_session_pps(ctx, user, realm, session_id, pps);
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "new subscription", pps);
+ free(user);
+ free(pw);
+
+ tnds = mo_to_tnds(ctx->xml, pps, 0, URN_HS20_PPS, NULL);
+ xml_node_free(ctx->xml, pps);
+ if (!tnds) {
+ xml_node_free(ctx->xml, spp_node);
+ free(realm);
+ return NULL;
+ }
+
+ str = xml_node_to_str(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (str == NULL) {
+ xml_node_free(ctx->xml, spp_node);
+ free(realm);
+ return NULL;
+ }
+
+ node = xml_node_create_text(ctx->xml, spp_node, ns, "addMO", str);
+ free(str);
+ snprintf(buf, sizeof(buf), "./Wi-Fi/%s/PerProviderSubscription", realm);
+ free(realm);
+ xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", buf);
+ xml_node_add_attr(ctx->xml, node, ns, "moURN", URN_HS20_PPS);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_free_remediation(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm,
+ const char *session_id)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node;
+ xml_node_t *cred;
+ char buf[400];
+ char *status;
+ char *free_account, *pw;
+
+ free_account = db_get_osu_config_val(ctx, realm, "free_account");
+ if (free_account == NULL)
+ return NULL;
+ pw = db_get_val(ctx, free_account, realm, "password", 0);
+ if (pw == NULL) {
+ free(free_account);
+ return NULL;
+ }
+
+ cred = build_credential_pw(ctx, free_account, realm, pw, 1);
+ free(free_account);
+ free(pw);
+ if (!cred) {
+ xml_node_free(ctx->xml, cred);
+ return NULL;
+ }
+
+ status = "Remediation complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ snprintf(buf, sizeof(buf),
+ "./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential",
+ realm);
+
+ if (add_update_node(ctx, spp_node, ns, buf, cred) < 0) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "free/public remediation", cred);
+ xml_node_free(ctx->xml, cred);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_complete(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm, int dmacc,
+ const char *session_id)
+{
+ char *val;
+ enum hs20_session_operation oper;
+
+ val = db_get_session_val(ctx, user, realm, session_id, "operation");
+ if (val == NULL) {
+ debug_print(ctx, 1, "No session %s found to continue",
+ session_id);
+ return NULL;
+ }
+ oper = atoi(val);
+ free(val);
+
+ if (oper == USER_REMEDIATION) {
+ return hs20_user_input_remediation(ctx, user, realm, dmacc,
+ session_id);
+ }
+
+ if (oper == FREE_REMEDIATION) {
+ return hs20_user_input_free_remediation(ctx, user, realm,
+ session_id);
+ }
+
+ if (oper == SUBSCRIPTION_REGISTRATION) {
+ return hs20_user_input_registration(ctx, session_id, 0);
+ }
+
+ debug_print(ctx, 1, "User session %s not in state for user input "
+ "completion", session_id);
+ return NULL;
+}
+
+
+static xml_node_t * hs20_cert_reenroll_complete(struct hs20_svc *ctx,
+ const char *session_id)
+{
+ char *user, *realm, *cert;
+ char *status;
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *cred;
+ char buf[400];
+
+ user = db_get_session_val(ctx, NULL, NULL, session_id, "user");
+ realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm");
+ cert = db_get_session_val(ctx, NULL, NULL, session_id, "cert");
+ if (!user || !realm || !cert) {
+ debug_print(ctx, 1,
+ "Could not find session info from DB for certificate reenrollment");
+ free(user);
+ free(realm);
+ free(cert);
+ return NULL;
+ }
+
+ cred = build_credential_cert(ctx, user, realm, cert);
+ if (!cred) {
+ debug_print(ctx, 1, "Could not build credential");
+ free(user);
+ free(realm);
+ free(cert);
+ return NULL;
+ }
+
+ status = "Remediation complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL) {
+ debug_print(ctx, 1, "Could not build sppPostDevDataResponse");
+ free(user);
+ free(realm);
+ free(cert);
+ xml_node_free(ctx->xml, cred);
+ return NULL;
+ }
+
+ snprintf(buf, sizeof(buf),
+ "./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential",
+ realm);
+
+ if (add_update_node(ctx, spp_node, ns, buf, cred) < 0) {
+ debug_print(ctx, 1, "Could not add update node");
+ xml_node_free(ctx->xml, spp_node);
+ free(user);
+ free(realm);
+ free(cert);
+ return NULL;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "certificate reenrollment", cred);
+ xml_node_free(ctx->xml, cred);
+
+ free(user);
+ free(realm);
+ free(cert);
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_cert_enroll_completed(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm, int dmacc,
+ const char *session_id)
+{
+ char *val;
+ enum hs20_session_operation oper;
+
+ val = db_get_session_val(ctx, NULL, NULL, session_id, "operation");
+ if (val == NULL) {
+ debug_print(ctx, 1, "No session %s found to continue",
+ session_id);
+ return NULL;
+ }
+ oper = atoi(val);
+ free(val);
+
+ if (oper == SUBSCRIPTION_REGISTRATION)
+ return hs20_user_input_registration(ctx, session_id, 1);
+ if (oper == CERT_REENROLL)
+ return hs20_cert_reenroll_complete(ctx, session_id);
+
+ debug_print(ctx, 1, "User session %s not in state for certificate "
+ "enrollment completion", session_id);
+ return NULL;
+}
+
+
+static xml_node_t * hs20_cert_enroll_failed(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm, int dmacc,
+ const char *session_id)
+{
+ char *val;
+ enum hs20_session_operation oper;
+ xml_node_t *spp_node, *node;
+ char *status;
+ xml_namespace_t *ns;
+
+ val = db_get_session_val(ctx, user, realm, session_id, "operation");
+ if (val == NULL) {
+ debug_print(ctx, 1, "No session %s found to continue",
+ session_id);
+ return NULL;
+ }
+ oper = atoi(val);
+ free(val);
+
+ if (oper != SUBSCRIPTION_REGISTRATION) {
+ debug_print(ctx, 1, "User session %s not in state for "
+ "enrollment failure", session_id);
+ return NULL;
+ }
+
+ status = "Error occurred";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+ node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
+ xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
+ "Credentials cannot be provisioned at this time");
+ db_remove_session(ctx, user, realm, session_id);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_sim_provisioning(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm, int dmacc,
+ const char *session_id)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *node = NULL;
+ xml_node_t *pps, *tnds;
+ char buf[400];
+ char *str;
+ const char *status;
+ char dmacc_username[32];
+ char dmacc_password[32];
+ char *policy;
+ xml_node_t *policy_node = NULL;
+
+ if (!ctx->imsi) {
+ debug_print(ctx, 1, "IMSI not available for SIM provisioning");
+ return NULL;
+ }
+
+ if (new_password(dmacc_username, sizeof(dmacc_username)) < 0 ||
+ new_password(dmacc_password, sizeof(dmacc_password)) < 0) {
+ debug_print(ctx, 1,
+ "Failed to generate DMAcc username/password");
+ return NULL;
+ }
+
+ status = "Provisioning complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (!spp_node)
+ return NULL;
+
+ policy = db_get_osu_config_val(ctx, realm, "sim_policy");
+ if (policy) {
+ policy_node = read_policy_file(ctx, policy);
+ os_free(policy);
+ if (!policy_node) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+ update_policy_update_uri(ctx, realm, policy_node);
+ node = get_node_uri(ctx->xml, policy_node,
+ "Policy/PolicyUpdate");
+ if (node)
+ build_username_password(ctx, node, dmacc_username,
+ dmacc_password);
+ }
+
+ pps = build_pps(ctx, NULL, realm, NULL, NULL, 0, NULL, ctx->imsi,
+ dmacc_username, dmacc_password, policy_node);
+ if (!pps) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+
+ debug_print(ctx, 1,
+ "Request DB subscription registration on success notification");
+ if (!user || !user[0])
+ user = ctx->imsi;
+ db_add_session(ctx, user, realm, session_id, NULL, NULL,
+ SUBSCRIPTION_REGISTRATION, NULL);
+ db_add_session_dmacc(ctx, session_id, dmacc_username, dmacc_password);
+ if (ctx->eap_method)
+ db_add_session_eap_method(ctx, session_id, ctx->eap_method);
+ if (ctx->id_hash)
+ db_add_session_id_hash(ctx, session_id, ctx->id_hash);
+ db_add_session_pps(ctx, user, realm, session_id, pps);
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "new subscription", pps);
+
+ tnds = mo_to_tnds(ctx->xml, pps, 0, URN_HS20_PPS, NULL);
+ xml_node_free(ctx->xml, pps);
+ if (!tnds) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+
+ str = xml_node_to_str(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (!str) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+
+ node = xml_node_create_text(ctx->xml, spp_node, ns, "addMO", str);
+ free(str);
+ snprintf(buf, sizeof(buf), "./Wi-Fi/%s/PerProviderSubscription", realm);
+ xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", buf);
+ xml_node_add_attr(ctx->xml, node, ns, "moURN", URN_HS20_PPS);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_spp_post_dev_data(struct hs20_svc *ctx,
+ xml_node_t *node,
+ const char *user,
+ const char *realm,
+ const char *session_id,
+ int dmacc)
+{
+ const char *req_reason;
+ char *redirect_uri = NULL;
+ char *req_reason_buf = NULL;
+ char str[200];
+ xml_node_t *ret = NULL, *devinfo = NULL, *devdetail = NULL;
+ xml_node_t *mo, *macaddr;
+ char *version;
+ int valid;
+ char *supp, *pos;
+ char *err;
+ u8 wifi_mac_addr[ETH_ALEN];
+
+ version = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
+ "sppVersion");
+ if (version == NULL || strstr(version, "1.0") == NULL) {
+ ret = build_post_dev_data_response(
+ ctx, NULL, session_id, "Error occurred",
+ "SPP version not supported");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Unsupported sppVersion", ret);
+ xml_node_get_attr_value_free(ctx->xml, version);
+ return ret;
+ }
+ xml_node_get_attr_value_free(ctx->xml, version);
+
+ mo = get_node(ctx->xml, node, "supportedMOList");
+ if (mo == NULL) {
+ ret = build_post_dev_data_response(
+ ctx, NULL, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "No supportedMOList element", ret);
+ return ret;
+ }
+ supp = xml_node_get_text(ctx->xml, mo);
+ for (pos = supp; pos && *pos; pos++)
+ *pos = tolower(*pos);
+ if (supp == NULL ||
+ strstr(supp, URN_OMA_DM_DEVINFO) == NULL ||
+ strstr(supp, URN_OMA_DM_DEVDETAIL) == NULL ||
+ strstr(supp, URN_HS20_PPS) == NULL) {
+ xml_node_get_text_free(ctx->xml, supp);
+ ret = build_post_dev_data_response(
+ ctx, NULL, session_id, "Error occurred",
+ "One or more mandatory MOs not supported");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Unsupported MOs", ret);
+ return ret;
+ }
+ xml_node_get_text_free(ctx->xml, supp);
+
+ req_reason_buf = xml_node_get_attr_value(ctx->xml, node,
+ "requestReason");
+ if (req_reason_buf == NULL) {
+ debug_print(ctx, 1, "No requestReason attribute");
+ return NULL;
+ }
+ req_reason = req_reason_buf;
+
+ redirect_uri = xml_node_get_attr_value(ctx->xml, node, "redirectURI");
+
+ debug_print(ctx, 1, "requestReason: %s sessionID: %s redirectURI: %s",
+ req_reason, session_id, redirect_uri);
+ snprintf(str, sizeof(str), "sppPostDevData: requestReason=%s",
+ req_reason);
+ hs20_eventlog(ctx, user, realm, session_id, str, NULL);
+
+ devinfo = spp_get_mo(ctx, node, URN_OMA_DM_DEVINFO, &valid, &err);
+ if (devinfo == NULL) {
+ ret = build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred", "Other");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "No DevInfo moContainer in sppPostDevData",
+ ret);
+ os_free(err);
+ goto out;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Received DevInfo MO", devinfo);
+ if (valid == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "OMA-DM DDF DTD validation errors in DevInfo MO",
+ err);
+ ret = build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred", "Other");
+ os_free(err);
+ goto out;
+ }
+ os_free(err);
+ if (user)
+ db_update_mo(ctx, user, realm, "devinfo", devinfo);
+
+ devdetail = spp_get_mo(ctx, node, URN_OMA_DM_DEVDETAIL, &valid, &err);
+ if (devdetail == NULL) {
+ ret = build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred", "Other");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "No DevDetail moContainer in sppPostDevData",
+ ret);
+ os_free(err);
+ goto out;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Received DevDetail MO", devdetail);
+ if (valid == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "OMA-DM DDF DTD validation errors "
+ "in DevDetail MO", err);
+ ret = build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred", "Other");
+ os_free(err);
+ goto out;
+ }
+ os_free(err);
+
+ os_memset(wifi_mac_addr, 0, ETH_ALEN);
+ macaddr = get_node(ctx->xml, devdetail,
+ "Ext/org.wi-fi/Wi-Fi/Wi-FiMACAddress");
+ if (macaddr) {
+ char *addr, buf[50];
+
+ addr = xml_node_get_text(ctx->xml, macaddr);
+ if (addr && hwaddr_compact_aton(addr, wifi_mac_addr) == 0) {
+ snprintf(buf, sizeof(buf), "DevDetail MAC address: "
+ MACSTR, MAC2STR(wifi_mac_addr));
+ hs20_eventlog(ctx, user, realm, session_id, buf, NULL);
+ xml_node_get_text_free(ctx->xml, addr);
+ } else {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "Could not extract MAC address from DevDetail",
+ NULL);
+ }
+ } else {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "No MAC address in DevDetail", NULL);
+ }
+
+ if (user)
+ db_update_mo(ctx, user, realm, "devdetail", devdetail);
+
+ if (user)
+ mo = spp_get_mo(ctx, node, URN_HS20_PPS, &valid, &err);
+ else {
+ mo = NULL;
+ err = NULL;
+ }
+ if (user && mo) {
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Received PPS MO", mo);
+ if (valid == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "OMA-DM DDF DTD validation errors "
+ "in PPS MO", err);
+ xml_node_get_attr_value_free(ctx->xml, redirect_uri);
+ os_free(err);
+ return build_post_dev_data_response(
+ ctx, NULL, session_id,
+ "Error occurred", "Other");
+ }
+ db_update_mo(ctx, user, realm, "pps", mo);
+ db_update_val(ctx, user, realm, "fetch_pps", "0", dmacc);
+ xml_node_free(ctx->xml, mo);
+ }
+ os_free(err);
+
+ if (user && !mo) {
+ char *fetch;
+ int fetch_pps;
+
+ fetch = db_get_val(ctx, user, realm, "fetch_pps", dmacc);
+ fetch_pps = fetch ? atoi(fetch) : 0;
+ free(fetch);
+
+ if (fetch_pps) {
+ enum hs20_session_operation oper;
+ if (strcasecmp(req_reason, "Subscription remediation")
+ == 0)
+ oper = CONTINUE_SUBSCRIPTION_REMEDIATION;
+ else if (strcasecmp(req_reason, "Policy update") == 0)
+ oper = CONTINUE_POLICY_UPDATE;
+ else
+ oper = NO_OPERATION;
+ if (db_add_session(ctx, user, realm, session_id, NULL,
+ NULL, oper, NULL) < 0)
+ goto out;
+
+ ret = spp_exec_upload_mo(ctx, session_id,
+ URN_HS20_PPS);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "request PPS MO upload",
+ ret);
+ goto out;
+ }
+ }
+
+ if (user && strcasecmp(req_reason, "MO upload") == 0) {
+ char *val = db_get_session_val(ctx, user, realm, session_id,
+ "operation");
+ enum hs20_session_operation oper;
+ if (!val) {
+ debug_print(ctx, 1, "No session %s found to continue",
+ session_id);
+ goto out;
+ }
+ oper = atoi(val);
+ free(val);
+ if (oper == CONTINUE_SUBSCRIPTION_REMEDIATION)
+ req_reason = "Subscription remediation";
+ else if (oper == CONTINUE_POLICY_UPDATE)
+ req_reason = "Policy update";
+ else {
+ debug_print(ctx, 1,
+ "No pending operation in session %s",
+ session_id);
+ goto out;
+ }
+ }
+
+ if (strcasecmp(req_reason, "Subscription registration") == 0) {
+ ret = hs20_subscription_registration(ctx, realm, session_id,
+ redirect_uri,
+ wifi_mac_addr);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "subscription registration response",
+ ret);
+ goto out;
+ }
+ if (user && strcasecmp(req_reason, "Subscription remediation") == 0) {
+ ret = hs20_subscription_remediation(ctx, user, realm,
+ session_id, dmacc,
+ redirect_uri);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "subscription remediation response",
+ ret);
+ goto out;
+ }
+ if (user && strcasecmp(req_reason, "Policy update") == 0) {
+ ret = hs20_policy_update(ctx, user, realm, session_id, dmacc);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "policy update response",
+ ret);
+ goto out;
+ }
+
+ if (strcasecmp(req_reason, "User input completed") == 0) {
+ db_add_session_devinfo(ctx, session_id, devinfo);
+ db_add_session_devdetail(ctx, session_id, devdetail);
+ ret = hs20_user_input_complete(ctx, user, realm, dmacc,
+ session_id);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "user input completed response", ret);
+ goto out;
+ }
+
+ if (strcasecmp(req_reason, "Certificate enrollment completed") == 0) {
+ ret = hs20_cert_enroll_completed(ctx, user, realm, dmacc,
+ session_id);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "certificate enrollment response", ret);
+ goto out;
+ }
+
+ if (strcasecmp(req_reason, "Certificate enrollment failed") == 0) {
+ ret = hs20_cert_enroll_failed(ctx, user, realm, dmacc,
+ session_id);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "certificate enrollment failed response",
+ ret);
+ goto out;
+ }
+
+ if (strcasecmp(req_reason, "Subscription provisioning") == 0) {
+ ret = hs20_sim_provisioning(ctx, user, realm, dmacc,
+ session_id);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "subscription provisioning response",
+ ret);
+ goto out;
+ }
+
+ debug_print(ctx, 1, "Unsupported requestReason '%s' user '%s'",
+ req_reason, user);
+out:
+ xml_node_get_attr_value_free(ctx->xml, req_reason_buf);
+ xml_node_get_attr_value_free(ctx->xml, redirect_uri);
+ if (devinfo)
+ xml_node_free(ctx->xml, devinfo);
+ if (devdetail)
+ xml_node_free(ctx->xml, devdetail);
+ return ret;
+}
+
+
+static xml_node_t * build_spp_exchange_complete(struct hs20_svc *ctx,
+ const char *session_id,
+ const char *status,
+ const char *error_code)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *node;
+
+ spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
+ "sppExchangeComplete");
+
+
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", status);
+
+ if (error_code) {
+ node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
+ xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
+ error_code);
+ }
+
+ return spp_node;
+}
+
+
+static int add_subscription(struct hs20_svc *ctx, const char *session_id)
+{
+ char *user, *realm, *pw, *pw_mm, *pps, *str;
+ char *osu_user, *osu_password, *eap_method;
+ char *policy = NULL;
+ char *sql;
+ int ret = -1;
+ char *free_account;
+ int free_acc;
+ char *type;
+ int cert = 0;
+ char *cert_pem, *fingerprint;
+ const char *method;
+
+ user = db_get_session_val(ctx, NULL, NULL, session_id, "user");
+ realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm");
+ pw = db_get_session_val(ctx, NULL, NULL, session_id, "password");
+ pw_mm = db_get_session_val(ctx, NULL, NULL, session_id,
+ "machine_managed");
+ pps = db_get_session_val(ctx, NULL, NULL, session_id, "pps");
+ cert_pem = db_get_session_val(ctx, NULL, NULL, session_id, "cert_pem");
+ fingerprint = db_get_session_val(ctx, NULL, NULL, session_id, "cert");
+ type = db_get_session_val(ctx, NULL, NULL, session_id, "type");
+ if (type && strcmp(type, "cert") == 0)
+ cert = 1;
+ free(type);
+ osu_user = db_get_session_val(ctx, NULL, NULL, session_id, "osu_user");
+ osu_password = db_get_session_val(ctx, NULL, NULL, session_id,
+ "osu_password");
+ eap_method = db_get_session_val(ctx, NULL, NULL, session_id,
+ "eap_method");
+
+ if (!user || !realm || !pw) {
+ debug_print(ctx, 1, "Could not find session info from DB for "
+ "the new subscription");
+ goto out;
+ }
+
+ free_account = db_get_osu_config_val(ctx, realm, "free_account");
+ free_acc = free_account && strcmp(free_account, user) == 0;
+ free(free_account);
+
+ policy = db_get_osu_config_val(ctx, realm, "sim_policy");
+
+ debug_print(ctx, 1,
+ "New subscription: user='%s' realm='%s' free_acc=%d",
+ user, realm, free_acc);
+ debug_print(ctx, 1, "New subscription: pps='%s'", pps);
+
+ sql = sqlite3_mprintf("UPDATE eventlog SET user=%Q, realm=%Q WHERE "
+ "sessionid=%Q AND (user='' OR user IS NULL)",
+ user, realm, session_id);
+ if (sql) {
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to update eventlog in "
+ "sqlite database: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+ }
+
+ if (free_acc) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "completed shared free account registration",
+ NULL);
+ ret = 0;
+ goto out;
+ }
+
+ str = db_get_session_val(ctx, NULL, NULL, session_id, "mac_addr");
+
+ if (eap_method && eap_method[0])
+ method = eap_method;
+ else
+ method = cert ? "TLS" : "TTLS-MSCHAPV2";
+ sql = sqlite3_mprintf("INSERT INTO users(identity,realm,phase2,methods,cert,cert_pem,machine_managed,mac_addr,osu_user,osu_password,policy) VALUES (%Q,%Q,%d,%Q,%Q,%Q,%d,%Q,%Q,%Q,%Q)",
+ user, realm, cert ? 0 : 1,
+ method,
+ fingerprint ? fingerprint : "",
+ cert_pem ? cert_pem : "",
+ pw_mm && atoi(pw_mm) ? 1 : 0,
+ str ? str : "",
+ osu_user ? osu_user : "",
+ osu_password ? osu_password : "",
+ policy ? policy : "");
+ free(str);
+ if (sql == NULL)
+ goto out;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add user in sqlite database: %s",
+ sqlite3_errmsg(ctx->db));
+ sqlite3_free(sql);
+ goto out;
+ }
+ sqlite3_free(sql);
+
+ if (cert)
+ ret = 0;
+ else
+ ret = update_password(ctx, user, realm, pw, 0);
+ if (ret < 0) {
+ sql = sqlite3_mprintf("DELETE FROM users WHERE identity=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')",
+ user, realm);
+ if (sql) {
+ debug_print(ctx, 1, "DB: %s", sql);
+ sqlite3_exec(ctx->db, sql, NULL, NULL, NULL);
+ sqlite3_free(sql);
+ }
+ }
+
+ if (pps)
+ db_update_mo_str(ctx, user, realm, "pps", pps);
+
+ str = db_get_session_val(ctx, NULL, NULL, session_id, "devinfo");
+ if (str) {
+ db_update_mo_str(ctx, user, realm, "devinfo", str);
+ free(str);
+ }
+
+ str = db_get_session_val(ctx, NULL, NULL, session_id, "devdetail");
+ if (str) {
+ db_update_mo_str(ctx, user, realm, "devdetail", str);
+ free(str);
+ }
+
+ if (cert && user) {
+ const char *serialnum;
+
+ str = db_get_session_val(ctx, NULL, NULL, session_id,
+ "mac_addr");
+
+ if (os_strncmp(user, "cert-", 5) == 0)
+ serialnum = user + 5;
+ else
+ serialnum = "";
+ sql = sqlite3_mprintf("INSERT OR REPLACE INTO cert_enroll (mac_addr,user,realm,serialnum) VALUES(%Q,%Q,%Q,%Q)",
+ str ? str : "", user, realm ? realm : "",
+ serialnum);
+ free(str);
+ if (sql) {
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) !=
+ SQLITE_OK) {
+ debug_print(ctx, 1,
+ "Failed to add cert_enroll entry into sqlite database: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+ }
+ }
+
+ str = db_get_session_val(ctx, NULL, NULL, session_id,
+ "mobile_identifier_hash");
+ if (str) {
+ sql = sqlite3_mprintf("DELETE FROM sim_provisioning WHERE mobile_identifier_hash=%Q",
+ str);
+ if (sql) {
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) !=
+ SQLITE_OK) {
+ debug_print(ctx, 1,
+ "Failed to delete pending sim_provisioning entry: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+ }
+ os_free(str);
+ }
+
+ if (ret == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "completed subscription registration", NULL);
+ }
+
+out:
+ free(user);
+ free(realm);
+ free(pw);
+ free(pw_mm);
+ free(pps);
+ free(cert_pem);
+ free(fingerprint);
+ free(osu_user);
+ free(osu_password);
+ free(eap_method);
+ os_free(policy);
+ return ret;
+}
+
+
+static xml_node_t * hs20_spp_update_response(struct hs20_svc *ctx,
+ xml_node_t *node,
+ const char *user,
+ const char *realm,
+ const char *session_id,
+ int dmacc)
+{
+ char *status;
+ xml_node_t *ret;
+ char *val;
+ enum hs20_session_operation oper;
+
+ status = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
+ "sppStatus");
+ if (status == NULL) {
+ debug_print(ctx, 1, "No sppStatus attribute");
+ return NULL;
+ }
+
+ debug_print(ctx, 1, "sppUpdateResponse: sppStatus: %s sessionID: %s",
+ status, session_id);
+
+ val = db_get_session_val(ctx, NULL, NULL, session_id, "operation");
+ if (!val) {
+ debug_print(ctx, 1,
+ "No session active for sessionID: %s",
+ session_id);
+ oper = NO_OPERATION;
+ } else
+ oper = atoi(val);
+
+ if (strcasecmp(status, "OK") == 0) {
+ char *new_pw = NULL;
+
+ xml_node_get_attr_value_free(ctx->xml, status);
+
+ if (oper == USER_REMEDIATION) {
+ new_pw = db_get_session_val(ctx, user, realm,
+ session_id, "password");
+ if (new_pw == NULL || strlen(new_pw) == 0) {
+ free(new_pw);
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id, "No password "
+ "had been assigned for "
+ "session", ret);
+ db_remove_session(ctx, user, realm, session_id);
+ return ret;
+ }
+ oper = UPDATE_PASSWORD;
+ }
+ if (oper == UPDATE_PASSWORD) {
+ if (!new_pw) {
+ new_pw = db_get_session_val(ctx, user, realm,
+ session_id,
+ "password");
+ if (!new_pw) {
+ db_remove_session(ctx, user, realm,
+ session_id);
+ return NULL;
+ }
+ }
+ debug_print(ctx, 1, "Update user '%s' password in DB",
+ user);
+ if (update_password(ctx, user, realm, new_pw, dmacc) <
+ 0) {
+ debug_print(ctx, 1, "Failed to update user "
+ "'%s' password in DB", user);
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id, "Failed to "
+ "update database", ret);
+ db_remove_session(ctx, user, realm, session_id);
+ return ret;
+ }
+ hs20_eventlog(ctx, user, realm,
+ session_id, "Updated user password "
+ "in database", NULL);
+ }
+ if (oper == CLEAR_REMEDIATION) {
+ debug_print(ctx, 1,
+ "Clear remediation requirement for user '%s' in DB",
+ user);
+ if (clear_remediation(ctx, user, realm, dmacc) < 0) {
+ debug_print(ctx, 1,
+ "Failed to clear remediation requirement for user '%s' in DB",
+ user);
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id,
+ "Failed to update database",
+ ret);
+ db_remove_session(ctx, user, realm, session_id);
+ return ret;
+ }
+ hs20_eventlog(ctx, user, realm,
+ session_id,
+ "Cleared remediation requirement in database",
+ NULL);
+ }
+ if (oper == SUBSCRIPTION_REGISTRATION) {
+ if (add_subscription(ctx, session_id) < 0) {
+ debug_print(ctx, 1, "Failed to add "
+ "subscription into DB");
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id, "Failed to "
+ "update database", ret);
+ db_remove_session(ctx, user, realm, session_id);
+ return ret;
+ }
+ }
+ if (oper == POLICY_REMEDIATION || oper == POLICY_UPDATE) {
+ char *val;
+ val = db_get_val(ctx, user, realm, "remediation",
+ dmacc);
+ if (val && strcmp(val, "policy") == 0)
+ db_update_val(ctx, user, realm, "remediation",
+ "", dmacc);
+ free(val);
+ }
+ if (oper == POLICY_UPDATE)
+ db_update_val(ctx, user, realm, "polupd_done", "1",
+ dmacc);
+ if (oper == CERT_REENROLL) {
+ char *new_user;
+ char event[200];
+
+ new_user = db_get_session_val(ctx, NULL, NULL,
+ session_id, "user");
+ if (!new_user) {
+ debug_print(ctx, 1,
+ "Failed to find new user name (cert-serialnum)");
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id,
+ "Failed to find new user name (cert reenroll)",
+ ret);
+ db_remove_session(ctx, NULL, NULL, session_id);
+ return ret;
+ }
+
+ debug_print(ctx, 1,
+ "Update certificate user entry to use the new serial number (old=%s new=%s)",
+ user, new_user);
+ os_snprintf(event, sizeof(event), "renamed user to: %s",
+ new_user);
+ hs20_eventlog(ctx, user, realm, session_id, event,
+ NULL);
+
+ if (db_update_val(ctx, user, realm, "identity",
+ new_user, 0) < 0 ||
+ db_update_val(ctx, new_user, realm, "remediation",
+ "", 0) < 0) {
+ debug_print(ctx, 1,
+ "Failed to update user name (cert-serialnum)");
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id,
+ "Failed to update user name (cert reenroll)",
+ ret);
+ db_remove_session(ctx, NULL, NULL, session_id);
+ os_free(new_user);
+ return ret;
+ }
+
+ os_free(new_user);
+ }
+ ret = build_spp_exchange_complete(
+ ctx, session_id,
+ "Exchange complete, release TLS connection", NULL);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Exchange completed", ret);
+ db_remove_session(ctx, NULL, NULL, session_id);
+ return ret;
+ }
+
+ ret = build_spp_exchange_complete(ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm, session_id, "Error occurred", ret);
+ db_remove_session(ctx, user, realm, session_id);
+ xml_node_get_attr_value_free(ctx->xml, status);
+ return ret;
+}
+
+
+#define SPP_SESSION_ID_LEN 16
+
+static char * gen_spp_session_id(void)
+{
+ FILE *f;
+ int i;
+ char *session;
+
+ session = os_malloc(SPP_SESSION_ID_LEN * 2 + 1);
+ if (session == NULL)
+ return NULL;
+
+ f = fopen("/dev/urandom", "r");
+ if (f == NULL) {
+ os_free(session);
+ return NULL;
+ }
+ for (i = 0; i < SPP_SESSION_ID_LEN; i++)
+ os_snprintf(session + i * 2, 3, "%02x", fgetc(f));
+
+ fclose(f);
+ return session;
+}
+
+xml_node_t * hs20_spp_server_process(struct hs20_svc *ctx, xml_node_t *node,
+ const char *auth_user,
+ const char *auth_realm, int dmacc)
+{
+ xml_node_t *ret = NULL;
+ char *session_id;
+ const char *op_name;
+ char *xml_err;
+ char fname[200];
+
+ debug_dump_node(ctx, "received request", node);
+
+ if (!dmacc && auth_user && auth_realm) {
+ char *real;
+ real = db_get_val(ctx, auth_user, auth_realm, "identity", 0);
+ if (!real) {
+ real = db_get_val(ctx, auth_user, auth_realm,
+ "identity", 1);
+ if (real)
+ dmacc = 1;
+ }
+ os_free(real);
+ }
+
+ snprintf(fname, sizeof(fname), "%s/spp/spp.xsd", ctx->root_dir);
+ if (xml_validate(ctx->xml, node, fname, &xml_err) < 0) {
+ /*
+ * We may not be able to extract the sessionID from invalid
+ * input, but well, we can try.
+ */
+ session_id = xml_node_get_attr_value_ns(ctx->xml, node,
+ SPP_NS_URI,
+ "sessionID");
+ debug_print(ctx, 1,
+ "SPP message failed validation, xsd file: %s xml-error: %s",
+ fname, xml_err);
+ hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+ "SPP message failed validation", node);
+ hs20_eventlog(ctx, auth_user, auth_realm, session_id,
+ "Validation errors", xml_err);
+ os_free(xml_err);
+ xml_node_get_attr_value_free(ctx->xml, session_id);
+ /* TODO: what to return here? */
+ ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+ "SppValidationError");
+ return ret;
+ }
+
+ session_id = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
+ "sessionID");
+ if (session_id) {
+ char *tmp;
+ debug_print(ctx, 1, "Received sessionID %s", session_id);
+ tmp = os_strdup(session_id);
+ xml_node_get_attr_value_free(ctx->xml, session_id);
+ if (tmp == NULL)
+ return NULL;
+ session_id = tmp;
+ } else {
+ session_id = gen_spp_session_id();
+ if (session_id == NULL) {
+ debug_print(ctx, 1, "Failed to generate sessionID");
+ return NULL;
+ }
+ debug_print(ctx, 1, "Generated sessionID %s", session_id);
+ }
+
+ op_name = xml_node_get_localname(ctx->xml, node);
+ if (op_name == NULL) {
+ debug_print(ctx, 1, "Could not get op_name");
+ return NULL;
+ }
+
+ if (strcmp(op_name, "sppPostDevData") == 0) {
+ hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+ "sppPostDevData received and validated",
+ node);
+ ret = hs20_spp_post_dev_data(ctx, node, auth_user, auth_realm,
+ session_id, dmacc);
+ } else if (strcmp(op_name, "sppUpdateResponse") == 0) {
+ hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+ "sppUpdateResponse received and validated",
+ node);
+ ret = hs20_spp_update_response(ctx, node, auth_user,
+ auth_realm, session_id, dmacc);
+ } else {
+ hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+ "Unsupported SPP message received and "
+ "validated", node);
+ debug_print(ctx, 1, "Unsupported operation '%s'", op_name);
+ /* TODO: what to return here? */
+ ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+ "SppUnknownCommandError");
+ }
+ os_free(session_id);
+
+ if (ret == NULL) {
+ /* TODO: what to return here? */
+ ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+ "SppInternalError");
+ }
+
+ return ret;
+}
+
+
+int hs20_spp_server_init(struct hs20_svc *ctx)
+{
+ char fname[200];
+ ctx->db = NULL;
+ snprintf(fname, sizeof(fname), "%s/AS/DB/eap_user.db", ctx->root_dir);
+ if (sqlite3_open(fname, &ctx->db)) {
+ printf("Failed to open sqlite database: %s\n",
+ sqlite3_errmsg(ctx->db));
+ sqlite3_close(ctx->db);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void hs20_spp_server_deinit(struct hs20_svc *ctx)
+{
+ sqlite3_close(ctx->db);
+ ctx->db = NULL;
+}
diff --git a/contrib/wpa/hs20/server/spp_server.h b/contrib/wpa/hs20/server/spp_server.h
new file mode 100644
index 00000000000..421974c607b
--- /dev/null
+++ b/contrib/wpa/hs20/server/spp_server.h
@@ -0,0 +1,36 @@
+/*
+ * Hotspot 2.0 SPP server
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SPP_SERVER_H
+#define SPP_SERVER_H
+
+struct hs20_svc {
+ const void *ctx;
+ struct xml_node_ctx *xml;
+ char *root_dir;
+ FILE *debug_log;
+ sqlite3 *db;
+ const char *addr;
+ const char *test;
+ const char *imsi;
+ const char *eap_method;
+ const char *id_hash;
+};
+
+
+void debug_print(struct hs20_svc *ctx, int print, const char *fmt, ...)
+ __attribute__ ((format (printf, 3, 4)));
+void debug_dump_node(struct hs20_svc *ctx, const char *title, xml_node_t *node);
+
+xml_node_t * hs20_spp_server_process(struct hs20_svc *ctx, xml_node_t *node,
+ const char *auth_user,
+ const char *auth_realm, int dmacc);
+int hs20_spp_server_init(struct hs20_svc *ctx);
+void hs20_spp_server_deinit(struct hs20_svc *ctx);
+
+#endif /* SPP_SERVER_H */
diff --git a/contrib/wpa/hs20/server/sql-example.txt b/contrib/wpa/hs20/server/sql-example.txt
new file mode 100644
index 00000000000..20dcf2f5c68
--- /dev/null
+++ b/contrib/wpa/hs20/server/sql-example.txt
@@ -0,0 +1,17 @@
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','fqdn','example.com');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','friendly_name','Example Operator');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','spp_http_auth_url','https://subscription-server.osu.example.com/hs20/spp.php?realm=example.com');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','trust_root_cert_url','https://osu-server.osu.example.com/hs20/files/spp-root-ca.der');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','trust_root_cert_fingerprint','5b393a9246865569485c2605c3304e48212b449367858299beba9384c4cf4647');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','aaa_trust_root_cert_url','https://osu-server.osu.example.com/hs20/files/aaa-root-ca.der');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','aaa_trust_root_cert_fingerprint','5b393a9246865569485c2605c3304e48212b449367858299beba9384c4cf4647');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','free_account','free');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','policy_url','https://subscription-server.osu.example.com/hs20/spp.php?realm=example.com');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','remediation_url','https://subscription-server.osu.example.com/hs20/remediation.php?session_id=');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','free_remediation_url','https://subscription-server.osu.example.com/hs20/free-remediation.php?session_id=');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','signup_url','https://subscription-server.osu.example.com/hs20/signup.php?session_id=');
+
+
+INSERT INTO users(identity,realm,methods,password,phase2,shared) VALUES('free','example.com','TTLS-MSCHAPV2','free',1,1);
+
+INSERT INTO wildcards(identity,methods) VALUES('','TTLS,TLS');
diff --git a/contrib/wpa/hs20/server/sql.txt b/contrib/wpa/hs20/server/sql.txt
new file mode 100644
index 00000000000..2cc6edea406
--- /dev/null
+++ b/contrib/wpa/hs20/server/sql.txt
@@ -0,0 +1,108 @@
+CREATE TABLE eventlog(
+ user TEXT,
+ realm TEXT,
+ sessionid TEXT COLLATE NOCASE,
+ timestamp TEXT,
+ notes TEXT,
+ dump TEXT,
+ addr TEXT
+);
+
+CREATE TABLE sessions(
+ timestamp TEXT,
+ id TEXT COLLATE NOCASE,
+ user TEXT,
+ realm TEXT,
+ password TEXT,
+ machine_managed BOOLEAN,
+ operation INTEGER,
+ type TEXT,
+ pps TEXT,
+ redirect_uri TEXT,
+ devinfo TEXT,
+ devdetail TEXT,
+ cert TEXT,
+ cert_pem TEXT,
+ mac_addr TEXT,
+ osu_user TEXT,
+ osu_password TEXT,
+ eap_method TEXT,
+ mobile_identifier_hash TEXT,
+ test TEXT
+);
+
+CREATE index sessions_id_index ON sessions(id);
+
+CREATE TABLE osu_config(
+ realm TEXT,
+ field TEXT,
+ value TEXT
+);
+
+CREATE TABLE users(
+ identity TEXT PRIMARY KEY,
+ methods TEXT,
+ password TEXT,
+ machine_managed BOOLEAN,
+ remediation TEXT,
+ phase2 INTEGER,
+ realm TEXT,
+ policy TEXT,
+ devinfo TEXT,
+ devdetail TEXT,
+ pps TEXT,
+ fetch_pps INTEGER,
+ osu_user TEXT,
+ osu_password TEXT,
+ shared INTEGER,
+ cert TEXT,
+ cert_pem TEXT,
+ t_c_timestamp INTEGER,
+ mac_addr TEXT,
+ last_msk TEXT,
+ polupd_done TEXT,
+ subrem TEXT
+);
+
+CREATE TABLE wildcards(
+ identity TEXT PRIMARY KEY,
+ methods TEXT
+);
+
+CREATE TABLE authlog(
+ timestamp TEXT,
+ session TEXT,
+ nas_ip TEXT,
+ 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
+);
+
+CREATE TABLE cert_enroll(
+ mac_addr TEXT PRIMARY KEY,
+ user TEXT,
+ realm TEXT,
+ serialnum TEXT
+);
+
+CREATE TABLE sim_provisioning(
+ mobile_identifier_hash TEXT PRIMARY KEY,
+ imsi TEXT,
+ mac_addr TEXT,
+ eap_method TEXT,
+ timestamp TEXT
+);
diff --git a/contrib/wpa/hs20/server/www/add-free.php b/contrib/wpa/hs20/server/www/add-free.php
new file mode 100644
index 00000000000..1efc6556327
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/add-free.php
@@ -0,0 +1,50 @@
+query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+ die("Session not found");
+}
+
+$uri = $row['redirect_uri'];
+$rowid = $row['rowid'];
+$realm = $row['realm'];
+
+$row = $db->query("SELECT value FROM osu_config WHERE realm='$realm' AND field='free_account'")->fetch();
+if (!$row || strlen($row['value']) == 0) {
+ die("Free account disabled");
+}
+
+$user = $row['value'];
+
+$row = $db->query("SELECT password FROM users WHERE identity='$user' AND realm='$realm'")->fetch();
+if (!$row)
+ die("Free account not found");
+
+$pw = $row['password'];
+
+if (!$db->exec("UPDATE sessions SET user='$user', password='$pw', realm='$realm', machine_managed='1' WHERE rowid=$rowid")) {
+ die("Failed to update session database");
+}
+
+$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
+ "VALUES ('$user', '$realm', '$id', " .
+ "strftime('%Y-%m-%d %H:%M:%f','now'), " .
+ "'completed user input response for a new PPS MO')");
+
+header("Location: $uri", true, 302);
+
+?>
diff --git a/contrib/wpa/hs20/server/www/add-mo.php b/contrib/wpa/hs20/server/www/add-mo.php
new file mode 100644
index 00000000000..a3b4513531f
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/add-mo.php
@@ -0,0 +1,56 @@
+Invalid username
\n";
+ echo "Try again\n";
+ echo "