diff --git a/hostapd/Android.mk b/hostapd/Android.mk new file mode 100644 index 000000000000..573564d5b0de --- /dev/null +++ b/hostapd/Android.mk @@ -0,0 +1,1181 @@ +# 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/common/ptksa_cache.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 + +NEED_AES_UNWRAP=y + +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 +NEED_AES_SIV=y +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_IEEE80211BE +CONFIG_IEEE80211AX=y +L_CFLAGS += -DCONFIG_IEEE80211BE +OBJS += src/ap/ieee802_11_eht.c +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 +ifdef CONFIG_DPP3 +L_CFLAGS += -DCONFIG_DPP3 +endif +endif + +ifdef CONFIG_NAN_USD +OBJS += src/common/nan_de.c +OBJS += src/ap/nan_usd_ap.c +L_CFLAGS += -DCONFIG_NAN_USD +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 +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 CONFIG_RADIUS_TLS +TLS_FUNCS=y +L_CFLAGS += -DCONFIG_RADIUS_TLS +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) +L_CFLAGS += -DCRYPTO_RSA_OAEP_SHA256 +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 +ifneq ($(CONFIG_TLS), openssl) +AESOBJS += src/crypto/aes-omac1.c +endif +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/comeback_token.c +OBJS += src/pasn/pasn_responder.c +OBJS += src/pasn/pasn_common.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/hostapd/ChangeLog b/hostapd/ChangeLog new file mode 100644 index 000000000000..1c8240d333c4 --- /dev/null +++ b/hostapd/ChangeLog @@ -0,0 +1,1358 @@ +ChangeLog for hostapd + +2024-07-20 - v2.11 + * Wi-Fi Easy Connect + - add support for DPP release 3 + - allow Configurator parameters to be provided during config exchange + * HE/IEEE 802.11ax/Wi-Fi 6 + - various fixes + * EHT/IEEE 802.11be/Wi-Fi 7 + - add preliminary support + * SAE: add support for fetching the password from a RADIUS server + * support OpenSSL 3.0 API changes + * support background radar detection and CAC with some additional + drivers + * support RADIUS ACL/PSK check during 4-way handshake (wpa_psk_radius=3) + * EAP-SIM/AKA: support IMSI privacy + * improve 4-way handshake operations + - use Secure=1 in message 3 during PTK rekeying + * OCV: do not check Frequency Segment 1 Channel Number for 160 MHz cases + to avoid interoperability issues + * support new SAE AKM suites with variable length keys + * support new AKM for 802.1X/EAP with SHA384 + * extend PASN support for secure ranging + * FT: Use SHA256 to derive PMKID for AKM 00-0F-AC:3 (FT-EAP) + - this is based on additional details being added in the IEEE 802.11 + standard + - the new implementation is not backwards compatible + * improved ACS to cover additional channel types/bandwidths + * extended Multiple BSSID support + * fix beacon protection with FT protocol (incorrect BIGTK was provided) + * support unsynchronized service discovery (USD) + * add preliminary support for RADIUS/TLS + * add support for explicit SSID protection in 4-way handshake + (a mitigation for CVE-2023-52424; disabled by default for now, can be + enabled with ssid_protection=1) + * fix SAE H2E rejected groups validation to avoid downgrade attacks + * use stricter validation for some RADIUS messages + * a large number of other fixes, cleanup, and extensions + +2022-01-16 - v2.10 + * SAE changes + - improved protection against side channel attacks + [https://w1.fi/security/2022-1/] + - added option send SAE Confirm immediately (sae_config_immediate=1) + after SAE Commit + - added support for the hash-to-element mechanism (sae_pwe=1 or + sae_pwe=2) + - fixed PMKSA caching with OKC + - added support for SAE-PK + * EAP-pwd changes + - improved protection against side channel attacks + [https://w1.fi/security/2022-1/] + * fixed WPS UPnP SUBSCRIBE handling of invalid operations + [https://w1.fi/security/2020-1/] + * fixed PMF disconnection protection bypass + [https://w1.fi/security/2019-7/] + * added support for using OpenSSL 3.0 + * fixed various issues in experimental support for EAP-TEAP server + * added configuration (max_auth_rounds, max_auth_rounds_short) to + increase the maximum number of EAP message exchanges (mainly to + support cases with very large certificates) for the EAP server + * added support for DPP release 2 (Wi-Fi Device Provisioning Protocol) + * extended HE (IEEE 802.11ax) support, including 6 GHz support + * removed obsolete IAPP functionality + * fixed EAP-FAST server with TLS GCM/CCM ciphers + * dropped support for libnl 1.1 + * added support for nl80211 control port for EAPOL frame TX/RX + * fixed OWE key derivation with groups 20 and 21; this breaks backwards + compatibility for these groups while the default group 19 remains + backwards compatible; owe_ptk_workaround=1 can be used to enabled a + a workaround for the group 20/21 backwards compatibility + * added support for Beacon protection + * added support for Extended Key ID for pairwise keys + * removed WEP support from the default build (CONFIG_WEP=y can be used + to enable it, if really needed) + * added a build option to remove TKIP support (CONFIG_NO_TKIP=y) + * added support for Transition Disable mechanism to allow the AP to + automatically disable transition mode to improve security + * added support for PASN + * added EAP-TLS server support for TLS 1.3 (disabled by default for now) + * a large number of other fixes, cleanup, and extensions + +2019-08-07 - v2.9 + * SAE changes + - disable use of groups using Brainpool curves + - improved protection against side channel attacks + [https://w1.fi/security/2019-6/] + * EAP-pwd changes + - disable use of groups using Brainpool curves + - improved protection against side channel attacks + [https://w1.fi/security/2019-6/] + * fixed FT-EAP initial mobility domain association using PMKSA caching + * added configuration of airtime policy + * fixed FILS to and RSNE into (Re)Association Response frames + * fixed DPP bootstrapping URI parser of channel list + * added support for regulatory WMM limitation (for ETSI) + * added support for MACsec Key Agreement using IEEE 802.1X/PSK + * added experimental support for EAP-TEAP server (RFC 7170) + * added experimental support for EAP-TLS server with TLS v1.3 + * added support for two server certificates/keys (RSA/ECC) + * added AKMSuiteSelector into "STA " control interface data to + determine with AKM was used for an association + * added eap_sim_id parameter to allow EAP-SIM/AKA server pseudonym and + fast reauthentication use to be disabled + * fixed an ECDH operation corner case with OpenSSL + +2019-04-21 - v2.8 + * SAE changes + - added support for SAE Password Identifier + - changed default configuration to enable only group 19 + (i.e., disable groups 20, 21, 25, 26 from default configuration) and + disable all unsuitable groups completely based on REVmd changes + - improved anti-clogging token mechanism and SAE authentication + frame processing during heavy CPU load; this mitigates some issues + with potential DoS attacks trying to flood an AP with large number + of SAE messages + - added Finite Cyclic Group field in status code 77 responses + - reject use of unsuitable groups based on new implementation guidance + in REVmd (allow only FFC groups with prime >= 3072 bits and ECC + groups with prime >= 256) + - minimize timing and memory use differences in PWE derivation + [https://w1.fi/security/2019-1/] (CVE-2019-9494) + - fixed confirm message validation in error cases + [https://w1.fi/security/2019-3/] (CVE-2019-9496) + * EAP-pwd changes + - minimize timing and memory use differences in PWE derivation + [https://w1.fi/security/2019-2/] (CVE-2019-9495) + - verify peer scalar/element + [https://w1.fi/security/2019-4/] (CVE-2019-9497 and CVE-2019-9498) + - fix message reassembly issue with unexpected fragment + [https://w1.fi/security/2019-5/] + - enforce rand,mask generation rules more strictly + - fix a memory leak in PWE derivation + - disallow ECC groups with a prime under 256 bits (groups 25, 26, and + 27) + * Hotspot 2.0 changes + - added support for release number 3 + - reject release 2 or newer association without PMF + * added support for RSN operating channel validation + (CONFIG_OCV=y and configuration parameter ocv=1) + * added Multi-AP protocol support + * added FTM responder configuration + * fixed build with LibreSSL + * added FT/RRB workaround for short Ethernet frame padding + * fixed KEK2 derivation for FILS+FT + * added RSSI-based association rejection from OCE + * extended beacon reporting functionality + * VLAN changes + - allow local VLAN management with remote RADIUS authentication + - add WPA/WPA2 passphrase/PSK -based VLAN assignment + * OpenSSL: allow systemwide policies to be overridden + * extended PEAP to derive EMSK to enable use with ERP/FILS + * extended WPS to allow SAE configuration to be added automatically + for PSK (wps_cred_add_sae=1) + * fixed FT and SA Query Action frame with AP-MLME-in-driver cases + * OWE: allow Diffie-Hellman Parameter element to be included with DPP + in preparation for DPP protocol extension + * RADIUS server: started to accept ERP keyName-NAI as user identity + automatically without matching EAP database entry + * fixed PTK rekeying with FILS and FT + +2018-12-02 - v2.7 + * fixed WPA packet number reuse with replayed messages and key + reinstallation + [http://w1.fi/security/2017-1/] (CVE-2017-13082) + * added support for FILS (IEEE 802.11ai) shared key authentication + * added support for OWE (Opportunistic Wireless Encryption, RFC 8110; + and transition mode defined by WFA) + * added support for DPP (Wi-Fi Device Provisioning Protocol) + * FT: + - added local generation of PMK-R0/PMK-R1 for FT-PSK + (ft_psk_generate_local=1) + - replaced inter-AP protocol with a cleaner design that is more + easily extensible; this breaks backward compatibility and requires + all APs in the ESS to be updated at the same time to maintain FT + functionality + - added support for wildcard R0KH/R1KH + - replaced r0_key_lifetime (minutes) parameter with + ft_r0_key_lifetime (seconds) + - fixed wpa_psk_file use for FT-PSK + - fixed FT-SAE PMKID matching + - added expiration to PMK-R0 and PMK-R1 cache + - added IEEE VLAN support (including tagged VLANs) + - added support for SHA384 based AKM + * SAE + - fixed some PMKSA caching cases with SAE + - added support for configuring SAE password separately of the + WPA2 PSK/passphrase + - added option to require MFP for SAE associations + (sae_require_pmf=1) + - fixed PTK and EAPOL-Key integrity and key-wrap algorithm selection + for SAE; + note: this is not backwards compatible, i.e., both the AP and + station side implementations will need to be update at the same + time to maintain interoperability + - added support for Password Identifier + * hostapd_cli: added support for command history and completion + * added support for requesting beacon report + * large number of other fixes, cleanup, and extensions + * added option to configure EAPOL-Key retry limits + (wpa_group_update_count and wpa_pairwise_update_count) + * removed all PeerKey functionality + * fixed nl80211 AP mode configuration regression with Linux 4.15 and + newer + * added support for using wolfSSL cryptographic library + * fixed some 20/40 MHz coexistence cases where the BSS could drop to + 20 MHz even when 40 MHz would be allowed + * Hotspot 2.0 + - added support for setting Venue URL ANQP-element (venue_url) + - added support for advertising Hotspot 2.0 operator icons + - added support for Roaming Consortium Selection element + - added support for Terms and Conditions + - added support for OSEN connection in a shared RSN BSS + * added support for using OpenSSL 1.1.1 + * added EAP-pwd server support for salted passwords + +2016-10-02 - v2.6 + * fixed EAP-pwd last fragment validation + [http://w1.fi/security/2015-7/] (CVE-2015-5314) + * fixed WPS configuration update vulnerability with malformed passphrase + [http://w1.fi/security/2016-1/] (CVE-2016-4476) + * extended channel switch support for VHT bandwidth changes + * added support for configuring new ANQP-elements with + anqp_elem=: + * fixed Suite B 192-bit AKM to use proper PMK length + (note: this makes old releases incompatible with the fixed behavior) + * added no_probe_resp_if_max_sta=1 parameter to disable Probe Response + frame sending for not-associated STAs if max_num_sta limit has been + reached + * added option (-S as command line argument) to request all interfaces + to be started at the same time + * modified rts_threshold and fragm_threshold configuration parameters + to allow -1 to be used to disable RTS/fragmentation + * EAP-pwd: added support for Brainpool Elliptic Curves + (with OpenSSL 1.0.2 and newer) + * fixed EAPOL reauthentication after FT protocol run + * fixed FTIE generation for 4-way handshake after FT protocol run + * fixed and improved various FST operations + * TLS server + - support SHA384 and SHA512 hashes + - support TLS v1.2 signature algorithm with SHA384 and SHA512 + - support PKCS #5 v2.0 PBES2 + - support PKCS #5 with PKCS #12 style key decryption + - minimal support for PKCS #12 + - support OCSP stapling (including ocsp_multi) + * added support for OpenSSL 1.1 API changes + - drop support for OpenSSL 0.9.8 + - drop support for OpenSSL 1.0.0 + * EAP-PEAP: support fast-connect crypto binding + * RADIUS + - fix Called-Station-Id to not escape SSID + - add Event-Timestamp to all Accounting-Request packets + - add Acct-Session-Id to Accounting-On/Off + - add Acct-Multi-Session-Id ton Access-Request packets + - add Service-Type (= Frames) + - allow server to provide PSK instead of passphrase for WPA-PSK + Tunnel_password case + - update full message for interim accounting updates + - add Acct-Delay-Time into Accounting messages + - add require_message_authenticator configuration option to require + CoA/Disconnect-Request packets to be authenticated + * started to postpone WNM-Notification frame sending by 100 ms so that + the STA has some more time to configure the key before this frame is + received after the 4-way handshake + * VHT: added interoperability workaround for 80+80 and 160 MHz channels + * extended VLAN support (per-STA vif, etc.) + * fixed PMKID derivation with SAE + * nl80211 + - added support for full station state operations + - fix IEEE 802.1X/WEP EAP reauthentication and rekeying to use + unencrypted EAPOL frames + * added initial MBO support; number of extensions to WNM BSS Transition + Management + * added initial functionality for location related operations + * added assocresp_elements parameter to allow vendor specific elements + to be added into (Re)Association Response frames + * improved Public Action frame addressing + - use Address 3 = wildcard BSSID in GAS response if a query from an + unassociated STA used that address + - fix TX status processing for Address 3 = wildcard BSSID + - add gas_address3 configuration parameter to control Address 3 + behavior + * added command line parameter -i to override interface parameter in + hostapd.conf + * added command completion support to hostapd_cli + * added passive client taxonomy determination (CONFIG_TAXONOMY=y + compile option and "SIGNATURE " control interface command) + * number of small fixes + +2015-09-27 - v2.5 + * fixed WPS UPnP vulnerability with HTTP chunked transfer encoding + [http://w1.fi/security/2015-2/] (CVE-2015-4141) + * fixed WMM Action frame parser + [http://w1.fi/security/2015-3/] (CVE-2015-4142) + * fixed EAP-pwd server missing payload length validation + [http://w1.fi/security/2015-4/] + (CVE-2015-4143, CVE-2015-4144, CVE-2015-4145) + * fixed validation of WPS and P2P NFC NDEF record payload length + [http://w1.fi/security/2015-5/] + * nl80211: + - fixed vendor command handling to check OUI properly + * fixed hlr_auc_gw build with OpenSSL + * hlr_auc_gw: allow Milenage RES length to be reduced + * disable HT for a station that does not support WMM/QoS + * added support for hashed password (NtHash) in EAP-pwd server + * fixed and extended dynamic VLAN cases + * added EAP-EKE server support for deriving Session-Id + * set Acct-Session-Id to a random value to make it more likely to be + unique even if the device does not have a proper clock + * added more 2.4 GHz channels for 20/40 MHz HT co-ex scan + * modified SAE routines to be more robust and PWE generation to be + stronger against timing attacks + * added support for Brainpool Elliptic Curves with SAE + * increases maximum value accepted for cwmin/cwmax + * added support for CCMP-256 and GCMP-256 as group ciphers with FT + * added Fast Session Transfer (FST) module + * removed optional fields from RSNE when using FT with PMF + (workaround for interoperability issues with iOS 8.4) + * added EAP server support for TLS session resumption + * fixed key derivation for Suite B 192-bit AKM (this breaks + compatibility with the earlier version) + * added mechanism to track unconnected stations and do minimal band + steering + * number of small fixes + +2015-03-15 - v2.4 + * allow OpenSSL cipher configuration to be set for internal EAP server + (openssl_ciphers parameter) + * fixed number of small issues based on hwsim test case failures and + static analyzer reports + * fixed Accounting-Request to not include duplicated Acct-Session-Id + * add support for Acct-Multi-Session-Id in RADIUS Accounting messages + * add support for PMKSA caching with SAE + * add support for generating BSS Load element (bss_load_update_period) + * fixed channel switch from VHT to HT + * add INTERFACE-ENABLED and INTERFACE-DISABLED ctrl_iface events + * add support for learning STA IPv4/IPv6 addresses and configuring + ProxyARP support + * dropped support for the madwifi driver interface + * add support for Suite B (128-bit and 192-bit level) key management and + cipher suites + * fixed a regression with driver=wired + * extend EAPOL-Key msg 1/4 retry workaround for changing SNonce + * add BSS_TM_REQ ctrl_iface command to send BSS Transition Management + Request frames and BSS-TM-RESP event to indicate response to such + frame + * add support for EAP Re-Authentication Protocol (ERP) + * fixed AP IE in EAPOL-Key 3/4 when both WPA and FT was enabled + * fixed a regression in HT 20/40 coex Action frame parsing + * set stdout to be line-buffered + * add support for vendor specific VHT extension to enable 256 QAM rates + (VHT-MCS 8 and 9) on 2.4 GHz band + * RADIUS DAS: + - extend Disconnect-Request processing to allow matching of multiple + sessions + - support Acct-Multi-Session-Id as an identifier + - allow PMKSA cache entry to be removed without association + * expire hostapd STA entry if kernel does not have a matching entry + * allow chanlist to be used to specify a subset of channels for ACS + * improve ACS behavior on 2.4 GHz band and allow channel bias to be + configured with acs_chan_bias parameter + * do not reply to a Probe Request frame that includes DSS Parameter Set + element in which the channel does not match the current operating + channel + * add UPDATE_BEACON ctrl_iface command; this can be used to force Beacon + frame contents to be updated and to start beaconing on an interface + that used start_disabled=1 + * fixed some RADIUS server failover cases + +2014-10-09 - v2.3 + * fixed number of minor issues identified in static analyzer warnings + * fixed DFS and channel switch operation for multi-BSS cases + * started to use constant time comparison for various password and hash + values to reduce possibility of any externally measurable timing + differences + * extended explicit clearing of freed memory and expired keys to avoid + keeping private data in memory longer than necessary + * added support for number of new RADIUS attributes from RFC 7268 + (Mobility-Domain-Id, WLAN-HESSID, WLAN-Pairwise-Cipher, + WLAN-Group-Cipher, WLAN-AKM-Suite, WLAN-Group-Mgmt-Pairwise-Cipher) + * fixed GET_CONFIG wpa_pairwise_cipher value + * added code to clear bridge FDB entry on station disconnection + * fixed PMKSA cache timeout from Session-Timeout for WPA/WPA2 cases + * fixed OKC PMKSA cache entry fetch to avoid a possible infinite loop + in case the first entry does not match + * fixed hostapd_cli action script execution to use more robust mechanism + (CVE-2014-3686) + +2014-06-04 - v2.2 + * fixed SAE confirm-before-commit validation to avoid a potential + segmentation fault in an unexpected message sequence that could be + triggered remotely + * extended VHT support + - Operating Mode Notification + - Power Constraint element (local_pwr_constraint) + - Spectrum management capability (spectrum_mgmt_required=1) + - fix VHT80 segment picking in ACS + - fix vht_capab 'Maximum A-MPDU Length Exponent' handling + - fix VHT20 + * fixed HT40 co-ex scan for some pri/sec channel switches + * extended HT40 co-ex support to allow dynamic channel width changes + during the lifetime of the BSS + * fixed HT40 co-ex support to check for overlapping 20 MHz BSS + * fixed MSCHAP UTF-8 to UCS-2 conversion for three-byte encoding; + this fixes password with include UTF-8 characters that use + three-byte encoding EAP methods that use NtPasswordHash + * reverted TLS certificate validation step change in v2.1 that rejected + any AAA server certificate with id-kp-clientAuth even if + id-kp-serverAuth EKU was included + * fixed STA validation step for WPS ER commands to prevent a potential + crash if an ER sends an unexpected PutWLANResponse to a station that + is disassociated, but not fully removed + * enforce full EAP authentication after RADIUS Disconnect-Request by + removing the PMKSA cache entry + * added support for NAS-IP-Address, NAS-identifier, and NAS-IPv6-Address + in RADIUS Disconnect-Request + * added mechanism for removing addresses for MAC ACLs by prefixing an + entry with "-" + * Interworking/Hotspot 2.0 enhancements + - support Hotspot 2.0 Release 2 + * OSEN network for online signup connection + * subscription remediation (based on RADIUS server request or + control interface HS20_WNM_NOTIF for testing purposes) + * Hotspot 2.0 release number indication in WFA RADIUS VSA + * deauthentication request (based on RADIUS server request or + control interface WNM_DEAUTH_REQ for testing purposes) + * Session Info URL RADIUS AVP to trigger ESS Disassociation Imminent + * hs20_icon config parameter to configure icon files for OSU + * osu_* config parameters for OSU Providers list + - do not use Interworking filtering rules on Probe Request if + Interworking is disabled to avoid interop issues + * added/fixed nl80211 functionality + - AP interface teardown optimization + - support vendor specific driver command + (VENDOR []) + * fixed PMF protection of Deauthentication frame when this is triggered + by session timeout + * internal TLS implementation enhancements/fixes + - add SHA256-based cipher suites + - add DHE-RSA cipher suites + - fix X.509 validation of PKCS#1 signature to check for extra data + * 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 database + - added option for TLS protocol testing of an EAP peer by simulating + various misbehaviors/known attacks + - MAC ACL support for testing purposes + * fixed PTK derivation for CCMP-256 and GCMP-256 + * extended WPS per-station PSK to support ER case + * added option to configure the management group cipher + (group_mgmt_cipher=AES-128-CMAC (default), BIP-GMAC-128, BIP-GMAC-256, + BIP-CMAC-256) + * fixed AP mode default TXOP Limit values for AC_VI and AC_VO (these + were rounded incorrectly) + * added support for postponing FT response in case PMK-R1 needs to be + pulled from R0KH + * added option to advertise 40 MHz intolerant HT capability with + ht_capab=[40-INTOLERANT] + * remove WPS 1.0 only support, i.e., WSC 2.0 support is now enabled + whenever CONFIG_WPS=y is set + * EAP-pwd fixes + - fix possible segmentation fault on EAP method deinit if an invalid + group is negotiated + * fixed RADIUS client retransmit/failover behavior + - there was a potential ctash due to freed memory being accessed + - failover to a backup server mechanism did not work properly + * fixed a possible crash on double DISABLE command when multiple BSSes + are enabled + * fixed a memory leak in SAE random number generation + * fixed GTK rekeying when the station uses FT protocol + * fixed off-by-one bounds checking in printf_encode() + - this could result in deinial of service in some EAP server cases + * various bug fixes + +2014-02-04 - v2.1 + * added support for simultaneous authentication of equals (SAE) for + stronger password-based authentication with WPA2-Personal + * added nl80211 functionality + - VHT configuration for nl80211 + - support split wiphy dump + - driver-based MAC ACL + - QoS Mapping configuration + * added fully automated regression testing with mac80211_hwsim + * allow ctrl_iface group to be specified on command line (-G) + * allow single hostapd process to control independent WPS interfaces + (wps_independent=1) instead of synchronized operations through all + configured interfaces within a process + * avoid processing received management frames multiple times when using + nl80211 with multiple BSSes + * added support for DFS (processing radar detection events, CAC, channel + re-selection) + * added EAP-EKE server + * added automatic channel selection (ACS) + * added option for using per-BSS (vif) configuration files with + -b: + * extended global control interface ADD/REMOVE commands to allow BSSes + of a radio to be removed individually without having to add/remove all + other BSSes of the radio at the same time + * added support for sending debug info to Linux tracing (-T on command + line) + * replace dump_file functionality with same information being available + through the hostapd control interface + * added support for using Protected Dual of Public Action frames for + GAS/ANQP exchanges when PMF is enabled + * added support for WPS+NFC updates + - improved protocol + - option to fetch and report alternative carrier records for external + NFC operations + * various bug fixes + +2013-01-12 - v2.0 + * added AP-STA-DISCONNECTED ctrl_iface event + * improved debug logging (human readable event names, interface name + included in more entries) + * added number of small changes to make it easier for static analyzers + to understand the implementation + * added a workaround for Windows 7 Michael MIC failure reporting and + use of the Secure bit in EAPOL-Key msg 3/4 + * fixed number of small bugs (see git logs for more details) + * changed OpenSSL to read full certificate chain from server_cert file + * nl80211: number of updates to use new cfg80211/nl80211 functionality + - replace monitor interface with nl80211 commands + - additional information for driver-based AP SME + * EAP-pwd: + - fix KDF for group 21 and zero-padding + - added support for fragmentation + - increased maximum number of hunting-and-pecking iterations + * avoid excessive Probe Response retries for broadcast Probe Request + frames (only with drivers using hostapd SME/MLME) + * added preliminary support for using TLS v1.2 (CONFIG_TLSV12=y) + * fixed WPS operation stopping on dual concurrent AP + * added wps_rf_bands configuration parameter for overriding RF Bands + value for WPS + * added support for getting per-device PSK from RADIUS Tunnel-Password + * added support for libnl 3.2 and newer + * increased initial group key handshake retransmit timeout to 500 ms + * added a workaround for 4-way handshake to update SNonce even after + having sent EAPOL-Key 3/4 to avoid issues with some supplicant + implementations that can change SNonce for each EAP-Key 2/4 + * added a workaround for EAPOL-Key 4/4 using incorrect type value in + WPA2 mode (some deployed stations use WPA type in that message) + * added a WPS workaround for mixed mode AP Settings with Windows 7 + * changed WPS AP PIN disabling mechanism to disable the PIN after 10 + consecutive failures in addition to using the exponential lockout + period + * added support for WFA Hotspot 2.0 + - GAS/ANQP advertisement of network information + - disable_dgaf parameter to disable downstream group-addressed + forwarding + * simplified licensing terms by selecting the BSD license as the only + alternative + * EAP-SIM: fixed re-authentication not to update pseudonym + * EAP-SIM: use Notification round before EAP-Failure + * EAP-AKA: added support for AT_COUNTER_TOO_SMALL + * EAP-AKA: skip AKA/Identity exchange if EAP identity is recognized + * EAP-AKA': fixed identity for MK derivation + * EAP-AKA': updated to RFC 5448 (username prefixes changed); note: this + breaks interoperability with older versions + * EAP-SIM/AKA: allow pseudonym to be used after unknown reauth id + * changed ANonce to be a random number instead of Counter-based + * added support for canceling WPS operations with hostapd_cli wps_cancel + * fixed EAP/WPS to PSK transition on reassociation in cases where + deauthentication is missed + * hlr_auc_gw enhancements: + - a new command line parameter -u can be used to enable updating of + SQN in Milenage file + - use 5 bit IND for SQN updates + - SQLite database can now be used to store Milenage information + * EAP-SIM/AKA DB: added optional use of SQLite database for pseudonyms + and reauth data + * added support for Chargeable-User-Identity (RFC 4372) + * added radius_auth_req_attr and radius_acct_req_attr configuration + parameters to allow adding/overriding of RADIUS attributes in + Access-Request and Accounting-Request packets + * added support for RADIUS dynamic authorization server (RFC 5176) + * added initial support for WNM operations + - BSS max idle period + - WNM-Sleep Mode + * added new WPS NFC ctrl_iface mechanism + - removed obsoleted WPS_OOB command (including support for deprecated + UFD config_method) + * added FT support for drivers that implement MLME internally + * added SA Query support for drivers that implement MLME internally + * removed default ACM=1 from AC_VO and AC_VI + * changed VENDOR-TEST EAP method to use proper private enterprise number + (this will not interoperate with older versions) + * added hostapd.conf parameter vendor_elements to allow arbitrary vendor + specific elements to be added to the Beacon and Probe Response frames + * added support for configuring GCMP cipher for IEEE 802.11ad + * added support for 256-bit AES with internal TLS implementation + * changed EAPOL transmission to use AC_VO if WMM is active + * fixed EAP-TLS/PEAP/TTLS/FAST server to validate TLS Message Length + correctly; invalid messages could have caused the hostapd process to + terminate before this fix [CVE-2012-4445] + * limit number of active wildcard PINs for WPS Registrar to one to avoid + confusing behavior with multiple wildcard PINs + * added a workaround for WPS PBC session overlap detection to avoid + interop issues with deployed station implementations that do not + remove active PBC indication from Probe Request frames properly + * added support for using SQLite for the eap_user database + * added Acct-Session-Id attribute into Access-Request messages + * fixed EAPOL frame transmission to non-QoS STAs with nl80211 + (do not send QoS frames if the STA did not negotiate use of QoS for + this association) + +2012-05-10 - v1.0 + * Add channel selection support in hostapd. See hostapd.conf. + * Add support for IEEE 802.11v Time Advertisement mechanism with UTC + TSF offset. See hostapd.conf for config info. + * Delay STA entry removal until Deauth/Disassoc TX status in AP mode. + This allows the driver to use PS buffering of Deauthentication and + Disassociation frames when the STA is in power save sleep. Only + available with drivers that provide TX status events for Deauth/ + Disassoc frames (nl80211). + * Allow PMKSA caching to be disabled on the Authenticator. See + hostap.conf config parameter disable_pmksa_caching. + * atheros: Add support for IEEE 802.11w configuration. + * bsd: Add support for setting HT values in IFM_MMASK. + * Allow client isolation to be configured with ap_isolate. Client + isolation can be used to prevent low-level bridging of frames + between associated stations in the BSS. By default, this bridging + is allowed. + * Allow coexistance of HT BSSes with WEP/TKIP BSSes. + * Add require_ht config parameter, which can be used to configure + hostapd to reject association with any station that does not support + HT PHY. + * Add support for writing debug log to a file using "-f" option. Also + add relog CLI command to re-open the log file. + * Add bridge handling for WDS STA interfaces. By default they are + added to the configured bridge of the AP interface (if present), + but the user can also specify a separate bridge using cli command + wds_bridge. + * hostapd_cli: + - Add wds_bridge command for specifying bridge for WDS STA + interfaces. + - Add relog command for reopening log file. + - Send AP-STA-DISCONNECTED event when an AP disconnects a station + due to inactivity. + - Add wps_config ctrl_interface command for configuring AP. This + command can be used to configure the AP using the internal WPS + registrar. It works in the same way as new AP settings received + from an ER. + - Many WPS/WPS ER commands - see WPS/WPS ER sections for details. + - Add command get version, that returns hostapd version string. + * WNM: Add BSS Transition Management Request for ESS Disassoc Imminent. + Use hostapd_cli ess_disassoc (STA addr) (URL) to send the + notification to the STA. + * Allow AP mode to disconnect STAs based on low ACK condition (when + the data connection is not working properly, e.g., due to the STA + going outside the range of the AP). Disabled by default, enable by + config option disassoc_low_ack. + * Add WPA_IGNORE_CONFIG_ERRORS build option to continue in case of bad + config file. + * WPS: + - Send AP Settings as a wrapped Credential attribute to ctrl_iface + in WPS-NEW-AP-SETTINGS. + - Dispatch more WPS events through hostapd ctrl_iface. + - Add mechanism for indicating non-standard WPS errors. + - Change concurrent radio AP to use only one WPS UPnP instance. + - Add wps_check_pin command for processing PIN from user input. + UIs can use this command to process a PIN entered by a user and to + validate the checksum digit (if present). + - Add hostap_cli get_config command to display current AP config. + - Add new hostapd_cli command, wps_ap_pin, to manage AP PIN at + runtime and support dynamic AP PIN management. + - Disable AP PIN after 10 consecutive failures. Slow down attacks + on failures up to 10. + - Allow AP to start in Enrollee mode without AP PIN for probing, + to be compatible with Windows 7. + - Add Config Error into WPS-FAIL events to provide more info + to the user on how to resolve the issue. + - When controlling multiple interfaces: + - apply WPS commands to all interfaces configured to use WPS + - apply WPS config changes to all interfaces that use WPS + - when an attack is detected on any interface, disable AP PIN on + all interfaces + * WPS ER: + - Show SetSelectedRegistrar events as ctrl_iface events. + - Add special AP Setup Locked mode to allow read only ER. + ap_setup_locked=2 can now be used to enable a special mode where + WPS ER can learn the current AP settings, but cannot change them. + * WPS 2.0: Add support for WPS 2.0 (CONFIG_WPS2) + - Add build option CONFIG_WPS_EXTENSIBILITY_TESTING to enable tool + for testing protocol extensibility. + - Add build option CONFIG_WPS_STRICT to allow disabling of WPS + workarounds. + - Add support for AuthorizedMACs attribute. + * TDLS: + - Allow TDLS use or TDLS channel switching in the BSS to be + prohibited in the BSS, using config params tdls_prohibit and + tdls_prohibit_chan_switch. + * EAP server: Add support for configuring fragment size (see + fragment_size in hostapd.conf). + * wlantest: Add a tool wlantest for IEEE802.11 protocol testing. + wlantest can be used to capture frames from a monitor interface + for realtime capturing or from pcap files for offline analysis. + * Interworking: Support added for 802.11u. Enable in .config with + CONFIG_INTERWORKING. See hostapd.conf for config parameters for + interworking. + * Android: Add build and runtime support for Android hostapd. + * Add a new debug message level for excessive information. Use + -ddd to enable. + * TLS: Add support for tls_disable_time_checks=1 in client mode. + * Internal TLS: + - Add support for TLS v1.1 (RFC 4346). Enable with build parameter + CONFIG_TLSV11. + - Add domainComponent parser for X.509 names + * Reorder some IEs to get closer to IEEE 802.11 standard. Move + WMM into end of Beacon, Probe Resp and (Re)Assoc Resp frames. + Move HT IEs to be later in (Re)Assoc Resp. + * Many bugfixes. + +2010-04-18 - v0.7.2 + * fix WPS internal Registrar use when an external Registrar is also + active + * bsd: Cleaned up driver wrapper and added various low-level + configuration options + * TNC: fixed issues with fragmentation + * EAP-TNC: add Flags field into fragment acknowledgement (needed to + interoperate with other implementations; may potentially breaks + compatibility with older wpa_supplicant/hostapd versions) + * cleaned up driver wrapper API for multi-BSS operations + * nl80211: fix multi-BSS and VLAN operations + * fix number of issues with IEEE 802.11r/FT; this version is not + backwards compatible with old versions + * add SA Query Request processing in AP mode (IEEE 802.11w) + * fix IGTK PN in group rekeying (IEEE 802.11w) + * fix WPS PBC session overlap detection to use correct attribute + * hostapd_notif_Assoc() can now be called with all IEs to simplify + driver wrappers + * work around interoperability issue with some WPS External Registrar + implementations + * nl80211: fix WPS IE update + * hostapd_cli: add support for action script operations (run a script + on hostapd events) + * fix DH padding with internal crypto code (mainly, for WPS) + * fix WPS association with both WPS IE and WPA/RSN IE present with + driver wrappers that use hostapd MLME (e.g., nl80211) + +2010-01-16 - v0.7.1 + * cleaned up driver wrapper API (struct wpa_driver_ops); the new API + is not fully backwards compatible, so out-of-tree driver wrappers + will need modifications + * cleaned up various module interfaces + * merge hostapd and wpa_supplicant developers' documentation into a + single document + * 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 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 + * WPS: add support for dynamically selecting whether to provision the + PSK as an ASCII passphrase or PSK + * added support for WDS (4-address frame) mode with per-station virtual + interfaces (wds_sta=1 in config file; only supported with + driver=nl80211 for now) + * fixed WPS Probe Request processing to handle missing required + attribute + * fixed PKCS#12 use with OpenSSL 1.0.0 + * detect bridge interface automatically so that bridge parameter in + hostapd.conf becomes optional (though, it may now be used to + automatically add then WLAN interface into a bridge with + driver=nl80211) + +2009-11-21 - v0.7.0 + * increased hostapd_cli ping interval to 5 seconds and made this + configurable with a new command line options (-G) + * driver_nl80211: use Linux socket filter to improve performance + * added support for external Registrars with WPS (UPnP transport) + * 802.11n: scan for overlapping BSSes before starting 20/40 MHz channel + * driver_nl80211: fixed STA accounting data collection (TX/RX bytes + reported correctly; TX/RX packets not yet available from kernel) + * added support for WPS USBA out-of-band mechanism with USB Flash + Drives (UFD) (CONFIG_WPS_UFD=y) + * fixed EAPOL/EAP reauthentication when using an external RADIUS + authentication server + * fixed TNC with EAP-TTLS + * fixed IEEE 802.11r key derivation function to match with the standard + (note: this breaks interoperability with previous version) [Bug 303] + * fixed SHA-256 based key derivation function to match with the + standard when using CCMP (for IEEE 802.11r and IEEE 802.11w) + (note: this breaks interoperability with previous version) [Bug 307] + * added number of code size optimizations to remove unnecessary + functionality from the program binary based on build configuration + (part of this automatic; part configurable with CONFIG_NO_* build + options) + * use shared driver wrapper files with wpa_supplicant + * driver_nl80211: multiple updates to provide support for new Linux + nl80211/mac80211 functionality + * updated management frame protection to use IEEE Std 802.11w-2009 + * fixed number of small WPS issues and added workarounds to + interoperate with common deployed broken implementations + * added some IEEE 802.11n co-existence rules to disable 40 MHz channels + or modify primary/secondary channels if needed based on neighboring + networks + * added support for NFC out-of-band mechanism with WPS + * added preliminary support for IEEE 802.11r RIC processing + +2009-01-06 - v0.6.7 + * added support for Wi-Fi Protected Setup (WPS) + (hostapd can now be configured to act as an integrated WPS Registrar + and provision credentials for WPS Enrollees using PIN and PBC + methods; external wireless Registrar can configure the AP, but + external WLAN Manager Registrars are not supported); WPS support can + be enabled by adding CONFIG_WPS=y into .config and setting the + runtime configuration variables in hostapd.conf (see WPS section in + the example configuration file); new hostapd_cli commands wps_pin and + wps_pbc are used to configure WPS negotiation; see README-WPS for + more details + * added IEEE 802.11n HT capability configuration (ht_capab) + * added support for generating Country IE based on nl80211 regulatory + information (added if ieee80211d=1 in configuration) + * fixed WEP authentication (both Open System and Shared Key) with + mac80211 + * added support for EAP-AKA' (draft-arkko-eap-aka-kdf) + * added support for using driver_test over UDP socket + * changed EAP-GPSK to use the IANA assigned EAP method type 51 + * updated management frame protection to use IEEE 802.11w/D7.0 + * fixed retransmission of EAP requests if no response is received + +2008-11-23 - v0.6.6 + * added a new configuration option, wpa_ptk_rekey, that can be used to + enforce frequent PTK rekeying, e.g., to mitigate some attacks against + TKIP deficiencies + * updated OpenSSL code for EAP-FAST to use an updated version of the + session ticket overriding API that was included into the upstream + OpenSSL 0.9.9 tree on 2008-11-15 (no additional OpenSSL patch is + needed with that version anymore) + * changed channel flags configuration to read the information from + the driver (e.g., via driver_nl80211 when using mac80211) instead of + using hostapd as the source of the regulatory information (i.e., + information from CRDA is now used with mac80211); this allows 5 GHz + channels to be used with hostapd (if allowed in the current + regulatory domain) + * fixed EAP-TLS message processing for the last TLS message if it is + large enough to require fragmentation (e.g., if a large Session + Ticket data is included) + * fixed listen interval configuration for nl80211 drivers + +2008-11-01 - v0.6.5 + * added support for SHA-256 as X.509 certificate digest when using the + internal X.509/TLSv1 implementation + * fixed EAP-FAST PAC-Opaque padding (0.6.4 broke this for some peer + identity lengths) + * fixed internal TLSv1 implementation for abbreviated handshake (used + by EAP-FAST server) + * added support for setting VLAN ID for STAs based on local MAC ACL + (accept_mac_file) as an alternative for RADIUS server-based + configuration + * updated management frame protection to use IEEE 802.11w/D6.0 + (adds a new association ping to protect against unauthenticated + authenticate or (re)associate request frames dropping association) + * added support for using SHA256-based stronger key derivation for WPA2 + (IEEE 802.11w) + * added new "driver wrapper" for RADIUS-only configuration + (driver=none in hostapd.conf; CONFIG_DRIVER_NONE=y in .config) + * fixed WPA/RSN IE validation to verify that the proto (WPA vs. WPA2) + is enabled in configuration + * changed EAP-FAST configuration to use separate fields for A-ID and + A-ID-Info (eap_fast_a_id_info) to allow A-ID to be set to a fixed + 16-octet len binary value for better interoperability with some peer + implementations; eap_fast_a_id is now configured as a hex string + * driver_nl80211: Updated to match the current Linux mac80211 AP mode + configuration (wireless-testing.git and Linux kernel releases + starting from 2.6.29) + +2008-08-10 - v0.6.4 + * added peer identity into EAP-FAST PAC-Opaque and skip Phase 2 + Identity Request if identity is already known + * added support for EAP Sequences in EAP-FAST Phase 2 + * added support for EAP-TNC (Trusted Network Connect) + (this version implements the EAP-TNC method and EAP-TTLS/EAP-FAST + changes needed to run two methods in sequence (IF-T) and the IF-IMV + and IF-TNCCS interfaces from TNCS) + * added support for optional cryptobinding with PEAPv0 + * added fragmentation support for EAP-TNC + * added support for fragmenting EAP-TTLS/PEAP/FAST Phase 2 (tunneled) + data + * added support for opportunistic key caching (OKC) + +2008-02-22 - v0.6.3 + * fixed Reassociation Response callback processing when using internal + MLME (driver_{hostap,nl80211,test}.c) + * updated FT support to use the latest draft, IEEE 802.11r/D9.0 + * copy optional Proxy-State attributes into RADIUS response when acting + as a RADIUS authentication server + * fixed EAPOL state machine to handle a case in which no response is + received from the RADIUS authentication server; previous version + could have triggered a crash in some cases after a timeout + * fixed EAP-SIM/AKA realm processing to allow decorated usernames to + be used + * added a workaround for EAP-SIM/AKA peers that include incorrect null + termination in the username + * fixed EAP-SIM/AKA protected result indication to include AT_COUNTER + attribute in notification messages only when using fast + reauthentication + * fixed EAP-SIM Start response processing for fast reauthentication + case + * added support for pending EAP processing in EAP-{PEAP,TTLS,FAST} + phase 2 to allow EAP-SIM and EAP-AKA to be used as the Phase 2 method + +2008-01-01 - v0.6.2 + * fixed EAP-SIM and EAP-AKA message parser to validate attribute + lengths properly to avoid potential crash caused by invalid messages + * added data structure for storing allocated buffers (struct wpabuf); + this does not affect hostapd usage, but many of the APIs changed + and various interfaces (e.g., EAP) is not compatible with old + versions + * added support for protecting EAP-AKA/Identity messages with + AT_CHECKCODE (optional feature in RFC 4187) + * added support for protected result indication with AT_RESULT_IND for + EAP-SIM and EAP-AKA (eap_sim_aka_result_ind=1) + * added support for configuring EAP-TTLS phase 2 non-EAP methods in + EAP server configuration; previously all four were enabled for every + phase 2 user, now all four are disabled by default and need to be + enabled with new method names TTLS-PAP, TTLS-CHAP, TTLS-MSCHAP, + TTLS-MSCHAPV2 + * removed old debug printing mechanism and the related 'debug' + parameter in the configuration file; debug verbosity is now set with + -d (or -dd) command line arguments + * added support for EAP-IKEv2 (draft-tschofenig-eap-ikev2-15.txt); + only shared key/password authentication is supported in this version + +2007-11-24 - v0.6.1 + * added experimental, integrated TLSv1 server implementation with the + needed X.509/ASN.1/RSA/bignum processing (this can be enabled by + setting CONFIG_TLS=internal and CONFIG_INTERNAL_LIBTOMMATH=y in + .config); this can be useful, e.g., if the target system does not + have a suitable TLS library and a minimal code size is required + * added support for EAP-FAST server method to the integrated EAP + server + * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest + draft (draft-ietf-emu-eap-gpsk-07.txt) + * added a new configuration parameter, rsn_pairwise, to allow different + pairwise cipher suites to be enabled for WPA and RSN/WPA2 + (note: if wpa_pairwise differs from rsn_pairwise, the driver will + either need to support this or will have to use the WPA/RSN IEs from + hostapd; currently, the included madwifi and bsd driver interfaces do + not have support for this) + * updated FT support to use the latest draft, IEEE 802.11r/D8.0 + +2007-05-28 - v0.6.0 + * added experimental IEEE 802.11r/D6.0 support + * updated EAP-SAKE to RFC 4763 and the IANA-allocated EAP type 48 + * updated EAP-PSK to use the IANA-allocated EAP type 47 + * fixed EAP-PSK bit ordering of the Flags field + * fixed configuration reloading (SIGHUP) to re-initialize WPA PSKs + by reading wpa_psk_file [Bug 181] + * fixed EAP-TTLS AVP parser processing for too short AVP lengths + * fixed IPv6 connection to RADIUS accounting server + * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest + draft (draft-ietf-emu-eap-gpsk-04.txt) + * hlr_auc_gw: read GSM triplet file into memory and rotate through the + entries instead of only using the same three triplets every time + (this does not work properly with tests using multiple clients, but + provides bit better triplet data for testing a single client; anyway, + if a better quality triplets are needed, GSM-Milenage should be used + instead of hardcoded triplet file) + * fixed EAP-MSCHAPv2 server to use a space between S and M parameters + in Success Request [Bug 203] + * added support for sending EAP-AKA Notifications in error cases + * updated to use IEEE 802.11w/D2.0 for management frame protection + (still experimental) + * RADIUS server: added support for processing duplicate messages + (retransmissions from RADIUS client) by replying with the previous + reply + +2006-11-24 - v0.5.6 + * added support for configuring and controlling multiple BSSes per + radio interface (bss= in hostapd.conf); this is only + available with Devicescape and test driver interfaces + * fixed PMKSA cache update in the end of successful RSN + pre-authentication + * added support for dynamic VLAN configuration (i.e., selecting VLAN-ID + for each STA based on RADIUS Access-Accept attributes); this requires + VLAN support from the kernel driver/802.11 stack and this is + currently only available with Devicescape and test driver interfaces + * driver_madwifi: fixed configuration of unencrypted modes (plaintext + and IEEE 802.1X without WEP) + * removed STAKey handshake since PeerKey handshake has replaced it in + IEEE 802.11ma and there are no known deployments of STAKey + * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest + draft (draft-ietf-emu-eap-gpsk-01.txt) + * added preliminary implementation of IEEE 802.11w/D1.0 (management + frame protection) + (Note: this requires driver support to work properly.) + (Note2: IEEE 802.11w is an unapproved draft and subject to change.) + * hlr_auc_gw: added support for GSM-Milenage (for EAP-SIM) + * hlr_auc_gw: added support for reading per-IMSI Milenage keys and + parameters from a text file to make it possible to implement proper + GSM/UMTS authentication server for multiple SIM/USIM cards using + EAP-SIM/EAP-AKA + * fixed session timeout processing with drivers that do not use + ieee802_11.c (e.g., madwifi) + +2006-08-27 - v0.5.5 + * added 'hostapd_cli new_sta ' command for adding a new STA into + hostapd (e.g., to initialize wired network authentication based on an + external signal) + * fixed hostapd to add PMKID KDE into 4-Way Handshake Message 1 when + using WPA2 even if PMKSA caching is not used + * added -P argument for hostapd to write the current process + id into a file + * added support for RADIUS Authentication Server MIB (RFC 2619) + +2006-06-20 - v0.5.4 + * fixed nt_password_hash build [Bug 144] + * added PeerKey handshake implementation for IEEE 802.11e + direct link setup (DLS) to replace STAKey handshake + * added support for EAP Generalized Pre-Shared Key (EAP-GPSK, + draft-clancy-emu-eap-shared-secret-00.txt) + * fixed a segmentation fault when RSN pre-authentication was completed + successfully [Bug 152] + +2006-04-27 - v0.5.3 + * do not build nt_password_hash and hlr_auc_gw by default to avoid + requiring a TLS library for a successful build; these programs can be + build with 'make nt_password_hash' and 'make hlr_auc_gw' + * added a new configuration option, eapol_version, that can be used to + set EAPOL version to 1 (default is 2) to work around broken client + implementations that drop EAPOL frames which use version number 2 + [Bug 89] + * added support for EAP-SAKE (no EAP method number allocated yet, so + this is using the same experimental type 255 as EAP-PSK) + * fixed EAP-MSCHAPv2 message length validation + +2006-03-19 - v0.5.2 + * fixed stdarg use in hostapd_logger(): if both stdout and syslog + logging was enabled, hostapd could trigger a segmentation fault in + vsyslog on some CPU -- C library combinations + * moved HLR/AuC gateway implementation for EAP-SIM/AKA into an external + program to make it easier to use for implementing real SS7 gateway; + eap_sim_db is not anymore used as a file name for GSM authentication + triplets; instead, it is path to UNIX domain socket that will be used + to communicate with the external gateway program (e.g., hlr_auc_gw) + * added example HLR/AuC gateway implementation, hlr_auc_gw, that uses + local information (GSM authentication triplets from a text file and + hardcoded AKA authentication data); this can be used to test EAP-SIM + and EAP-AKA + * added Milenage algorithm (example 3GPP AKA algorithm) to hlr_auc_gw + to make it possible to test EAP-AKA with real USIM cards (this is + disabled by default; define AKA_USE_MILENAGE when building hlr_auc_gw + to enable this) + * driver_madwifi: added support for getting station RSN IE from + madwifi-ng svn r1453 and newer; this fixes RSN that was apparently + broken with earlier change (r1357) in the driver + * changed EAP method registration to use a dynamic list of methods + instead of a static list generated at build time + * fixed WPA message 3/4 not to encrypt Key Data field (WPA IE) + [Bug 125] + * added ap_max_inactivity configuration parameter + +2006-01-29 - v0.5.1 + * driver_test: added better support for multiple APs and STAs by using + a directory with sockets that include MAC address for each device in + the name (test_socket=DIR:/tmp/test) + * added support for EAP expanded type (vendor specific EAP methods) + +2005-12-18 - v0.5.0 (beginning of 0.5.x development releases) + * added experimental STAKey handshake implementation for IEEE 802.11e + direct link setup (DLS); note: this is disabled by default in both + build and runtime configuration (can be enabled with CONFIG_STAKEY=y + and stakey=1) + * added support for EAP methods to use callbacks to external programs + by buffering a pending request and processing it after the EAP method + is ready to continue + * improved EAP-SIM database interface to allow external request to GSM + HLR/AuC without blocking hostapd process + * added support for using EAP-SIM pseudonyms and fast re-authentication + * added support for EAP-AKA in the integrated EAP authenticator + * added support for matching EAP identity prefixes (e.g., "1"*) in EAP + user database to allow EAP-SIM/AKA selection without extra roundtrip + for EAP-Nak negotiation + * added support for storing EAP user password as NtPasswordHash instead + of plaintext password when using MSCHAP or MSCHAPv2 for + authentication (hash:<16-octet hex value>); added nt_password_hash + tool for hashing password to generate NtPasswordHash + +2005-11-20 - v0.4.7 (beginning of 0.4.x stable releases) + * driver_wired: fixed EAPOL sending to optionally use PAE group address + as the destination instead of supplicant MAC address; this is + disabled by default, but should be enabled with use_pae_group_addr=1 + in configuration file if the wired interface is used by only one + device at the time (common switch configuration) + * driver_madwifi: configure driver to use TKIP countermeasures in order + to get correct behavior (IEEE 802.11 association failing; previously, + association succeeded, but hostpad forced disassociation immediately) + * driver_madwifi: added support for madwifi-ng + +2005-10-27 - v0.4.6 + * added support for replacing user identity from EAP with RADIUS + User-Name attribute from Access-Accept message, if that is included, + for the RADIUS accounting messages (e.g., for EAP-PEAP/TTLS to get + tunneled identity into accounting messages when the RADIUS server + does not support better way of doing this with Class attribute) + * driver_madwifi: fixed EAPOL packet receive for configuration where + ath# is part of a bridge interface + * added a configuration file and log analyzer script for logwatch + * fixed EAPOL state machine step function to process all state + transitions before processing new events; this resolves a race + condition in which EAPOL-Start message could trigger hostapd to send + two EAP-Response/Identity frames to the authentication server + +2005-09-25 - v0.4.5 + * added client CA list to the TLS certificate request in order to make + it easier for the client to select which certificate to use + * added experimental support for EAP-PSK + * added support for WE-19 (hostap, madwifi) + +2005-08-21 - v0.4.4 + * fixed build without CONFIG_RSN_PREAUTH + * fixed FreeBSD build + +2005-06-26 - v0.4.3 + * fixed PMKSA caching to copy User-Name and Class attributes so that + RADIUS accounting gets correct information + * start RADIUS accounting only after successful completion of WPA + 4-Way Handshake if WPA-PSK is used + * fixed PMKSA caching for the case where STA (re)associates without + first disassociating + +2005-06-12 - v0.4.2 + * EAP-PAX is now registered as EAP type 46 + * fixed EAP-PAX MAC calculation + * fixed EAP-PAX CK and ICK key derivation + * renamed eap_authenticator configuration variable to eap_server to + better match with RFC 3748 (EAP) terminology + * driver_test: added support for testing hostapd with wpa_supplicant + by using test driver interface without any kernel drivers or network + cards + +2005-05-22 - v0.4.1 + * fixed RADIUS server initialization when only auth or acct server + is configured and the other one is left empty + * driver_madwifi: added support for RADIUS accounting + * driver_madwifi: added preliminary support for compiling against 'BSD' + branch of madwifi CVS tree + * driver_madwifi: fixed pairwise key removal to allow WPA reauth + without disassociation + * added support for reading additional certificates from PKCS#12 files + and adding them to the certificate chain + * fixed RADIUS Class attribute processing to only use Access-Accept + packets to update Class; previously, other RADIUS authentication + packets could have cleared Class attribute + * added support for more than one Class attribute in RADIUS packets + * added support for verifying certificate revocation list (CRL) when + using integrated EAP authenticator for EAP-TLS; new hostapd.conf + options 'check_crl'; CRL must be included in the ca_cert file for now + +2005-04-25 - v0.4.0 (beginning of 0.4.x development releases) + * added support for including network information into + EAP-Request/Identity message (ASCII-0 (nul) in eap_message) + (e.g., to implement draft-adrange-eap-network-discovery-07.txt) + * fixed a bug which caused some RSN pre-authentication cases to use + freed memory and potentially crash hostapd + * fixed private key loading for cases where passphrase is not set + * added support for sending TLS alerts and aborting authentication + when receiving a TLS alert + * fixed WPA2 to add PMKSA cache entry when using integrated EAP + authenticator + * fixed PMKSA caching (EAP authentication was not skipped correctly + with the new state machine changes from IEEE 802.1X draft) + * added support for RADIUS over IPv6; own_ip_addr, auth_server_addr, + and acct_server_addr can now be IPv6 addresses (CONFIG_IPV6=y needs + to be added to .config to include IPv6 support); for RADIUS server, + radius_server_ipv6=1 needs to be set in hostapd.conf and addresses + in RADIUS clients file can then use IPv6 format + * added experimental support for EAP-PAX + * replaced hostapd control interface library (hostapd_ctrl.[ch]) with + the same implementation that wpa_supplicant is using (wpa_ctrl.[ch]) + +2005-02-12 - v0.3.7 (beginning of 0.3.x stable releases) + +2005-01-23 - v0.3.5 + * added support for configuring a forced PEAP version based on the + Phase 1 identity + * fixed PEAPv1 to use tunneled EAP-Success/Failure instead of EAP-TLV + to terminate authentication + * fixed EAP identifier duplicate processing with the new IEEE 802.1X + draft + * clear accounting data in the driver when starting a new accounting + session + * driver_madwifi: filter wireless events based on ifindex to allow more + than one network interface to be used + * fixed WPA message 2/4 processing not to cancel timeout for TimeoutEvt + setting if the packet does not pass MIC verification (e.g., due to + incorrect PSK); previously, message 1/4 was not tried again if an + invalid message 2/4 was received + * fixed reconfiguration of RADIUS client retransmission timer when + adding a new message to the pending list; previously, timer was not + updated at this point and if there was a pending message with long + time for the next retry, the new message needed to wait that long for + its first retry, too + +2005-01-09 - v0.3.4 + * added support for configuring multiple allowed EAP types for Phase 2 + authentication (EAP-PEAP, EAP-TTLS) + * fixed EAPOL-Start processing to trigger WPA reauthentication + (previously, only EAPOL authentication was done) + +2005-01-02 - v0.3.3 + * added support for EAP-PEAP in the integrated EAP authenticator + * added support for EAP-GTC in the integrated EAP authenticator + * added support for configuring list of EAP methods for Phase 1 so that + the integrated EAP authenticator can, e.g., use the wildcard entry + for EAP-TLS and EAP-PEAP + * added support for EAP-TTLS in the integrated EAP authenticator + * added support for EAP-SIM in the integrated EAP authenticator + * added support for using hostapd as a RADIUS authentication server + with the integrated EAP authenticator taking care of EAP + authentication (new hostapd.conf options: radius_server_clients and + radius_server_auth_port); this is not included in default build; use + CONFIG_RADIUS_SERVER=y in .config to include + +2004-12-19 - v0.3.2 + * removed 'daemonize' configuration file option since it has not really + been used at all for more than year + * driver_madwifi: fixed group key setup and added get_ssid method + * added support for EAP-MSCHAPv2 in the integrated EAP authenticator + +2004-12-12 - v0.3.1 + * added support for integrated EAP-TLS authentication (new hostapd.conf + variables: ca_cert, server_cert, private_key, private_key_passwd); + this enabled dynamic keying (WPA2/WPA/IEEE 802.1X/WEP) without + external RADIUS server + * added support for reading PKCS#12 (PFX) files (as a replacement for + PEM/DER) to get certificate and private key (CONFIG_PKCS12) + +2004-12-05 - v0.3.0 (beginning of 0.3.x development releases) + * added support for Acct-{Input,Output}-Gigawords + * added support for Event-Timestamp (in RADIUS Accounting-Requests) + * added support for RADIUS Authentication Client MIB (RFC2618) + * added support for RADIUS Accounting Client MIB (RFC2620) + * made EAP re-authentication period configurable (eap_reauth_period) + * fixed EAPOL reauthentication to trigger WPA/WPA2 reauthentication + * fixed EAPOL state machine to stop if STA is removed during + eapol_sm_step(); this fixes at least one segfault triggering bug with + IEEE 802.11i pre-authentication + * added support for multiple WPA pre-shared keys (e.g., one for each + client MAC address or keys shared by a group of clients); + new hostapd.conf field wpa_psk_file for setting path to a text file + containing PSKs, see hostapd.wpa_psk for an example + * added support for multiple driver interfaces to allow hostapd to be + used with other drivers + * added wired authenticator driver interface (driver=wired in + hostapd.conf, see wired.conf for example configuration) + * added madwifi driver interface (driver=madwifi in hostapd.conf, see + madwifi.conf for example configuration; Note: include files from + madwifi project is needed for building and a configuration file, + .config, needs to be created in hostapd directory with + CONFIG_DRIVER_MADWIFI=y to include this driver interface in hostapd + build) + * fixed an alignment issue that could cause SHA-1 to fail on some + platforms (e.g., Intel ixp425 with a compiler that does not 32-bit + align variables) + * fixed RADIUS reconnection after an error in sending interim + accounting packets + * added hostapd control interface for external programs and an example + CLI, hostapd_cli (like wpa_cli for wpa_supplicant) + * started adding dot11, dot1x, radius MIBs ('hostapd_cli mib', + 'hostapd_cli sta ') + * finished update from IEEE 802.1X-2001 to IEEE 802.1X-REV (now d11) + * added support for strict GTK rekeying (wpa_strict_rekey in + hostapd.conf) + * updated IAPP to use UDP port 3517 and multicast address 224.0.1.178 + (instead of broadcast) for IAPP ADD-notify (moved from draft 3 to + IEEE 802.11F-2003) + * added Prism54 driver interface (driver=prism54 in hostapd.conf; + note: .config needs to be created in hostapd directory with + CONFIG_DRIVER_PRISM54=y to include this driver interface in hostapd + build) + * dual-licensed hostapd (GPLv2 and BSD licenses) + * fixed RADIUS accounting to generate a new session id for cases where + a station reassociates without first being complete deauthenticated + * fixed STA disassociation handler to mark next timeout state to + deauthenticate the station, i.e., skip long wait for inactivity poll + and extra disassociation, if the STA disassociates without + deauthenticating + * added integrated EAP authenticator that can be used instead of + external RADIUS authentication server; currently, only EAP-MD5 is + supported, so this cannot yet be used for key distribution; the EAP + method interface is generic, though, so adding new EAP methods should + be straightforward; new hostapd.conf variables: 'eap_authenticator' + and 'eap_user_file'; this obsoletes "minimal authentication server" + ('minimal_eap' in hostapd.conf) which is now removed + * added support for FreeBSD and driver interface for the BSD net80211 + layer (driver=bsd in hostapd.conf and CONFIG_DRIVER_BSD=y in + .config); please note that some of the required kernel mods have not + yet been committed + +2004-07-17 - v0.2.4 (beginning of 0.2.x stable releases) + * fixed some accounting cases where Accounting-Start was sent when + IEEE 802.1X port was being deauthorized + +2004-06-20 - v0.2.3 + * modified RADIUS client to re-connect the socket in case of certain + error codes that are generated when a network interface state is + changes (e.g., when IP address changes or the interface is set UP) + * fixed couple of cases where EAPOL state for a station was freed + twice causing a segfault for hostapd + * fixed couple of bugs in processing WPA deauthentication (freed data + was used) + +2004-05-31 - v0.2.2 + * fixed WPA/WPA2 group rekeying to use key index correctly (GN/GM) + * fixed group rekeying to send zero TSC in EAPOL-Key messages to fix + cases where STAs dropped multicast frames as replay attacks + * added support for copying RADIUS Attribute 'Class' from + authentication messages into accounting messages + * send canned EAP failure if RADIUS server sends Access-Reject without + EAP message (previously, Supplicant was not notified in this case) + * fixed mixed WPA-PSK and WPA-EAP mode to work with WPA-PSK (i.e., do + not start EAPOL state machines if the STA selected to use WPA-PSK) + +2004-05-06 - v0.2.1 + * added WPA and IEEE 802.11i/RSN (WPA2) Authenticator functionality + - based on IEEE 802.11i/D10.0 but modified to interoperate with WPA + (i.e., IEEE 802.11i/D3.0) + - supports WPA-only, RSN-only, and mixed WPA/RSN mode + - both WPA-PSK and WPA-RADIUS/EAP are supported + - PMKSA caching and pre-authentication + - new hostapd.conf variables: wpa, wpa_psk, wpa_passphrase, + wpa_key_mgmt, wpa_pairwise, wpa_group_rekey, wpa_gmk_rekey, + rsn_preauth, rsn_preauth_interfaces + * fixed interim accounting to remove any pending accounting messages + to the STA before sending a new one + +2004-02-15 - v0.2.0 + * added support for Acct-Interim-Interval: + - draft-ietf-radius-acct-interim-01.txt + - use Acct-Interim-Interval attribute from Access-Accept if local + 'radius_acct_interim_interval' is not set + - allow different update intervals for each STA + * fixed event loop to call signal handlers only after returning from + the real signal handler + * reset sta->timeout_next after successful association to make sure + that the previously registered inactivity timer will not remove the + STA immediately (e.g., if STA deauthenticates and re-associates + before the timer is triggered). + * added new hostapd.conf variable, nas_identifier, that can be used to + add an optional RADIUS Attribute, NAS-Identifier, into authentication + and accounting messages + * added support for Accounting-On and Accounting-Off messages + * fixed accounting session handling to send Accounting-Start only once + per session and not to send Accounting-Stop if the session was not + initialized properly + * fixed Accounting-Stop statistics in cases where the message was + previously sent after the kernel entry for the STA (and/or IEEE + 802.1X data) was removed + + +Note: + +Older changes up to and including v0.1.0 are included in the ChangeLog +of the Host AP driver. diff --git a/hostapd/Makefile b/hostapd/Makefile new file mode 100644 index 000000000000..ca4439234a11 --- /dev/null +++ b/hostapd/Makefile @@ -0,0 +1,1404 @@ +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 += ../src/common/ptksa_cache.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 -U_FORTIFY_SOURCE +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 + +NEED_AES_UNWRAP=y + +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 +NEED_AES_SIV=y +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_IEEE80211BE +CONFIG_IEEE80211AX=y +CFLAGS += -DCONFIG_IEEE80211BE +OBJS += ../src/ap/ieee802_11_eht.o +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 +ifdef CONFIG_DPP3 +CFLAGS += -DCONFIG_DPP3 +endif +endif + +ifdef CONFIG_NAN_USD +OBJS += ../src/common/nan_de.o +OBJS += ../src/ap/nan_usd_ap.o +CFLAGS += -DCONFIG_NAN_USD +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 +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 CONFIG_RADIUS_TLS +TLS_FUNCS=y +CFLAGS += -DCONFIG_RADIUS_TLS +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) +CFLAGS += -DCRYPTO_RSA_OAEP_SHA256 +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), openssl) +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), wolfssl) +AESOBJS += ../src/crypto/aes-omac1.o +endif +endif +endif +ifdef NEED_AES_UNWRAP +ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), wolfssl) +NEED_AES_DEC=y +AESOBJS += ../src/crypto/aes-unwrap.o +endif +endif +endif +endif +ifdef NEED_AES_CBC +NEED_AES_DEC=y +ifneq ($(CONFIG_TLS), openssl) +ifneq ($(CONFIG_TLS), linux) +ifneq ($(CONFIG_TLS), wolfssl) +AESOBJS += ../src/crypto/aes-cbc.o +endif +endif +endif +endif +ifdef NEED_AES_DEC +ifdef CONFIG_INTERNAL_AES +AESOBJS += ../src/crypto/aes-internal-dec.o +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/comeback_token.o +OBJS += ../src/pasn/pasn_responder.o +OBJS += ../src/pasn/pasn_common.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/hostapd/README b/hostapd/README new file mode 100644 index 000000000000..1a0248fce422 --- /dev/null +++ b/hostapd/README @@ -0,0 +1,354 @@ +hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP + Authenticator and RADIUS authentication server +================================================================ + +Copyright (c) 2002-2024, Jouni Malinen and contributors +All Rights Reserved. + +This program is licensed under the BSD license (the one with +advertisement clause removed). + +If you are submitting changes to the project, please see CONTRIBUTIONS +file for more instructions. + + + +License +------- + +This software may be distributed, used, and modified under the terms of +BSD license: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name(s) of the above-listed copyright holder(s) nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +Introduction +============ + +Originally, hostapd was an optional user space component for Host AP +driver. It adds more features to the basic IEEE 802.11 management +included in the kernel driver: using external RADIUS authentication +server for MAC address based access control, IEEE 802.1X Authenticator +and dynamic WEP keying, RADIUS accounting, WPA/WPA2 (IEEE 802.11i/RSN) +Authenticator and dynamic TKIP/CCMP keying. + +The current version includes support for other drivers, an integrated +EAP server (i.e., allow full authentication without requiring +an external RADIUS authentication server), and RADIUS authentication +server for EAP authentication. + + +Requirements +------------ + +Current hardware/software requirements: +- drivers: + Host AP driver for Prism2/2.5/3. + (http://w1.fi/hostap-driver.html) + Please note that station firmware version needs to be 1.7.0 or newer + to work in WPA mode. + + mac80211-based drivers that support AP mode (with driver=nl80211). + This includes drivers for Atheros (ath9k) and Broadcom (b43) + chipsets. + + Any wired Ethernet driver for wired IEEE 802.1X authentication + (experimental code) + + FreeBSD -current + BSD net80211 layer (e.g., Atheros driver) + + +Build configuration +------------------- + +In order to be able to build hostapd, you will need to create a build +time configuration file, .config that selects which optional +components are included. See defconfig file for example configuration +and list of available options. + + + +IEEE 802.1X +=========== + +IEEE Std 802.1X-2001 is a standard for port-based network access +control. In case of IEEE 802.11 networks, a "virtual port" is used +between each associated station and the AP. IEEE 802.11 specifies +minimal authentication mechanism for stations, whereas IEEE 802.1X +introduces a extensible mechanism for authenticating and authorizing +users. + +IEEE 802.1X uses elements called Supplicant, Authenticator, Port +Access Entity, and Authentication Server. Supplicant is a component in +a station and it performs the authentication with the Authentication +Server. An access point includes an Authenticator that relays the packets +between a Supplicant and an Authentication Server. In addition, it has a +Port Access Entity (PAE) with Authenticator functionality for +controlling the virtual port authorization, i.e., whether to accept +packets from or to the station. + +IEEE 802.1X uses Extensible Authentication Protocol (EAP). The frames +between a Supplicant and an Authenticator are sent using EAP over LAN +(EAPOL) and the Authenticator relays these frames to the Authentication +Server (and similarly, relays the messages from the Authentication +Server to the Supplicant). The Authentication Server can be colocated with the +Authenticator, in which case there is no need for additional protocol +for EAP frame transmission. However, a more common configuration is to +use an external Authentication Server and encapsulate EAP frame in the +frames used by that server. RADIUS is suitable for this, but IEEE +802.1X would also allow other mechanisms. + +Host AP driver includes PAE functionality in the kernel driver. It +is a relatively simple mechanism for denying normal frames going to +or coming from an unauthorized port. PAE allows IEEE 802.1X related +frames to be passed between the Supplicant and the Authenticator even +on an unauthorized port. + +User space daemon, hostapd, includes Authenticator functionality. It +receives 802.1X (EAPOL) frames from the Supplicant using the wlan#ap +device that is also used with IEEE 802.11 management frames. The +frames to the Supplicant are sent using the same device. + +The normal configuration of the Authenticator would use an external +Authentication Server. hostapd supports RADIUS encapsulation of EAP +packets, so the Authentication Server should be a RADIUS server, like +FreeRADIUS (http://www.freeradius.org/). The Authenticator in hostapd +relays the frames between the Supplicant and the Authentication +Server. It also controls the PAE functionality in the kernel driver by +controlling virtual port authorization, i.e., station-AP +connection, based on the IEEE 802.1X state. + +When a station would like to use the services of an access point, it +will first perform IEEE 802.11 authentication. This is normally done +with open systems authentication, so there is no security. After +this, IEEE 802.11 association is performed. If IEEE 802.1X is +configured to be used, the virtual port for the station is set in +Unauthorized state and only IEEE 802.1X frames are accepted at this +point. The Authenticator will then ask the Supplicant to authenticate +with the Authentication Server. After this is completed successfully, +the virtual port is set to Authorized state and frames from and to the +station are accepted. + +Host AP configuration for IEEE 802.1X +------------------------------------- + +The user space daemon has its own configuration file that can be used to +define AP options. Distribution package contains an example +configuration file (hostapd/hostapd.conf) that can be used as a basis +for configuration. It includes examples of all supported configuration +options and short description of each option. hostapd should be started +with full path to the configuration file as the command line argument, +e.g., './hostapd /etc/hostapd.conf'. If you have more that one wireless +LAN card, you can use one hostapd process for multiple interfaces by +giving a list of configuration files (one per interface) in the command +line. + +hostapd includes a minimal co-located IEEE 802.1X server which can be +used to test IEEE 802.1X authentication. However, it should not be +used in normal use since it does not provide any security. This can be +configured by setting ieee8021x and minimal_eap options in the +configuration file. + +An external Authentication Server (RADIUS) is configured with +auth_server_{addr,port,shared_secret} options. In addition, +ieee8021x and own_ip_addr must be set for this mode. With such +configuration, the co-located Authentication Server is not used and EAP +frames will be relayed using EAPOL between the Supplicant and the +Authenticator and RADIUS encapsulation between the Authenticator and +the Authentication Server. Other than this, the functionality is similar +to the case with the co-located Authentication Server. + +Authentication Server +--------------------- + +Any RADIUS server supporting EAP should be usable as an IEEE 802.1X +Authentication Server with hostapd Authenticator. FreeRADIUS +(http://www.freeradius.org/) has been successfully tested with hostapd +Authenticator. + +Automatic WEP key configuration +------------------------------- + +EAP/TLS generates a session key that can be used to send WEP keys from +an AP to authenticated stations. The Authenticator in hostapd can be +configured to automatically select a random default/broadcast key +(shared by all authenticated stations) with wep_key_len_broadcast +option (5 for 40-bit WEP or 13 for 104-bit WEP). In addition, +wep_key_len_unicast option can be used to configure individual unicast +keys for stations. This requires support for individual keys in the +station driver. + +WEP keys can be automatically updated by configuring rekeying. This +will improve security of the network since same WEP key will only be +used for a limited period of time. wep_rekey_period option sets the +interval for rekeying in seconds. + + +WPA/WPA2 +======== + +Features +-------- + +Supported WPA/IEEE 802.11i features: +- WPA-PSK ("WPA-Personal") +- WPA with EAP (e.g., with RADIUS authentication server) ("WPA-Enterprise") +- key management for CCMP, TKIP, WEP104, WEP40 +- RSN/WPA2 (IEEE 802.11i), including PMKSA caching and pre-authentication + +WPA +--- + +The original security mechanism of IEEE 802.11 standard was not +designed to be strong and has proved to be insufficient for most +networks that require some kind of security. Task group I (Security) +of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked +to address the flaws of the base standard and has in practice +completed its work in May 2004. The IEEE 802.11i amendment to the IEEE +802.11 standard was approved in June 2004 and this amendment was +published in July 2004. + +Wi-Fi Alliance (http://www.wi-fi.org/) used a draft version of the +IEEE 802.11i work (draft 3.0) to define a subset of the security +enhancements that can be implemented with existing wlan hardware. This +is called Wi-Fi Protected Access (WPA). This has now become a +mandatory component of interoperability testing and certification done +by Wi-Fi Alliance. + +IEEE 802.11 standard defined wired equivalent privacy (WEP) algorithm +for protecting wireless networks. WEP uses RC4 with 40-bit keys, +24-bit initialization vector (IV), and CRC32 to protect against packet +forgery. All these choices have proven to be insufficient: key space is +too small against current attacks, RC4 key scheduling is insufficient +(beginning of the pseudorandom stream should be skipped), IV space is +too small and IV reuse makes attacks easier, there is no replay +protection, and non-keyed authentication does not protect against bit +flipping packet data. + +WPA is an intermediate solution for the security issues. It uses +Temporal Key Integrity Protocol (TKIP) to replace WEP. TKIP is a +compromise on strong security and possibility to use existing +hardware. It still uses RC4 for the encryption like WEP, but with +per-packet RC4 keys. In addition, it implements replay protection, +keyed packet authentication mechanism (Michael MIC). + +Keys can be managed using two different mechanisms. WPA can either use +an external authentication server (e.g., RADIUS) and EAP just like +IEEE 802.1X is using or pre-shared keys without need for additional +servers. Wi-Fi calls these "WPA-Enterprise" and "WPA-Personal", +respectively. Both mechanisms will generate a master session key for +the Authenticator (AP) and Supplicant (client station). + +WPA implements a new key handshake (4-Way Handshake and Group Key +Handshake) for generating and exchanging data encryption keys between +the Authenticator and Supplicant. This handshake is also used to +verify that both Authenticator and Supplicant know the master session +key. These handshakes are identical regardless of the selected key +management mechanism (only the method for generating master session +key changes). + + +IEEE 802.11i / WPA2 +------------------- + +The design for parts of IEEE 802.11i that were not included in WPA has +finished (May 2004) and this amendment to IEEE 802.11 was approved in +June 2004. Wi-Fi Alliance is using the final IEEE 802.11i as a new +version of WPA called WPA2. This includes, e.g., support for more +robust encryption algorithm (CCMP: AES in Counter mode with CBC-MAC) +to replace TKIP and optimizations for handoff (reduced number of +messages in initial key handshake, pre-authentication, and PMKSA caching). + +Some wireless LAN vendors are already providing support for CCMP in +their WPA products. There is no "official" interoperability +certification for CCMP and/or mixed modes using both TKIP and CCMP, so +some interoperability issues can be expected even though many +combinations seem to be working with equipment from different vendors. +Testing for WPA2 is likely to start during the second half of 2004. + +hostapd configuration for WPA/WPA2 +---------------------------------- + +TODO + +# Enable WPA. Setting this variable configures the AP to require WPA (either +# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either +# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK. +# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys), +# RADIUS authentication server must be configured, and WPA-EAP must be included +# in wpa_key_mgmt. +# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0) +# and/or WPA2 (full IEEE 802.11i/RSN): +# bit0 = WPA +# bit1 = IEEE 802.11i/RSN (WPA2) +#wpa=1 + +# 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 +# so the PSK changes when ASCII passphrase is used and the SSID is changed. +#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef +#wpa_passphrase=secret passphrase + +# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The +# entries are separated with a space. +#wpa_key_mgmt=WPA-PSK WPA-EAP + +# Set of accepted cipher suites (encryption algorithms) for pairwise keys +# (unicast packets). This is a space separated list of algorithms: +# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i] +# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i] +# Group cipher suite (encryption algorithm for broadcast and multicast frames) +# is automatically selected based on this configuration. If only CCMP is +# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise, +# TKIP will be used as the group cipher. +#wpa_pairwise=TKIP CCMP + +# Time interval for rekeying GTK (broadcast/multicast encryption keys) in +# seconds. +#wpa_group_rekey=600 + +# Time interval for rekeying GMK (master key used internally to generate GTKs +# (in seconds). +#wpa_gmk_rekey=86400 + +# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up +# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN +# authentication and key handshake before actually associating with a new AP. +#rsn_preauth=1 +# +# Space separated list of interfaces from which pre-authentication frames are +# accepted (e.g., 'eth0' or 'eth0 wlan0wds0'. This list should include all +# interface that are used for connections to other APs. This could include +# wired interfaces and WDS links. The normal wireless data interface towards +# associated stations (e.g., wlan0) should not be added, since +# pre-authentication is only used with APs other than the currently associated +# one. +#rsn_preauth_interfaces=eth0 diff --git a/hostapd/README-MULTI-AP b/hostapd/README-MULTI-AP new file mode 100644 index 000000000000..ccee69e13c08 --- /dev/null +++ b/hostapd/README-MULTI-AP @@ -0,0 +1,160 @@ +hostapd, wpa_supplicant and the Multi-AP Specification +====================================================== + +This document describes how hostapd and wpa_supplicant can be configured to +support the Multi-AP Specification. + +Introduction to Multi-AP +------------------------ + +The Wi-Fi Alliance Multi-AP Specification is the technical specification for +Wi-Fi CERTIFIED EasyMesh(TM) [1], the Wi-Fi Alliance® certification program for +Multi-AP. It defines control protocols between Wi-Fi® access points (APs) to +join them into a network with centralized control and operation. It is targeted +only at routers (repeaters, gateways, ...), not at clients. Clients are not +involved at all in the protocols. + +Most of the Multi-AP specification falls outside of the scope of +hostapd/wpa_supplicant. hostapd/wpa_supplicant is only involved for the items +summarized below. The rest of the protocol must be implemented by a separate +daemon, e.g., prplMesh [2]. That daemon also needs to communicate with hostapd, +e.g., to get a list of associated clients, but this can be done using the normal +hostapd interfaces. + +hostapd/wpa_supplicant needs to be configured specifically to support: +- the WPS onboarding process; +- configuring backhaul links. + +The text below refers to "Multi-AP Specification v1.0" [3]. + + +Fronthaul and backhaul links +---------------------------- + +In a Multi-AP network, the central controller can configure the BSSs on the +devices that are joined into the network. These are called fronthaul BSSs. +From the point of view of hostapd, there is nothing special about these +fronthaul BSSs. + +In addition to fronthaul BSSs, the controller can also configure backhaul +links. A backhaul link is a link between two access point devices, giving +internet access to access point devices that don't have a wired link. The +Multi-AP specification doesn't dictate this, but typically the backhaul link +will be bridged into a LAN together with (one of) the fronthaul BSS(s) and the +wired Ethernet ports. + +A backhaul link must be treated specially by hostapd and wpa_supplicant. One +side of the backhaul link is configured through the Multi-AP protocol as the +"backhaul STA", i.e., the client side of the link. A backhaul STA is like any +station and is handled appropriately by wpa_supplicant, but two additional +features are required. It must send an additional information element in each +(Re)Association Request frame ([3], section 5.2, paragraph 4). In addition, it +must use 4-address mode for all frames sent over this link ([3], section 14). +Therefore, wpa_supplicant must be configured explicitly as the backhaul STA +role, by setting 'multi_ap_backhaul_sta=1' in the network configuration block +or when configuring the network profile through the control interface. When +'multi_ap_backhaul_sta=1', wpa_supplicant includes the Multi-AP IE in +(Re)Association Request frame and verifies that it is included in the +(Re)Association Response frame. If it is not, association fails. If it is, +wpa_supplicant sets 4-address mode for this interface through a driver +callback. + +The AP side of the backhaul link is called a "backhaul BSS". Such a BSS must +be handled specially by hostapd, because it must add an additional information +element in each (Re)Association Response frame, but only to stations that have +identified themselves as backhaul stations ([3], section 5.2, paragraph 5-6). +This is important because it is possible to use the same BSS and SSID for +fronthaul and backhaul at the same time. The additional information element must +only be used for frames sent to a backhaul STA, not to a normal STA. Also, +frames sent to a backhaul STA must use 4-address mode, while frames sent to a +normal STA (fronthaul, when it's a fronthaul and backhaul BSS) must use +3-address mode. + +A BSS is configured in Multi-AP mode in hostapd by setting the 'multi_ap' +configuration option to 1 (backhaul BSS), 2 (fronthaul BSS), or 3 +(simultaneous backhaul and fronthaul BSS). If this option is set, hostapd +parses the Multi-AP information element in the Association Request frame. If the +station is a backhaul STA and the BSS is configured as a backhaul BSS, +hostapd sets up 4-address mode. Since there may be multiple stations connected +simultaneously, and each of them has a different RA (receiver address), a VLAN +is created for each backhaul STA and it is automatically added to a bridge. +This is the same behavior as for WDS, and the relevant option ('bridge' or +'wds_bridge') applies here as well. + +If 'multi_ap' is 1 (backhaul BSS only), any station that tries to associate +without the Multi-AP information element will be denied. + +If 'multi_ap' is 2 (fronthaul BSS only), any station that tries to associate +with the Multi-AP information element will be denied. That is also the only +difference with 'multi_ap' set to 0: in the latter case, the Multi-AP +information element is simply ignored. + +In summary, this is the end-to-end behavior for a backhaul BSS (i.e., +multi_ap_backhaul_sta=1 in wpa_supplicant on STA, and multi_ap=1 or 3 in +hostapd on AP). Note that point 1 means that hostapd must not be configured +with WPS support on the backhaul BSS (multi_ap=1). hostapd does not check for +that. + +1. Backhaul BSS beacons do not advertise WPS support (other than that, nothing + Multi-AP specific). +2. STA sends Authentication frame (nothing Multi-AP specific). +3. AP sends Authentication frame (nothing Multi-AP specific). +4. STA sends Association Request frame with Multi-AP IE. +5. AP sends Association Response frame with Multi-AP IE. +6. STA and AP both use 4-address mode for Data frames. + + +WPS support +----------- + +WPS requires more special handling. WPS must only be advertised on fronthaul +BSSs, not on backhaul BSSs, so WPS should not be enabled on a backhaul-only +BSS in hostapd.conf. The WPS configuration purely works on the fronthaul BSS. +When a WPS M1 message has an additional subelement that indicates a request for +a Multi-AP backhaul link, hostapd must not respond with the normal fronthaul +BSS credentials; instead, it should respond with the (potentially different) +backhaul BSS credentials. + +To support this, hostapd has the 'multi_ap_backhaul_ssid', +'multi_ap_backhaul_wpa_psk' and 'multi_ap_backhaul_wpa_passphrase' options. +When these are set on an BSS with WPS, they are used instead of the normal +credentials when hostapd receives a WPS M1 message with the Multi-AP IE. Only +WPA2-Personal is supported in the Multi-AP specification, so there is no need +to specify authentication or encryption options. For the backhaul credentials, +per-device PSK is not supported. + +If the BSS is a simultaneous backhaul and fronthaul BSS, there is no need to +specify the backhaul credentials, since the backhaul and fronthaul credentials +are identical. + +To enable the Multi-AP backhaul STA feature when it performs WPS, a new +parameter has been introduced to the WPS_PBC control interface call. When this +"multi_ap=1" option is set, it adds the Multi-AP backhaul subelement to the +Association Request frame and the M1 message. It then configures the new network +profile with 'multi_ap_backhaul_sta=1'. Note that this means that if the AP does +not follow the Multi-AP specification, wpa_supplicant will fail to associate. + +In summary, this is the end-to-end behavior for WPS of a backhaul link (i.e., +multi_ap=1 option is given in the wps_pbc call on the STA side, and multi_ap=2 +and multi_ap_backhaul_ssid and either multi_ap_backhaul_wpa_psk or +multi_ap_backhaul_wpa_passphrase are set to the credentials of a backhaul BSS +in hostapd on Registrar AP). + +1. Fronthaul BSS Beacon frames advertise WPS support (nothing Multi-AP + specific). +2. Enrollee sends Authentication frame (nothing Multi-AP specific). +3. AP sends Authentication frame (nothing Multi-AP specific). +4. Enrollee sends Association Request frame with Multi-AP IE. +5. AP sends Association Response frame with Multi-AP IE. +6. Enrollee sends M1 with additional Multi-AP subelement. +7. AP sends M8 with backhaul instead of fronthaul credentials. +8. Enrollee sends Deauthentication frame. + + +References +---------- + +[1] https://www.wi-fi.org/discover-wi-fi/wi-fi-easymesh +[2] https://github.com/prplfoundation/prplMesh +[3] https://www.wi-fi.org/file/multi-ap-specification-v10 + (requires registration) diff --git a/hostapd/README-WPS b/hostapd/README-WPS new file mode 100644 index 000000000000..d5f713a8cb28 --- /dev/null +++ b/hostapd/README-WPS @@ -0,0 +1,352 @@ +hostapd and Wi-Fi Protected Setup (WPS) +======================================= + +This document describes how the WPS implementation in hostapd can be +configured and how an external component on an AP (e.g., web UI) is +used to enable enrollment of client devices. + + +Introduction to WPS +------------------- + +Wi-Fi Protected Setup (WPS) is a mechanism for easy configuration of a +wireless network. It allows automated generation of random keys (WPA +passphrase/PSK) and configuration of an access point and client +devices. WPS includes number of methods for setting up connections +with PIN method and push-button configuration (PBC) being the most +commonly deployed options. + +While WPS can enable more home networks to use encryption in the +wireless network, it should be noted that the use of the PIN and +especially PBC mechanisms for authenticating the initial key setup is +not very secure. As such, use of WPS may not be suitable for +environments that require secure network access without chance for +allowing outsiders to gain access during the setup phase. + +WPS uses following terms to describe the entities participating in the +network setup: +- access point: the WLAN access point +- Registrar: a device that control a network and can authorize + addition of new devices); this may be either in the AP ("internal + Registrar") or in an external device, e.g., a laptop, ("external + Registrar") +- Enrollee: a device that is being authorized to use the network + +It should also be noted that the AP and a client device may change +roles (i.e., AP acts as an Enrollee and client device as a Registrar) +when WPS is used to configure the access point. + + +More information about WPS is available from Wi-Fi Alliance: +http://www.wi-fi.org/wifi-protected-setup + + +hostapd implementation +---------------------- + +hostapd includes an optional WPS component that can be used as an +internal WPS Registrar to manage addition of new WPS enabled clients +to the network. In addition, WPS Enrollee functionality in hostapd can +be used to allow external WPS Registrars to configure the access +point, e.g., for initial network setup. In addition, hostapd can proxy a +WPS registration between a wireless Enrollee and an external Registrar +(e.g., Microsoft Vista or Atheros JumpStart) with UPnP. + + +hostapd configuration +--------------------- + +WPS is an optional component that needs to be enabled in hostapd build +configuration (.config). Here is an example configuration that +includes WPS support and uses nl80211 driver interface: + +CONFIG_DRIVER_NL80211=y +CONFIG_WPS=y +CONFIG_WPS_UPNP=y + +Following parameter can be used to enable support for NFC config method: + +CONFIG_WPS_NFC=y + + +Following section shows an example runtime configuration +(hostapd.conf) that enables WPS: + +# Configure the driver and network interface +driver=nl80211 +interface=wlan0 + +# WPA2-Personal configuration for the AP +ssid=wps-test +wpa=2 +wpa_key_mgmt=WPA-PSK +wpa_pairwise=CCMP +# Default WPA passphrase for legacy (non-WPS) clients +wpa_passphrase=12345678 +# Enable random per-device PSK generation for WPS clients +# Please note that the file has to exists for hostapd to start (i.e., create an +# empty file as a starting point). +wpa_psk_file=/etc/hostapd.psk + +# Enable control interface for PBC/PIN entry +ctrl_interface=/var/run/hostapd + +# Enable internal EAP server for EAP-WSC (part of Wi-Fi Protected Setup) +eap_server=1 + +# WPS configuration (AP configured, do not allow external WPS Registrars) +wps_state=2 +ap_setup_locked=1 +# If UUID is not configured, it will be generated based on local MAC address. +uuid=87654321-9abc-def0-1234-56789abc0000 +wps_pin_requests=/var/run/hostapd.pin-req +device_name=Wireless AP +manufacturer=Company +model_name=WAP +model_number=123 +serial_number=12345 +device_type=6-0050F204-1 +os_version=01020300 +config_methods=label display push_button keypad + +# if external Registrars are allowed, UPnP support could be added: +#upnp_iface=br0 +#friendly_name=WPS Access Point + + +External operations +------------------- + +WPS requires either a device PIN code (usually, 8-digit number) or a +pushbutton event (for PBC) to allow a new WPS Enrollee to join the +network. hostapd uses the control interface as an input channel for +these events. + +The PIN value used in the commands must be processed by an UI to +remove non-digit characters and potentially, to verify the checksum +digit. "hostapd_cli wps_check_pin " can be used to do such +processing. It returns FAIL if the PIN is invalid, or FAIL-CHECKSUM if +the checksum digit is incorrect, or the processed PIN (non-digit +characters removed) if the PIN is valid. + +When a client device (WPS Enrollee) connects to hostapd (WPS +Registrar) in order to start PIN mode negotiation for WPS, an +identifier (Enrollee UUID) is sent. hostapd will need to be configured +with a device password (PIN) for this Enrollee. This is an operation +that requires user interaction (assuming there are no pre-configured +PINs on the AP for a set of Enrollee). + +The PIN request with information about the device is appended to the +wps_pin_requests file (/var/run/hostapd.pin-req in this example). In +addition, hostapd control interface event is sent as a notification of +a new device. The AP could use, e.g., a web UI for showing active +Enrollees to the user and request a PIN for an Enrollee. + +The PIN request file has one line for every Enrollee that connected to +the AP, but for which there was no PIN. Following information is +provided for each Enrollee (separated with tabulators): +- timestamp (seconds from 1970-01-01) +- Enrollee UUID +- MAC address +- Device name +- Manufacturer +- Model Name +- Model Number +- Serial Number +- Device category + +Example line in the /var/run/hostapd.pin-req file: +1200188391 53b63a98-d29e-4457-a2ed-094d7e6a669c Intel(R) Centrino(R) Intel Corporation Intel(R) Centrino(R) - - 1-0050F204-1 + +Control interface data: +WPS-PIN-NEEDED [UUID-E|MAC Address|Device Name|Manufacturer|Model Name|Model Number|Serial Number|Device Category] +For example: +<2>WPS-PIN-NEEDED [53b63a98-d29e-4457-a2ed-094d7e6a669c|02:12:34:56:78:9a|Device|Manuf|Model|Model Number|Serial Number|1-0050F204-1] + +When the user enters a PIN for a pending Enrollee, e.g., on the web +UI), hostapd needs to be notified of the new PIN over the control +interface. This can be done either by using the UNIX domain socket +-based control interface directly (src/common/wpa_ctrl.c provides +helper functions for using the interface) or by calling hostapd_cli. + +Example command to add a PIN (12345670) for an Enrollee: + +hostapd_cli wps_pin 53b63a98-d29e-4457-a2ed-094d7e6a669c 12345670 + +If the UUID-E is not available (e.g., Enrollee waits for the Registrar +to be selected before connecting), wildcard UUID may be used to allow +the PIN to be used once with any UUID: + +hostapd_cli wps_pin any 12345670 + +To reduce likelihood of PIN being used with other devices or of +forgetting an active PIN available for potential attackers, expiration +time in seconds can be set for the new PIN (value 0 indicates no +expiration): + +hostapd_cli wps_pin any 12345670 300 + +If the MAC address of the enrollee is known, it should be configured +to allow the AP to advertise list of authorized enrollees: + +hostapd_cli wps_pin 53b63a98-d29e-4457-a2ed-094d7e6a669c \ + 12345670 300 00:11:22:33:44:55 + + +After this, the Enrollee can connect to the AP again and complete WPS +negotiation. At that point, a new, random WPA PSK is generated for the +client device and the client can then use that key to connect to the +AP to access the network. + + +If the AP includes a pushbutton, WPS PBC mode can be used. It is +enabled by pushing a button on both the AP and the client at about the +same time (2 minute window). hostapd needs to be notified about the AP +button pushed event over the control interface, e.g., by calling +hostapd_cli: + +hostapd_cli wps_pbc + +At this point, the client has two minutes to complete WPS negotiation +which will generate a new WPA PSK in the same way as the PIN method +described above. + + +When an external Registrar is used, the AP can act as an Enrollee and +use its AP PIN. A static AP PIN (e.g., one one a label in the AP +device) can be configured in hostapd.conf (ap_pin parameter). A more +secure option is to use hostapd_cli wps_ap_pin command to enable the +AP PIN only based on user action (and even better security by using a +random AP PIN for each session, i.e., by using "wps_ap_pin random" +command with a timeout value). Following commands are available for +managing the dynamic AP PIN operations: + +hostapd_cli wps_ap_pin disable +- disable AP PIN (i.e., do not allow external Registrars to use it to + learn the current AP settings or to reconfigure the AP) + +hostapd_cli wps_ap_pin random [timeout] +- generate a random AP PIN and enable it +- if the optional timeout parameter is given, the AP PIN will be enabled + for the specified number of seconds + +hostapd_cli wps_ap_pin get +- fetch the current AP PIN + +hostapd_cli wps_ap_pin set [timeout] +- set the AP PIN and enable it +- if the optional timeout parameter is given, the AP PIN will be enabled + for the specified number of seconds + +hostapd_cli get_config +- display the current configuration + +hostapd_cli wps_config +examples: + hostapd_cli wps_config testing WPA2PSK CCMP 12345678 + hostapd_cli wps_config "no security" OPEN NONE "" + + must be one of the following: OPEN WPAPSK WPA2PSK + must be one of the following: NONE WEP TKIP CCMP + + +Credential generation and configuration changes +----------------------------------------------- + +By default, hostapd generates credentials for Enrollees and processing +AP configuration updates internally. However, it is possible to +control these operations from external programs, if desired. + +The internal credential generation can be disabled with +skip_cred_build=1 option in the configuration. extra_cred option will +then need to be used to provide pre-configured Credential attribute(s) +for hostapd to use. The exact data from this binary file will be sent, +i.e., it will have to include valid WPS attributes. extra_cred can +also be used to add additional networks if the Registrar is used to +configure credentials for multiple networks. + +Processing of received configuration updates can be disabled with +wps_cred_processing=1 option. When this is used, an external program +is responsible for creating hostapd configuration files and processing +configuration updates based on messages received from hostapd over +control interface. This will also include the initial configuration on +first successful registration if the AP is initially set in +unconfigured state. + +Following control interface messages are sent out for external programs: + +WPS-REG-SUCCESS +For example: +<2>WPS-REG-SUCCESS 02:66:a0:ee:17:27 2b7093f1-d6fb-5108-adbb-bea66bb87333 + +This can be used to trigger change from unconfigured to configured +state (random configuration based on the first successful WPS +registration). In addition, this can be used to update AP UI about the +status of WPS registration progress. + + +WPS-NEW-AP-SETTINGS +For example: +<2>WPS-NEW-AP-SETTINGS 10260001011045000c6a6b6d2d7770732d74657374100300020020100f00020008102700403065346230343536633236366665306433396164313535346131663462663731323433376163666462376633393965353466316631623032306164343438623510200006024231cede15101e000844 + +This can be used to update the externally stored AP configuration and +then update hostapd configuration (followed by restarting of hostapd). + + +WPS with NFC +------------ + +WPS can be used with NFC-based configuration method. An NFC tag +containing a password token from the Enrollee can be used to +authenticate the connection instead of the PIN. In addition, an NFC tag +with a configuration token can be used to transfer AP settings without +going through the WPS protocol. + +When the AP acts as an Enrollee, a local NFC tag with a password token +can be used by touching the NFC interface of an external Registrar. The +wps_nfc_token command is used to manage use of the NFC password token +from the AP. "wps_nfc_token enable" enables the use of the AP's NFC +password token (in place of AP PIN) and "wps_nfc_token disable" disables +the NFC password token. + +The NFC password token that is either pre-configured in the +configuration file (wps_nfc_dev_pw_id, wps_nfc_dh_pubkey, +wps_nfc_dh_privkey, wps_nfc_dev_pw) or generated dynamically with +"wps_nfc_token " command. The nfc_pw_token tool from +wpa_supplicant can be used to generate NFC password tokens during +manufacturing (each AP needs to have its own random keys). + +The "wps_nfc_config_token " command can be used to build an +NFC configuration token. The output value from this command is a hexdump +of the current AP configuration (WPS parameter requests this to include +only the WPS attributes; NDEF parameter requests additional NDEF +encapsulation to be included). This data needs to be written to an NFC +tag with an external program. Once written, the NFC configuration token +can be used to touch an NFC interface on a station to provision the +credentials needed to access the network. + +When the NFC device on the AP reads an NFC tag with a MIME media type +"application/vnd.wfa.wsc", the NDEF message payload (with or without +NDEF encapsulation) can be delivered to hostapd using the +following hostapd_cli command: + +wps_nfc_tag_read + +If the NFC tag contains a password token, the token is added to the +internal Registrar. This allows station Enrollee from which the password +token was received to run through WPS protocol to provision the +credential. + +"nfc_get_handover_sel " command can be used to build the +contents of a Handover Select Message for connection handover when this +does not depend on the contents of the Handover Request Message. The +first argument selects the format of the output data and the second +argument selects which type of connection handover is requested (WPS = +Wi-Fi handover as specified in WSC 2.0). + +"nfc_report_handover WPS +" is used to report completed NFC +connection handover. The first parameter indicates whether the local +device initiated or responded to the connection handover and the carrier +records are the selected carrier from the handover request and select +messages as a hexdump. diff --git a/hostapd/android.config b/hostapd/android.config new file mode 100644 index 000000000000..522de87266d5 --- /dev/null +++ b/hostapd/android.config @@ -0,0 +1,220 @@ +# 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 + +# Include support fo RADIUS/TLS into the RADIUS client +#CONFIG_RADIUS_TLS=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 + +# Wi-Fi Aware unsynchronized service discovery (NAN USD) +#CONFIG_NAN_USD=y diff --git a/hostapd/config_file.c b/hostapd/config_file.c new file mode 100644 index 000000000000..3fb059770d49 --- /dev/null +++ b/hostapd/config_file.c @@ -0,0 +1,5226 @@ +/* + * hostapd / Configuration file parser + * Copyright (c) 2003-2024, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#ifndef CONFIG_NATIVE_WINDOWS +#include +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "utils/common.h" +#include "utils/uuid.h" +#include "utils/crc32.h" +#include "common/ieee802_11_defs.h" +#include "common/sae.h" +#include "crypto/sha256.h" +#include "crypto/tls.h" +#include "drivers/driver.h" +#include "eap_server/eap.h" +#include "radius/radius_client.h" +#include "ap/wpa_auth.h" +#include "ap/ap_config.h" +#include "config_file.h" + + +#ifndef CONFIG_NO_VLAN +static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss, + const char *fname) +{ + FILE *f; + char buf[128], *pos, *pos2, *pos3; + int line = 0, vlan_id; + struct hostapd_vlan *vlan; + + f = fopen(fname, "r"); + if (!f) { + wpa_printf(MSG_ERROR, "VLAN file '%s' not readable.", fname); + return -1; + } + + while (fgets(buf, sizeof(buf), f)) { + line++; + + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + if (buf[0] == '\0') + continue; + + if (buf[0] == '*') { + vlan_id = VLAN_ID_WILDCARD; + pos = buf + 1; + } else { + vlan_id = strtol(buf, &pos, 10); + if (buf == pos || vlan_id < 1 || + vlan_id > MAX_VLAN_ID) { + wpa_printf(MSG_ERROR, "Invalid VLAN ID at " + "line %d in '%s'", line, fname); + fclose(f); + return -1; + } + } + + while (*pos == ' ' || *pos == '\t') + pos++; + pos2 = pos; + while (*pos2 != ' ' && *pos2 != '\t' && *pos2 != '\0') + pos2++; + + if (*pos2 != '\0') + *(pos2++) = '\0'; + + if (*pos == '\0' || os_strlen(pos) > IFNAMSIZ) { + wpa_printf(MSG_ERROR, "Invalid VLAN ifname at line %d " + "in '%s'", line, fname); + fclose(f); + return -1; + } + + while (*pos2 == ' ' || *pos2 == '\t') + pos2++; + pos3 = pos2; + while (*pos3 != ' ' && *pos3 != '\t' && *pos3 != '\0') + pos3++; + *pos3 = '\0'; + + vlan = os_zalloc(sizeof(*vlan)); + if (vlan == NULL) { + wpa_printf(MSG_ERROR, "Out of memory while reading " + "VLAN interfaces from '%s'", fname); + fclose(f); + return -1; + } + + vlan->vlan_id = vlan_id; + vlan->vlan_desc.untagged = vlan_id; + vlan->vlan_desc.notempty = !!vlan_id; + os_strlcpy(vlan->ifname, pos, sizeof(vlan->ifname)); + os_strlcpy(vlan->bridge, pos2, sizeof(vlan->bridge)); + vlan->next = bss->vlan; + bss->vlan = vlan; + } + + fclose(f); + + return 0; +} +#endif /* CONFIG_NO_VLAN */ + + +static int hostapd_config_read_maclist(const char *fname, + struct mac_acl_entry **acl, int *num) +{ + FILE *f; + char buf[128], *pos; + int line = 0; + u8 addr[ETH_ALEN]; + int vlan_id; + + f = fopen(fname, "r"); + if (!f) { + wpa_printf(MSG_ERROR, "MAC list file '%s' not found.", fname); + return -1; + } + + while (fgets(buf, sizeof(buf), f)) { + int rem = 0; + + line++; + + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + if (buf[0] == '\0') + continue; + pos = buf; + if (buf[0] == '-') { + rem = 1; + pos++; + } + + if (hwaddr_aton(pos, addr)) { + wpa_printf(MSG_ERROR, "Invalid MAC address '%s' at " + "line %d in '%s'", pos, line, fname); + fclose(f); + return -1; + } + + if (rem) { + hostapd_remove_acl_mac(acl, num, addr); + continue; + } + vlan_id = 0; + pos = buf; + while (*pos != '\0' && *pos != ' ' && *pos != '\t') + pos++; + while (*pos == ' ' || *pos == '\t') + pos++; + if (*pos != '\0') + vlan_id = atoi(pos); + + if (hostapd_add_acl_maclist(acl, num, vlan_id, addr) < 0) { + fclose(f); + return -1; + } + } + + fclose(f); + + if (*acl) + qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp); + + return 0; +} + + +#ifdef EAP_SERVER + +static int hostapd_config_eap_user_salted(struct hostapd_eap_user *user, + const char *hash, size_t len, + char **pos, int line, + const char *fname) +{ + char *pos2 = *pos; + + while (*pos2 != '\0' && *pos2 != ' ' && *pos2 != '\t' && *pos2 != '#') + pos2++; + + if (pos2 - *pos < (int) (2 * (len + 1))) { /* at least 1 byte of salt */ + wpa_printf(MSG_ERROR, + "Invalid salted %s hash on line %d in '%s'", + hash, line, fname); + return -1; + } + + user->password = os_malloc(len); + if (!user->password) { + wpa_printf(MSG_ERROR, + "Failed to allocate memory for salted %s hash", + hash); + return -1; + } + + if (hexstr2bin(*pos, user->password, len) < 0) { + wpa_printf(MSG_ERROR, + "Invalid salted password on line %d in '%s'", + line, fname); + return -1; + } + user->password_len = len; + *pos += 2 * len; + + user->salt_len = (pos2 - *pos) / 2; + user->salt = os_malloc(user->salt_len); + if (!user->salt) { + wpa_printf(MSG_ERROR, + "Failed to allocate memory for salted %s hash", + hash); + return -1; + } + + if (hexstr2bin(*pos, user->salt, user->salt_len) < 0) { + wpa_printf(MSG_ERROR, + "Invalid salt for password on line %d in '%s'", + line, fname); + return -1; + } + + *pos = pos2; + return 0; +} + + +static int hostapd_config_read_eap_user(const char *fname, + struct hostapd_bss_config *conf) +{ + FILE *f; + char buf[512], *pos, *start, *pos2; + int line = 0, ret = 0, num_methods; + struct hostapd_eap_user *user = NULL, *tail = NULL, *new_user = NULL; + + if (os_strncmp(fname, "sqlite:", 7) == 0) { +#ifdef CONFIG_SQLITE + os_free(conf->eap_user_sqlite); + conf->eap_user_sqlite = os_strdup(fname + 7); + return 0; +#else /* CONFIG_SQLITE */ + wpa_printf(MSG_ERROR, + "EAP user file in SQLite DB, but CONFIG_SQLITE was not enabled in the build."); + return -1; +#endif /* CONFIG_SQLITE */ + } + + f = fopen(fname, "r"); + if (!f) { + wpa_printf(MSG_ERROR, "EAP user file '%s' not found.", fname); + return -1; + } + + /* Lines: "user" METHOD,METHOD2 "password" (password optional) */ + while (fgets(buf, sizeof(buf), f)) { + line++; + + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + if (buf[0] == '\0') + continue; + +#ifndef CONFIG_NO_RADIUS + if (user && os_strncmp(buf, "radius_accept_attr=", 19) == 0) { + struct hostapd_radius_attr *attr, *a; + attr = hostapd_parse_radius_attr(buf + 19); + if (attr == NULL) { + wpa_printf(MSG_ERROR, "Invalid radius_accept_attr: %s", + buf + 19); + user = NULL; /* already in the BSS list */ + goto failed; + } + if (user->accept_attr == NULL) { + user->accept_attr = attr; + } else { + a = user->accept_attr; + while (a->next) + a = a->next; + a->next = attr; + } + continue; + } +#endif /* CONFIG_NO_RADIUS */ + + user = NULL; + + if (buf[0] != '"' && buf[0] != '*') { + wpa_printf(MSG_ERROR, "Invalid EAP identity (no \" in " + "start) on line %d in '%s'", line, fname); + goto failed; + } + + user = os_zalloc(sizeof(*user)); + if (user == NULL) { + wpa_printf(MSG_ERROR, "EAP user allocation failed"); + goto failed; + } + user->force_version = -1; + + if (buf[0] == '*') { + pos = buf; + } else { + pos = buf + 1; + start = pos; + while (*pos != '"' && *pos != '\0') + pos++; + if (*pos == '\0') { + wpa_printf(MSG_ERROR, "Invalid EAP identity " + "(no \" in end) on line %d in '%s'", + line, fname); + goto failed; + } + + user->identity = os_memdup(start, pos - start); + if (user->identity == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate " + "memory for EAP identity"); + goto failed; + } + user->identity_len = pos - start; + + if (pos[0] == '"' && pos[1] == '*') { + user->wildcard_prefix = 1; + pos++; + } + } + pos++; + while (*pos == ' ' || *pos == '\t') + pos++; + + if (*pos == '\0') { + wpa_printf(MSG_ERROR, "No EAP method on line %d in " + "'%s'", line, fname); + goto failed; + } + + start = pos; + while (*pos != ' ' && *pos != '\t' && *pos != '\0') + pos++; + if (*pos == '\0') { + pos = NULL; + } else { + *pos = '\0'; + pos++; + } + num_methods = 0; + while (*start) { + char *pos3 = os_strchr(start, ','); + if (pos3) { + *pos3++ = '\0'; + } + user->methods[num_methods].method = + eap_server_get_type( + start, + &user->methods[num_methods].vendor); + if (user->methods[num_methods].vendor == + EAP_VENDOR_IETF && + user->methods[num_methods].method == EAP_TYPE_NONE) + { + if (os_strcmp(start, "TTLS-PAP") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_PAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-CHAP") == 0) { + user->ttls_auth |= EAP_TTLS_AUTH_CHAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-MSCHAP") == 0) { + user->ttls_auth |= + EAP_TTLS_AUTH_MSCHAP; + goto skip_eap; + } + if (os_strcmp(start, "TTLS-MSCHAPV2") == 0) { + user->ttls_auth |= + EAP_TTLS_AUTH_MSCHAPV2; + goto skip_eap; + } + if (os_strcmp(start, "MACACL") == 0) { + user->macacl = 1; + goto skip_eap; + } + wpa_printf(MSG_ERROR, "Unsupported EAP type " + "'%s' on line %d in '%s'", + start, line, fname); + goto failed; + } + + num_methods++; + if (num_methods >= EAP_MAX_METHODS) + break; + skip_eap: + if (pos3 == NULL) + break; + start = pos3; + } + if (num_methods == 0 && user->ttls_auth == 0 && !user->macacl) { + wpa_printf(MSG_ERROR, "No EAP types configured on " + "line %d in '%s'", line, fname); + goto failed; + } + + if (pos == NULL) + goto done; + + while (*pos == ' ' || *pos == '\t') + pos++; + if (*pos == '\0') + goto done; + + if (os_strncmp(pos, "[ver=0]", 7) == 0) { + user->force_version = 0; + goto done; + } + + if (os_strncmp(pos, "[ver=1]", 7) == 0) { + user->force_version = 1; + goto done; + } + + if (os_strncmp(pos, "[2]", 3) == 0) { + user->phase2 = 1; + goto done; + } + + if (*pos == '"') { + pos++; + start = pos; + while (*pos != '"' && *pos != '\0') + pos++; + if (*pos == '\0') { + wpa_printf(MSG_ERROR, "Invalid EAP password " + "(no \" in end) on line %d in '%s'", + line, fname); + goto failed; + } + + user->password = os_memdup(start, pos - start); + if (user->password == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate " + "memory for EAP password"); + goto failed; + } + user->password_len = pos - start; + + pos++; + } else if (os_strncmp(pos, "hash:", 5) == 0) { + pos += 5; + pos2 = pos; + while (*pos2 != '\0' && *pos2 != ' ' && + *pos2 != '\t' && *pos2 != '#') + pos2++; + if (pos2 - pos != 32) { + wpa_printf(MSG_ERROR, "Invalid password hash " + "on line %d in '%s'", line, fname); + goto failed; + } + user->password = os_malloc(16); + if (user->password == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate " + "memory for EAP password hash"); + goto failed; + } + if (hexstr2bin(pos, user->password, 16) < 0) { + wpa_printf(MSG_ERROR, "Invalid hash password " + "on line %d in '%s'", line, fname); + goto failed; + } + user->password_len = 16; + user->password_hash = 1; + pos = pos2; + } else if (os_strncmp(pos, "ssha1:", 6) == 0) { + pos += 6; + if (hostapd_config_eap_user_salted(user, "sha1", 20, + &pos, + line, fname) < 0) + goto failed; + } else if (os_strncmp(pos, "ssha256:", 8) == 0) { + pos += 8; + if (hostapd_config_eap_user_salted(user, "sha256", 32, + &pos, + line, fname) < 0) + goto failed; + } else if (os_strncmp(pos, "ssha512:", 8) == 0) { + pos += 8; + if (hostapd_config_eap_user_salted(user, "sha512", 64, + &pos, + line, fname) < 0) + goto failed; + } else { + pos2 = pos; + while (*pos2 != '\0' && *pos2 != ' ' && + *pos2 != '\t' && *pos2 != '#') + pos2++; + if ((pos2 - pos) & 1) { + wpa_printf(MSG_ERROR, "Invalid hex password " + "on line %d in '%s'", line, fname); + goto failed; + } + user->password = os_malloc((pos2 - pos) / 2); + if (user->password == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate " + "memory for EAP password"); + goto failed; + } + if (hexstr2bin(pos, user->password, + (pos2 - pos) / 2) < 0) { + wpa_printf(MSG_ERROR, "Invalid hex password " + "on line %d in '%s'", line, fname); + goto failed; + } + user->password_len = (pos2 - pos) / 2; + pos = pos2; + } + + while (*pos == ' ' || *pos == '\t') + pos++; + if (os_strncmp(pos, "[2]", 3) == 0) { + user->phase2 = 1; + } + + done: + if (tail == NULL) { + tail = new_user = user; + } else { + tail->next = user; + tail = user; + } + continue; + + failed: + if (user) + hostapd_config_free_eap_user(user); + ret = -1; + break; + } + + fclose(f); + + if (ret == 0) { + hostapd_config_free_eap_users(conf->eap_user); + conf->eap_user = new_user; + } else { + hostapd_config_free_eap_users(new_user); + } + + return ret; +} + +#endif /* EAP_SERVER */ + + +#ifndef CONFIG_NO_RADIUS +static int +hostapd_config_read_radius_addr(struct hostapd_radius_server **server, + int *num_server, const char *val, int def_port, + struct hostapd_radius_server **curr_serv) +{ + struct hostapd_radius_server *nserv; + int ret; + static int server_index = 1; + + nserv = os_realloc_array(*server, *num_server + 1, sizeof(*nserv)); + if (nserv == NULL) + return -1; + + *server = nserv; + nserv = &nserv[*num_server]; + (*num_server)++; + (*curr_serv) = nserv; + + os_memset(nserv, 0, sizeof(*nserv)); + nserv->port = def_port; + ret = hostapd_parse_ip_addr(val, &nserv->addr); + nserv->index = server_index++; + + return ret; +} + + + +static int hostapd_parse_das_client(struct hostapd_bss_config *bss, char *val) +{ + char *secret; + + secret = os_strchr(val, ' '); + if (secret == NULL) + return -1; + + *secret++ = '\0'; + + if (hostapd_parse_ip_addr(val, &bss->radius_das_client_addr)) + return -1; + + os_free(bss->radius_das_shared_secret); + bss->radius_das_shared_secret = (u8 *) os_strdup(secret); + if (bss->radius_das_shared_secret == NULL) + return -1; + bss->radius_das_shared_secret_len = os_strlen(secret); + + return 0; +} +#endif /* CONFIG_NO_RADIUS */ + + +static int hostapd_config_parse_key_mgmt(int line, const char *value) +{ + int val = 0, last; + char *start, *end, *buf; + + buf = os_strdup(value); + if (buf == NULL) + return -1; + start = buf; + + while (*start != '\0') { + while (*start == ' ' || *start == '\t') + start++; + if (*start == '\0') + break; + end = start; + while (*end != ' ' && *end != '\t' && *end != '\0') + end++; + last = *end == '\0'; + *end = '\0'; + if (os_strcmp(start, "WPA-PSK") == 0) + val |= WPA_KEY_MGMT_PSK; + else if (os_strcmp(start, "WPA-EAP") == 0) + val |= WPA_KEY_MGMT_IEEE8021X; +#ifdef CONFIG_IEEE80211R_AP + else if (os_strcmp(start, "FT-PSK") == 0) + val |= WPA_KEY_MGMT_FT_PSK; + else if (os_strcmp(start, "FT-EAP") == 0) + val |= WPA_KEY_MGMT_FT_IEEE8021X; +#ifdef CONFIG_SHA384 + else if (os_strcmp(start, "FT-EAP-SHA384") == 0) + val |= WPA_KEY_MGMT_FT_IEEE8021X_SHA384; +#endif /* CONFIG_SHA384 */ +#endif /* CONFIG_IEEE80211R_AP */ +#ifdef CONFIG_SHA384 + else if (os_strcmp(start, "WPA-EAP-SHA384") == 0) + val |= WPA_KEY_MGMT_IEEE8021X_SHA384; +#endif /* CONFIG_SHA384 */ + 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; +#ifdef CONFIG_SAE + else if (os_strcmp(start, "SAE") == 0) + val |= WPA_KEY_MGMT_SAE; + else if (os_strcmp(start, "SAE-EXT-KEY") == 0) + val |= WPA_KEY_MGMT_SAE_EXT_KEY; + else if (os_strcmp(start, "FT-SAE") == 0) + val |= WPA_KEY_MGMT_FT_SAE; + else if (os_strcmp(start, "FT-SAE-EXT-KEY") == 0) + val |= WPA_KEY_MGMT_FT_SAE_EXT_KEY; +#endif /* CONFIG_SAE */ +#ifdef CONFIG_SUITEB + else if (os_strcmp(start, "WPA-EAP-SUITE-B") == 0) + val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B; +#endif /* CONFIG_SUITEB */ +#ifdef CONFIG_SUITEB192 + else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0) + val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192; +#endif /* CONFIG_SUITEB192 */ +#ifdef CONFIG_FILS + else if (os_strcmp(start, "FILS-SHA256") == 0) + val |= WPA_KEY_MGMT_FILS_SHA256; + else if (os_strcmp(start, "FILS-SHA384") == 0) + val |= WPA_KEY_MGMT_FILS_SHA384; +#ifdef CONFIG_IEEE80211R_AP + else if (os_strcmp(start, "FT-FILS-SHA256") == 0) + val |= WPA_KEY_MGMT_FT_FILS_SHA256; + else if (os_strcmp(start, "FT-FILS-SHA384") == 0) + val |= WPA_KEY_MGMT_FT_FILS_SHA384; +#endif /* CONFIG_IEEE80211R_AP */ +#endif /* CONFIG_FILS */ +#ifdef CONFIG_OWE + else if (os_strcmp(start, "OWE") == 0) + val |= WPA_KEY_MGMT_OWE; +#endif /* CONFIG_OWE */ +#ifdef CONFIG_DPP + else if (os_strcmp(start, "DPP") == 0) + val |= WPA_KEY_MGMT_DPP; +#endif /* CONFIG_DPP */ +#ifdef CONFIG_HS20 + else if (os_strcmp(start, "OSEN") == 0) + val |= WPA_KEY_MGMT_OSEN; +#endif /* CONFIG_HS20 */ +#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); + os_free(buf); + return -1; + } + + if (last) + break; + start = end + 1; + } + + os_free(buf); + if (val == 0) { + wpa_printf(MSG_ERROR, "Line %d: no key_mgmt values " + "configured.", line); + return -1; + } + + return val; +} + + +static int hostapd_config_parse_cipher(int line, const char *value) +{ + int val = wpa_parse_cipher(value); + if (val < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.", + line, value); + return -1; + } + if (val == 0) { + wpa_printf(MSG_ERROR, "Line %d: no cipher values configured.", + line); + return -1; + } + return val; +} + + +#ifdef CONFIG_WEP +static int hostapd_config_read_wep(struct hostapd_wep_keys *wep, int keyidx, + char *val) +{ + size_t len = os_strlen(val); + + if (keyidx < 0 || keyidx > 3) + return -1; + + if (len == 0) { + int i, set = 0; + + bin_clear_free(wep->key[keyidx], wep->len[keyidx]); + wep->key[keyidx] = NULL; + wep->len[keyidx] = 0; + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (wep->key[i]) + set++; + } + if (!set) + wep->keys_set = 0; + return 0; + } + + if (wep->key[keyidx] != NULL) + return -1; + + if (val[0] == '"') { + if (len < 2 || val[len - 1] != '"') + return -1; + len -= 2; + wep->key[keyidx] = os_memdup(val + 1, len); + if (wep->key[keyidx] == NULL) + return -1; + wep->len[keyidx] = len; + } else { + if (len & 1) + return -1; + len /= 2; + wep->key[keyidx] = os_malloc(len); + if (wep->key[keyidx] == NULL) + return -1; + wep->len[keyidx] = len; + if (hexstr2bin(val, wep->key[keyidx], len) < 0) + return -1; + } + + wep->keys_set++; + + return 0; +} +#endif /* CONFIG_WEP */ + + +static int hostapd_parse_chanlist(struct hostapd_config *conf, char *val) +{ + char *pos; + + /* for backwards compatibility, translate ' ' in conf str to ',' */ + pos = val; + while (pos) { + pos = os_strchr(pos, ' '); + if (pos) + *pos++ = ','; + } + if (freq_range_list_parse(&conf->acs_ch_list, val)) + return -1; + + return 0; +} + + +static int hostapd_parse_intlist(int **int_list, char *val) +{ + int *list; + int count; + char *pos, *end; + + os_free(*int_list); + *int_list = NULL; + + pos = val; + count = 0; + while (*pos != '\0') { + if (*pos == ' ') + count++; + pos++; + } + + list = os_malloc(sizeof(int) * (count + 2)); + if (list == NULL) + return -1; + pos = val; + count = 0; + while (*pos != '\0') { + end = os_strchr(pos, ' '); + if (end) + *end = '\0'; + + list[count++] = atoi(pos); + if (!end) + break; + pos = end + 1; + } + list[count] = -1; + + *int_list = list; + return 0; +} + + +static int hostapd_config_bss(struct hostapd_config *conf, const char *ifname) +{ + struct hostapd_bss_config **all, *bss; + + if (*ifname == '\0') + return -1; + + all = os_realloc_array(conf->bss, conf->num_bss + 1, + sizeof(struct hostapd_bss_config *)); + if (all == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate memory for " + "multi-BSS entry"); + return -1; + } + conf->bss = all; + + bss = os_zalloc(sizeof(*bss)); + if (bss == NULL) + return -1; + bss->radius = os_zalloc(sizeof(*bss->radius)); + if (bss->radius == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate memory for " + "multi-BSS RADIUS data"); + os_free(bss); + return -1; + } + + conf->bss[conf->num_bss++] = bss; + conf->last_bss = bss; + + hostapd_config_defaults_bss(bss); + os_strlcpy(bss->iface, ifname, sizeof(bss->iface)); + os_memcpy(bss->ssid.vlan, bss->iface, IFNAMSIZ + 1); + + return 0; +} + + +#ifdef CONFIG_IEEE80211R_AP + +static int rkh_derive_key(const char *pos, u8 *key, size_t key_len) +{ + u8 oldkey[16]; + int ret; + + if (!hexstr2bin(pos, key, key_len)) + return 0; + + /* Try to use old short key for backwards compatibility */ + if (hexstr2bin(pos, oldkey, sizeof(oldkey))) + return -1; + + ret = hmac_sha256_kdf(oldkey, sizeof(oldkey), "FT OLDKEY", NULL, 0, + key, key_len); + os_memset(oldkey, 0, sizeof(oldkey)); + return ret; +} + + +static int add_r0kh(struct hostapd_bss_config *bss, char *value) +{ + struct ft_remote_r0kh *r0kh; + char *pos, *next; + + r0kh = os_zalloc(sizeof(*r0kh)); + if (r0kh == NULL) + return -1; + + /* 02:01:02:03:04:05 a.example.com 000102030405060708090a0b0c0d0e0f */ + pos = value; + next = os_strchr(pos, ' '); + if (next) + *next++ = '\0'; + if (next == NULL || hwaddr_aton(pos, r0kh->addr)) { + wpa_printf(MSG_ERROR, "Invalid R0KH MAC address: '%s'", pos); + os_free(r0kh); + return -1; + } + + pos = next; + next = os_strchr(pos, ' '); + if (next) + *next++ = '\0'; + if (next == NULL || next - pos > FT_R0KH_ID_MAX_LEN) { + wpa_printf(MSG_ERROR, "Invalid R0KH-ID: '%s'", pos); + os_free(r0kh); + return -1; + } + r0kh->id_len = next - pos - 1; + os_memcpy(r0kh->id, pos, r0kh->id_len); + + pos = next; + if (rkh_derive_key(pos, r0kh->key, sizeof(r0kh->key)) < 0) { + wpa_printf(MSG_ERROR, "Invalid R0KH key: '%s'", pos); + os_free(r0kh); + return -1; + } + + r0kh->next = bss->r0kh_list; + bss->r0kh_list = r0kh; + + return 0; +} + + +static int add_r1kh(struct hostapd_bss_config *bss, char *value) +{ + struct ft_remote_r1kh *r1kh; + char *pos, *next; + + r1kh = os_zalloc(sizeof(*r1kh)); + if (r1kh == NULL) + return -1; + + /* 02:01:02:03:04:05 02:01:02:03:04:05 + * 000102030405060708090a0b0c0d0e0f */ + pos = value; + next = os_strchr(pos, ' '); + if (next) + *next++ = '\0'; + if (next == NULL || hwaddr_aton(pos, r1kh->addr)) { + wpa_printf(MSG_ERROR, "Invalid R1KH MAC address: '%s'", pos); + os_free(r1kh); + return -1; + } + + pos = next; + next = os_strchr(pos, ' '); + if (next) + *next++ = '\0'; + if (next == NULL || hwaddr_aton(pos, r1kh->id)) { + wpa_printf(MSG_ERROR, "Invalid R1KH-ID: '%s'", pos); + os_free(r1kh); + return -1; + } + + pos = next; + if (rkh_derive_key(pos, r1kh->key, sizeof(r1kh->key)) < 0) { + wpa_printf(MSG_ERROR, "Invalid R1KH key: '%s'", pos); + os_free(r1kh); + return -1; + } + + r1kh->next = bss->r1kh_list; + bss->r1kh_list = r1kh; + + return 0; +} + + +int hostapd_config_read_rxkh_file(struct hostapd_bss_config *conf, + const char *fname) +{ + FILE *f; + char buf[256], *pos; + int line = 0, errors = 0; + + if (!fname) + return 0; + + f = fopen(fname, "r"); + if (!f) { + wpa_printf(MSG_ERROR, "rxkh file '%s' not found.", fname); + return -1; + } + + while (fgets(buf, sizeof(buf), f)) { + line++; + + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + if (buf[0] == '\0') + continue; + + pos = os_strchr(buf, '='); + if (!pos) { + wpa_printf(MSG_ERROR, "Line %d: Invalid line '%s'", + line, buf); + errors++; + continue; + } + *pos = '\0'; + pos++; + + if (os_strcmp(buf, "r0kh") == 0) { + if (add_r0kh(conf, pos) < 0) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid r0kh '%s'", + line, pos); + errors++; + } + } else if (os_strcmp(buf, "r1kh") == 0) { + if (add_r1kh(conf, pos) < 0) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid r1kh '%s'", + line, pos); + errors++; + } + } + } + + fclose(f); + + if (errors) { + wpa_printf(MSG_ERROR, + "%d errors in configuring RxKHs from '%s'", + errors, fname); + return -1; + } + return 0; +} + +#endif /* CONFIG_IEEE80211R_AP */ + + +static int hostapd_config_ht_capab(struct hostapd_config *conf, + const char *capab) +{ + if (os_strstr(capab, "[LDPC]")) + conf->ht_capab |= HT_CAP_INFO_LDPC_CODING_CAP; + if (os_strstr(capab, "[HT40-]")) { + conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; + conf->secondary_channel = -1; + } + if (os_strstr(capab, "[HT40+]")) { + conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; + conf->secondary_channel = 1; + } + if (os_strstr(capab, "[HT40+]") && os_strstr(capab, "[HT40-]")) { + conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; + conf->ht40_plus_minus_allowed = 1; + } + if (!os_strstr(capab, "[HT40+]") && !os_strstr(capab, "[HT40-]")) + conf->secondary_channel = 0; + if (os_strstr(capab, "[GF]")) + conf->ht_capab |= HT_CAP_INFO_GREEN_FIELD; + if (os_strstr(capab, "[SHORT-GI-20]")) + conf->ht_capab |= HT_CAP_INFO_SHORT_GI20MHZ; + if (os_strstr(capab, "[SHORT-GI-40]")) + conf->ht_capab |= HT_CAP_INFO_SHORT_GI40MHZ; + if (os_strstr(capab, "[TX-STBC]")) + conf->ht_capab |= HT_CAP_INFO_TX_STBC; + if (os_strstr(capab, "[RX-STBC1]")) { + conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK; + conf->ht_capab |= HT_CAP_INFO_RX_STBC_1; + } + if (os_strstr(capab, "[RX-STBC12]")) { + conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK; + conf->ht_capab |= HT_CAP_INFO_RX_STBC_12; + } + if (os_strstr(capab, "[RX-STBC123]")) { + conf->ht_capab &= ~HT_CAP_INFO_RX_STBC_MASK; + conf->ht_capab |= HT_CAP_INFO_RX_STBC_123; + } + if (os_strstr(capab, "[DELAYED-BA]")) + conf->ht_capab |= HT_CAP_INFO_DELAYED_BA; + if (os_strstr(capab, "[MAX-AMSDU-7935]")) + conf->ht_capab |= HT_CAP_INFO_MAX_AMSDU_SIZE; + if (os_strstr(capab, "[DSSS_CCK-40]")) + conf->ht_capab |= HT_CAP_INFO_DSSS_CCK40MHZ; + if (os_strstr(capab, "[40-INTOLERANT]")) + conf->ht_capab |= HT_CAP_INFO_40MHZ_INTOLERANT; + if (os_strstr(capab, "[LSIG-TXOP-PROT]")) + conf->ht_capab |= HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT; + + return 0; +} + + +#ifdef CONFIG_IEEE80211AC +static int hostapd_config_vht_capab(struct hostapd_config *conf, + const char *capab) +{ + if (os_strstr(capab, "[MAX-MPDU-7991]")) + conf->vht_capab |= VHT_CAP_MAX_MPDU_LENGTH_7991; + if (os_strstr(capab, "[MAX-MPDU-11454]")) + conf->vht_capab |= VHT_CAP_MAX_MPDU_LENGTH_11454; + if (os_strstr(capab, "[VHT160]")) + conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; + if (os_strstr(capab, "[VHT160-80PLUS80]")) + conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; + if (os_strstr(capab, "[RXLDPC]")) + conf->vht_capab |= VHT_CAP_RXLDPC; + if (os_strstr(capab, "[SHORT-GI-80]")) + conf->vht_capab |= VHT_CAP_SHORT_GI_80; + if (os_strstr(capab, "[SHORT-GI-160]")) + conf->vht_capab |= VHT_CAP_SHORT_GI_160; + if (os_strstr(capab, "[TX-STBC-2BY1]")) + conf->vht_capab |= VHT_CAP_TXSTBC; + if (os_strstr(capab, "[RX-STBC-1]")) + conf->vht_capab |= VHT_CAP_RXSTBC_1; + if (os_strstr(capab, "[RX-STBC-12]")) + conf->vht_capab |= VHT_CAP_RXSTBC_2; + if (os_strstr(capab, "[RX-STBC-123]")) + conf->vht_capab |= VHT_CAP_RXSTBC_3; + if (os_strstr(capab, "[RX-STBC-1234]")) + conf->vht_capab |= VHT_CAP_RXSTBC_4; + if (os_strstr(capab, "[SU-BEAMFORMER]")) + conf->vht_capab |= VHT_CAP_SU_BEAMFORMER_CAPABLE; + if (os_strstr(capab, "[SU-BEAMFORMEE]")) + conf->vht_capab |= VHT_CAP_SU_BEAMFORMEE_CAPABLE; + if (os_strstr(capab, "[BF-ANTENNA-2]") && + (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE)) + conf->vht_capab |= (1 << VHT_CAP_BEAMFORMEE_STS_OFFSET); + if (os_strstr(capab, "[BF-ANTENNA-3]") && + (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE)) + conf->vht_capab |= (2 << VHT_CAP_BEAMFORMEE_STS_OFFSET); + if (os_strstr(capab, "[BF-ANTENNA-4]") && + (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE)) + conf->vht_capab |= (3 << VHT_CAP_BEAMFORMEE_STS_OFFSET); + if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") && + (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE)) + conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET); + if (os_strstr(capab, "[SOUNDING-DIMENSION-3]") && + (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE)) + conf->vht_capab |= (2 << VHT_CAP_SOUNDING_DIMENSION_OFFSET); + if (os_strstr(capab, "[SOUNDING-DIMENSION-4]") && + (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE)) + conf->vht_capab |= (3 << VHT_CAP_SOUNDING_DIMENSION_OFFSET); + if (os_strstr(capab, "[MU-BEAMFORMER]")) + conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE; + if (os_strstr(capab, "[VHT-TXOP-PS]")) + conf->vht_capab |= VHT_CAP_VHT_TXOP_PS; + if (os_strstr(capab, "[HTC-VHT]")) + conf->vht_capab |= VHT_CAP_HTC_VHT; + if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP7]")) + conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX; + else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP6]")) + conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_6; + else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP5]")) + conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_5; + else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP4]")) + conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_4; + else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP3]")) + conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_3; + else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP2]")) + conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_2; + else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP1]")) + conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_1; + if (os_strstr(capab, "[VHT-LINK-ADAPT2]") && + (conf->vht_capab & VHT_CAP_HTC_VHT)) + conf->vht_capab |= VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB; + if (os_strstr(capab, "[VHT-LINK-ADAPT3]") && + (conf->vht_capab & VHT_CAP_HTC_VHT)) + conf->vht_capab |= VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB; + if (os_strstr(capab, "[RX-ANTENNA-PATTERN]")) + conf->vht_capab |= VHT_CAP_RX_ANTENNA_PATTERN; + if (os_strstr(capab, "[TX-ANTENNA-PATTERN]")) + conf->vht_capab |= VHT_CAP_TX_ANTENNA_PATTERN; + return 0; +} +#endif /* CONFIG_IEEE80211AC */ + + +#ifdef CONFIG_IEEE80211AX + +static u8 find_bit_offset(u8 val) +{ + u8 res = 0; + + for (; val; val >>= 1) { + if (val & 1) + break; + res++; + } + + return res; +} + + +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 */ + + +#ifdef CONFIG_INTERWORKING +static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos, + int line) +{ + size_t len = os_strlen(pos); + u8 oi[MAX_ROAMING_CONSORTIUM_LEN]; + + struct hostapd_roaming_consortium *rc; + + if ((len & 1) || len < 2 * 3 || len / 2 > MAX_ROAMING_CONSORTIUM_LEN || + hexstr2bin(pos, oi, len / 2)) { + wpa_printf(MSG_ERROR, "Line %d: invalid roaming_consortium " + "'%s'", line, pos); + return -1; + } + len /= 2; + + rc = os_realloc_array(bss->roaming_consortium, + bss->roaming_consortium_count + 1, + sizeof(struct hostapd_roaming_consortium)); + if (rc == NULL) + return -1; + + os_memcpy(rc[bss->roaming_consortium_count].oi, oi, len); + rc[bss->roaming_consortium_count].len = len; + + bss->roaming_consortium = rc; + bss->roaming_consortium_count++; + + return 0; +} + + +static int parse_lang_string(struct hostapd_lang_string **array, + unsigned int *count, char *pos) +{ + char *sep, *str = NULL; + size_t clen, nlen, slen; + struct hostapd_lang_string *ls; + int ret = -1; + + if (*pos == '"' || (*pos == 'P' && pos[1] == '"')) { + str = wpa_config_parse_string(pos, &slen); + if (!str) + return -1; + pos = str; + } + + sep = os_strchr(pos, ':'); + if (sep == NULL) + goto fail; + *sep++ = '\0'; + + clen = os_strlen(pos); + if (clen < 2 || clen > sizeof(ls->lang)) + goto fail; + nlen = os_strlen(sep); + if (nlen > 252) + goto fail; + + ls = os_realloc_array(*array, *count + 1, + sizeof(struct hostapd_lang_string)); + if (ls == NULL) + goto fail; + + *array = ls; + ls = &(*array)[*count]; + (*count)++; + + os_memset(ls->lang, 0, sizeof(ls->lang)); + os_memcpy(ls->lang, pos, clen); + ls->name_len = nlen; + os_memcpy(ls->name, sep, nlen); + + ret = 0; +fail: + os_free(str); + return ret; +} + + +static int parse_venue_name(struct hostapd_bss_config *bss, char *pos, + int line) +{ + if (parse_lang_string(&bss->venue_name, &bss->venue_name_count, pos)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid venue_name '%s'", + line, pos); + return -1; + } + return 0; +} + + +static int parse_venue_url(struct hostapd_bss_config *bss, char *pos, + int line) +{ + char *sep; + size_t nlen; + struct hostapd_venue_url *url; + int ret = -1; + + sep = os_strchr(pos, ':'); + if (!sep) + goto fail; + *sep++ = '\0'; + + nlen = os_strlen(sep); + if (nlen > 254) + goto fail; + + url = os_realloc_array(bss->venue_url, bss->venue_url_count + 1, + sizeof(struct hostapd_venue_url)); + if (!url) + goto fail; + + bss->venue_url = url; + url = &bss->venue_url[bss->venue_url_count++]; + + url->venue_number = atoi(pos); + url->url_len = nlen; + os_memcpy(url->url, sep, nlen); + + ret = 0; +fail: + if (ret) + wpa_printf(MSG_ERROR, "Line %d: Invalid venue_url '%s'", + line, pos); + return ret; +} + + +static int parse_3gpp_cell_net(struct hostapd_bss_config *bss, char *buf, + int line) +{ + size_t count; + char *pos; + u8 *info = NULL, *ipos; + + /* format: [;][;...] */ + + count = 1; + for (pos = buf; *pos; pos++) { + if ((*pos < '0' || *pos > '9') && *pos != ';' && *pos != ',') + goto fail; + if (*pos == ';') + count++; + } + if (1 + count * 3 > 0x7f) + goto fail; + + info = os_zalloc(2 + 3 + count * 3); + if (info == NULL) + return -1; + + ipos = info; + *ipos++ = 0; /* GUD - Version 1 */ + *ipos++ = 3 + count * 3; /* User Data Header Length (UDHL) */ + *ipos++ = 0; /* PLMN List IEI */ + /* ext(b8) | Length of PLMN List value contents(b7..1) */ + *ipos++ = 1 + count * 3; + *ipos++ = count; /* Number of PLMNs */ + + pos = buf; + while (pos && *pos) { + char *mcc, *mnc; + size_t mnc_len; + + mcc = pos; + mnc = os_strchr(pos, ','); + if (mnc == NULL) + goto fail; + *mnc++ = '\0'; + pos = os_strchr(mnc, ';'); + if (pos) + *pos++ = '\0'; + + mnc_len = os_strlen(mnc); + if (os_strlen(mcc) != 3 || (mnc_len != 2 && mnc_len != 3)) + goto fail; + + /* BC coded MCC,MNC */ + /* MCC digit 2 | MCC digit 1 */ + *ipos++ = ((mcc[1] - '0') << 4) | (mcc[0] - '0'); + /* MNC digit 3 | MCC digit 3 */ + *ipos++ = (((mnc_len == 2) ? 0xf0 : ((mnc[2] - '0') << 4))) | + (mcc[2] - '0'); + /* MNC digit 2 | MNC digit 1 */ + *ipos++ = ((mnc[1] - '0') << 4) | (mnc[0] - '0'); + } + + os_free(bss->anqp_3gpp_cell_net); + bss->anqp_3gpp_cell_net = info; + bss->anqp_3gpp_cell_net_len = 2 + 3 + 3 * count; + wpa_hexdump(MSG_MSGDUMP, "3GPP Cellular Network information", + bss->anqp_3gpp_cell_net, bss->anqp_3gpp_cell_net_len); + + return 0; + +fail: + wpa_printf(MSG_ERROR, "Line %d: Invalid anqp_3gpp_cell_net: %s", + line, buf); + os_free(info); + return -1; +} + + +static int parse_nai_realm(struct hostapd_bss_config *bss, char *buf, int line) +{ + struct hostapd_nai_realm_data *realm; + size_t i, j, len; + int *offsets; + char *pos, *end, *rpos; + + offsets = os_calloc(bss->nai_realm_count * MAX_NAI_REALMS, + sizeof(int)); + if (offsets == NULL) + return -1; + + for (i = 0; i < bss->nai_realm_count; i++) { + realm = &bss->nai_realm_data[i]; + for (j = 0; j < MAX_NAI_REALMS; j++) { + offsets[i * MAX_NAI_REALMS + j] = + realm->realm[j] ? + realm->realm[j] - realm->realm_buf : -1; + } + } + + realm = os_realloc_array(bss->nai_realm_data, bss->nai_realm_count + 1, + sizeof(struct hostapd_nai_realm_data)); + if (realm == NULL) { + os_free(offsets); + return -1; + } + bss->nai_realm_data = realm; + + /* patch the pointers after realloc */ + for (i = 0; i < bss->nai_realm_count; i++) { + realm = &bss->nai_realm_data[i]; + for (j = 0; j < MAX_NAI_REALMS; j++) { + int offs = offsets[i * MAX_NAI_REALMS + j]; + if (offs >= 0) + realm->realm[j] = realm->realm_buf + offs; + else + realm->realm[j] = NULL; + } + } + os_free(offsets); + + realm = &bss->nai_realm_data[bss->nai_realm_count]; + os_memset(realm, 0, sizeof(*realm)); + + pos = buf; + realm->encoding = atoi(pos); + pos = os_strchr(pos, ','); + if (pos == NULL) + goto fail; + pos++; + + end = os_strchr(pos, ','); + if (end) { + len = end - pos; + *end = '\0'; + } else { + len = os_strlen(pos); + } + + if (len > MAX_NAI_REALMLEN) { + wpa_printf(MSG_ERROR, "Too long a realm string (%d > max %d " + "characters)", (int) len, MAX_NAI_REALMLEN); + goto fail; + } + os_memcpy(realm->realm_buf, pos, len); + + if (end) + pos = end + 1; + else + pos = NULL; + + while (pos && *pos) { + struct hostapd_nai_realm_eap *eap; + + if (realm->eap_method_count >= MAX_NAI_EAP_METHODS) { + wpa_printf(MSG_ERROR, "Too many EAP methods"); + goto fail; + } + + eap = &realm->eap_method[realm->eap_method_count]; + realm->eap_method_count++; + + end = os_strchr(pos, ','); + if (end == NULL) + end = pos + os_strlen(pos); + + eap->eap_method = atoi(pos); + for (;;) { + pos = os_strchr(pos, '['); + if (pos == NULL || pos > end) + break; + pos++; + if (eap->num_auths >= MAX_NAI_AUTH_TYPES) { + wpa_printf(MSG_ERROR, "Too many auth params"); + goto fail; + } + eap->auth_id[eap->num_auths] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL || pos > end) + goto fail; + pos++; + eap->auth_val[eap->num_auths] = atoi(pos); + pos = os_strchr(pos, ']'); + if (pos == NULL || pos > end) + goto fail; + pos++; + eap->num_auths++; + } + + if (*end != ',') + break; + + pos = end + 1; + } + + /* Split realm list into null terminated realms */ + rpos = realm->realm_buf; + i = 0; + while (*rpos) { + if (i >= MAX_NAI_REALMS) { + wpa_printf(MSG_ERROR, "Too many realms"); + goto fail; + } + realm->realm[i++] = rpos; + rpos = os_strchr(rpos, ';'); + if (rpos == NULL) + break; + *rpos++ = '\0'; + } + + bss->nai_realm_count++; + + return 0; + +fail: + wpa_printf(MSG_ERROR, "Line %d: invalid nai_realm '%s'", line, buf); + return -1; +} + + +static int parse_anqp_elem(struct hostapd_bss_config *bss, char *buf, int line) +{ + char *delim; + u16 infoid; + size_t len; + struct wpabuf *payload; + struct anqp_element *elem; + + delim = os_strchr(buf, ':'); + if (!delim) + return -1; + delim++; + infoid = atoi(buf); + len = os_strlen(delim); + if (len & 1) + return -1; + len /= 2; + payload = wpabuf_alloc(len); + if (!payload) + return -1; + if (hexstr2bin(delim, wpabuf_put(payload, len), len) < 0) { + wpabuf_free(payload); + return -1; + } + + dl_list_for_each(elem, &bss->anqp_elem, struct anqp_element, list) { + if (elem->infoid == infoid) { + /* Update existing entry */ + wpabuf_free(elem->payload); + elem->payload = payload; + return 0; + } + } + + /* Add a new entry */ + elem = os_zalloc(sizeof(*elem)); + if (!elem) { + wpabuf_free(payload); + return -1; + } + elem->infoid = infoid; + elem->payload = payload; + dl_list_add(&bss->anqp_elem, &elem->list); + + return 0; +} + +#endif /* CONFIG_INTERWORKING */ + + +static int parse_qos_map_set(struct hostapd_bss_config *bss, + char *buf, int line) +{ + u8 qos_map_set[16 + 2 * 21], count = 0; + char *pos = buf; + int val; + + for (;;) { + if (count == sizeof(qos_map_set)) { + wpa_printf(MSG_ERROR, "Line %d: Too many qos_map_set " + "parameters '%s'", line, buf); + return -1; + } + + val = atoi(pos); + if (val > 255 || val < 0) { + wpa_printf(MSG_ERROR, "Line %d: Invalid qos_map_set " + "'%s'", line, buf); + return -1; + } + + qos_map_set[count++] = val; + pos = os_strchr(pos, ','); + if (!pos) + break; + pos++; + } + + if (count < 16 || count & 1) { + wpa_printf(MSG_ERROR, "Line %d: Invalid qos_map_set '%s'", + line, buf); + return -1; + } + + os_memcpy(bss->qos_map_set, qos_map_set, count); + bss->qos_map_set_len = count; + + return 0; +} + + +#ifdef CONFIG_HS20 +static int hs20_parse_conn_capab(struct hostapd_bss_config *bss, char *buf, + int line) +{ + u8 *conn_cap; + char *pos; + + if (bss->hs20_connection_capability_len >= 0xfff0) + return -1; + + conn_cap = os_realloc(bss->hs20_connection_capability, + bss->hs20_connection_capability_len + 4); + if (conn_cap == NULL) + return -1; + + bss->hs20_connection_capability = conn_cap; + conn_cap += bss->hs20_connection_capability_len; + pos = buf; + conn_cap[0] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) + return -1; + pos++; + WPA_PUT_LE16(conn_cap + 1, atoi(pos)); + pos = os_strchr(pos, ':'); + if (pos == NULL) + return -1; + pos++; + conn_cap[3] = atoi(pos); + bss->hs20_connection_capability_len += 4; + + return 0; +} + + +static int hs20_parse_wan_metrics(struct hostapd_bss_config *bss, char *buf, + int line) +{ + u8 *wan_metrics; + char *pos; + + /* :
:
    :
    :
      : */ + + wan_metrics = os_zalloc(13); + if (wan_metrics == NULL) + return -1; + + pos = buf; + /* WAN Info */ + if (hexstr2bin(pos, wan_metrics, 1) < 0) + goto fail; + pos += 2; + if (*pos != ':') + goto fail; + pos++; + + /* Downlink Speed */ + WPA_PUT_LE32(wan_metrics + 1, atoi(pos)); + pos = os_strchr(pos, ':'); + if (pos == NULL) + goto fail; + pos++; + + /* Uplink Speed */ + WPA_PUT_LE32(wan_metrics + 5, atoi(pos)); + pos = os_strchr(pos, ':'); + if (pos == NULL) + goto fail; + pos++; + + /* Downlink Load */ + wan_metrics[9] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) + goto fail; + pos++; + + /* Uplink Load */ + wan_metrics[10] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) + goto fail; + pos++; + + /* LMD */ + WPA_PUT_LE16(wan_metrics + 11, atoi(pos)); + + os_free(bss->hs20_wan_metrics); + bss->hs20_wan_metrics = wan_metrics; + + return 0; + +fail: + wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_wan_metrics '%s'", + line, buf); + os_free(wan_metrics); + return -1; +} + + +static int hs20_parse_oper_friendly_name(struct hostapd_bss_config *bss, + char *pos, int line) +{ + if (parse_lang_string(&bss->hs20_oper_friendly_name, + &bss->hs20_oper_friendly_name_count, pos)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid " + "hs20_oper_friendly_name '%s'", line, pos); + return -1; + } + return 0; +} + + +static int hs20_parse_icon(struct hostapd_bss_config *bss, char *pos) +{ + struct hs20_icon *icon; + char *end; + + icon = os_realloc_array(bss->hs20_icons, bss->hs20_icons_count + 1, + sizeof(struct hs20_icon)); + if (icon == NULL) + return -1; + bss->hs20_icons = icon; + icon = &bss->hs20_icons[bss->hs20_icons_count]; + os_memset(icon, 0, sizeof(*icon)); + + icon->width = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) + return -1; + pos++; + + icon->height = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) + return -1; + pos++; + + end = os_strchr(pos, ':'); + if (end == NULL || end - pos > 3) + return -1; + os_memcpy(icon->language, pos, end - pos); + pos = end + 1; + + end = os_strchr(pos, ':'); + if (end == NULL || end - pos > 255) + return -1; + os_memcpy(icon->type, pos, end - pos); + pos = end + 1; + + end = os_strchr(pos, ':'); + if (end == NULL || end - pos > 255) + return -1; + os_memcpy(icon->name, pos, end - pos); + pos = end + 1; + + if (os_strlen(pos) > 255) + return -1; + os_memcpy(icon->file, pos, os_strlen(pos)); + + bss->hs20_icons_count++; + + return 0; +} + + +static int hs20_parse_osu_ssid(struct hostapd_bss_config *bss, + char *pos, int line) +{ + size_t slen; + char *str; + + str = wpa_config_parse_string(pos, &slen); + if (str == NULL || slen < 1 || slen > SSID_MAX_LEN) { + wpa_printf(MSG_ERROR, "Line %d: Invalid SSID '%s'", line, pos); + os_free(str); + return -1; + } + + os_memcpy(bss->osu_ssid, str, slen); + bss->osu_ssid_len = slen; + os_free(str); + + return 0; +} + + +static int hs20_parse_osu_server_uri(struct hostapd_bss_config *bss, + char *pos, int line) +{ + struct hs20_osu_provider *p; + + p = os_realloc_array(bss->hs20_osu_providers, + bss->hs20_osu_providers_count + 1, sizeof(*p)); + if (p == NULL) + return -1; + + bss->hs20_osu_providers = p; + bss->last_osu = &bss->hs20_osu_providers[bss->hs20_osu_providers_count]; + bss->hs20_osu_providers_count++; + os_memset(bss->last_osu, 0, sizeof(*p)); + bss->last_osu->server_uri = os_strdup(pos); + + return 0; +} + + +static int hs20_parse_osu_friendly_name(struct hostapd_bss_config *bss, + char *pos, int line) +{ + if (bss->last_osu == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); + return -1; + } + + if (parse_lang_string(&bss->last_osu->friendly_name, + &bss->last_osu->friendly_name_count, pos)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid osu_friendly_name '%s'", + line, pos); + return -1; + } + + return 0; +} + + +static int hs20_parse_osu_nai(struct hostapd_bss_config *bss, + char *pos, int line) +{ + if (bss->last_osu == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); + return -1; + } + + os_free(bss->last_osu->osu_nai); + bss->last_osu->osu_nai = os_strdup(pos); + if (bss->last_osu->osu_nai == NULL) + return -1; + + return 0; +} + + +static int hs20_parse_osu_nai2(struct hostapd_bss_config *bss, + char *pos, int line) +{ + if (bss->last_osu == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); + return -1; + } + + os_free(bss->last_osu->osu_nai2); + bss->last_osu->osu_nai2 = os_strdup(pos); + if (bss->last_osu->osu_nai2 == NULL) + return -1; + bss->hs20_osu_providers_nai_count++; + + return 0; +} + + +static int hs20_parse_osu_method_list(struct hostapd_bss_config *bss, char *pos, + int line) +{ + if (bss->last_osu == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); + return -1; + } + + if (hostapd_parse_intlist(&bss->last_osu->method_list, pos)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid osu_method_list", line); + return -1; + } + + return 0; +} + + +static int hs20_parse_osu_icon(struct hostapd_bss_config *bss, char *pos, + int line) +{ + char **n; + struct hs20_osu_provider *p = bss->last_osu; + + if (p == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); + return -1; + } + + n = os_realloc_array(p->icons, p->icons_count + 1, sizeof(char *)); + if (n == NULL) + return -1; + p->icons = n; + p->icons[p->icons_count] = os_strdup(pos); + if (p->icons[p->icons_count] == NULL) + return -1; + p->icons_count++; + + return 0; +} + + +static int hs20_parse_osu_service_desc(struct hostapd_bss_config *bss, + char *pos, int line) +{ + if (bss->last_osu == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line); + return -1; + } + + if (parse_lang_string(&bss->last_osu->service_desc, + &bss->last_osu->service_desc_count, pos)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid osu_service_desc '%s'", + line, pos); + return -1; + } + + return 0; +} + + +static int hs20_parse_operator_icon(struct hostapd_bss_config *bss, char *pos, + int line) +{ + char **n; + + n = os_realloc_array(bss->hs20_operator_icon, + bss->hs20_operator_icon_count + 1, sizeof(char *)); + if (!n) + return -1; + bss->hs20_operator_icon = n; + bss->hs20_operator_icon[bss->hs20_operator_icon_count] = os_strdup(pos); + if (!bss->hs20_operator_icon[bss->hs20_operator_icon_count]) + return -1; + bss->hs20_operator_icon_count++; + + return 0; +} + +#endif /* CONFIG_HS20 */ + + +#ifdef CONFIG_ACS +static int hostapd_config_parse_acs_chan_bias(struct hostapd_config *conf, + char *pos) +{ + struct acs_bias *bias = NULL, *tmp; + unsigned int num = 0; + char *end; + + while (*pos) { + tmp = os_realloc_array(bias, num + 1, sizeof(*bias)); + if (!tmp) + goto fail; + bias = tmp; + + bias[num].channel = atoi(pos); + if (bias[num].channel <= 0) + goto fail; + pos = os_strchr(pos, ':'); + if (!pos) + goto fail; + pos++; + bias[num].bias = strtod(pos, &end); + if (end == pos || bias[num].bias < 0.0) + goto fail; + pos = end; + if (*pos != ' ' && *pos != '\0') + goto fail; + num++; + } + + os_free(conf->acs_chan_bias); + conf->acs_chan_bias = bias; + conf->num_acs_chan_bias = num; + + return 0; +fail: + os_free(bias); + return -1; +} +#endif /* CONFIG_ACS */ + + +static int parse_wpabuf_hex(int line, const char *name, struct wpabuf **buf, + const char *val) +{ + struct wpabuf *elems; + + if (val[0] == '\0') { + wpabuf_free(*buf); + *buf = NULL; + return 0; + } + + elems = wpabuf_parse_bin(val); + if (!elems) { + wpa_printf(MSG_ERROR, "Line %d: Invalid %s '%s'", + line, name, val); + return -1; + } + + wpabuf_free(*buf); + *buf = elems; + + return 0; +} + + +#ifdef CONFIG_FILS +static int parse_fils_realm(struct hostapd_bss_config *bss, const char *val) +{ + struct fils_realm *realm; + size_t len; + + len = os_strlen(val); + realm = os_zalloc(sizeof(*realm) + len + 1); + if (!realm) + return -1; + + os_memcpy(realm->realm, val, len); + if (fils_domain_name_hash(val, realm->hash) < 0) { + os_free(realm); + return -1; + } + dl_list_add_tail(&bss->fils_realms, &realm->list); + + return 0; +} +#endif /* CONFIG_FILS */ + + +#ifdef EAP_SERVER +static unsigned int parse_tls_flags(const char *val) +{ + unsigned int flags = 0; + + /* Disable TLS v1.3 by default for now to avoid interoperability issue. + * This can be enabled by default once the implementation has been fully + * completed and tested with other implementations. */ + flags |= TLS_CONN_DISABLE_TLSv1_3; + + if (os_strstr(val, "[ALLOW-SIGN-RSA-MD5]")) + flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5; + if (os_strstr(val, "[DISABLE-TIME-CHECKS]")) + flags |= TLS_CONN_DISABLE_TIME_CHECKS; + if (os_strstr(val, "[DISABLE-TLSv1.0]")) + flags |= TLS_CONN_DISABLE_TLSv1_0; + if (os_strstr(val, "[ENABLE-TLSv1.0]")) + flags |= TLS_CONN_ENABLE_TLSv1_0; + if (os_strstr(val, "[DISABLE-TLSv1.1]")) + flags |= TLS_CONN_DISABLE_TLSv1_1; + if (os_strstr(val, "[ENABLE-TLSv1.1]")) + flags |= TLS_CONN_ENABLE_TLSv1_1; + if (os_strstr(val, "[DISABLE-TLSv1.2]")) + flags |= TLS_CONN_DISABLE_TLSv1_2; + if (os_strstr(val, "[ENABLE-TLSv1.2]")) + flags |= TLS_CONN_ENABLE_TLSv1_2; + if (os_strstr(val, "[DISABLE-TLSv1.3]")) + flags |= TLS_CONN_DISABLE_TLSv1_3; + if (os_strstr(val, "[ENABLE-TLSv1.3]")) + flags &= ~TLS_CONN_DISABLE_TLSv1_3; + if (os_strstr(val, "[SUITEB]")) + flags |= TLS_CONN_SUITEB; + if (os_strstr(val, "[SUITEB-NO-ECDH]")) + flags |= TLS_CONN_SUITEB_NO_ECDH | TLS_CONN_SUITEB; + + return flags; +} +#endif /* EAP_SERVER */ + + +#ifdef CONFIG_AIRTIME_POLICY +static int add_airtime_weight(struct hostapd_bss_config *bss, char *value) +{ + struct airtime_sta_weight *wt; + char *pos, *next; + + wt = os_zalloc(sizeof(*wt)); + if (!wt) + return -1; + + /* 02:01:02:03:04:05 10 */ + pos = value; + next = os_strchr(pos, ' '); + if (next) + *next++ = '\0'; + if (!next || hwaddr_aton(pos, wt->addr)) { + wpa_printf(MSG_ERROR, "Invalid station address: '%s'", pos); + os_free(wt); + return -1; + } + + pos = next; + wt->weight = atoi(pos); + if (!wt->weight) { + wpa_printf(MSG_ERROR, "Invalid weight: '%s'", pos); + os_free(wt); + return -1; + } + + wt->next = bss->airtime_weight_list; + bss->airtime_weight_list = wt; + return 0; +} +#endif /* CONFIG_AIRTIME_POLICY */ + + +#ifdef CONFIG_SAE + +static int parse_sae_password(struct hostapd_bss_config *bss, const char *val) +{ + struct sae_password_entry *pw; + const char *pos = val, *pos2, *end = NULL; + + pw = os_zalloc(sizeof(*pw)); + if (!pw) + return -1; + os_memset(pw->peer_addr, 0xff, ETH_ALEN); /* default to wildcard */ + + pos2 = os_strstr(pos, "|mac="); + if (pos2) { + end = pos2; + pos2 += 5; + if (hwaddr_aton(pos2, pw->peer_addr) < 0) + goto fail; + pos = pos2 + ETH_ALEN * 3 - 1; + } + + pos2 = os_strstr(pos, "|vlanid="); + if (pos2) { + if (!end) + end = pos2; + pos2 += 8; + 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) + end = pos2; + pos2 += 4; + pw->identifier = os_strdup(pos2); + if (!pw->identifier) + goto fail; + } + + if (!end) { + pw->password = os_strdup(val); + if (!pw->password) + goto fail; + } else { + pw->password = os_malloc(end - val + 1); + if (!pw->password) + goto fail; + os_memcpy(pw->password, val, end - val); + pw->password[end - val] = '\0'; + } + +#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; + + return 0; +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; +} + + +static int parse_sae_password_file(struct hostapd_bss_config *bss, + const char *fname) +{ + FILE *f; + char buf[500], *pos; + unsigned int line = 0; + + f = fopen(fname, "r"); + if (!f) { + wpa_printf(MSG_ERROR, "sae_password_file '%s' not found.", + fname); + return -1; + } + + while (fgets(buf, sizeof(buf), f)) { + pos = os_strchr(buf, '\n'); + if (pos) + *pos = '\0'; + line++; + if (parse_sae_password(bss, buf)) { + wpa_printf(MSG_ERROR, + "Invalid SAE password at line %d in '%s'", + line, fname); + fclose(f); + return -1; + } + } + + fclose(f); + return 0; +} + +#endif /* CONFIG_SAE */ + + +#ifdef CONFIG_DPP2 +static int hostapd_dpp_controller_parse(struct hostapd_bss_config *bss, + const char *pos) +{ + struct dpp_controller_conf *conf; + char *val; + + conf = os_zalloc(sizeof(*conf)); + if (!conf) + return -1; + val = get_param(pos, "ipaddr="); + if (!val || hostapd_parse_ip_addr(val, &conf->ipaddr)) + goto fail; + os_free(val); + val = get_param(pos, "pkhash="); + if (!val || os_strlen(val) != 2 * SHA256_MAC_LEN || + hexstr2bin(val, conf->pkhash, SHA256_MAC_LEN) < 0) + goto fail; + os_free(val); + conf->next = bss->dpp_controller; + bss->dpp_controller = conf; + return 0; +fail: + os_free(val); + os_free(conf); + return -1; +} +#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; +} + + +#ifdef CONFIG_IEEE80211BE +static int get_u16(const char *pos, int line, u16 *ret_val) +{ + char *end; + long int val = strtol(pos, &end, 0); + + if (*end || val < 0 || val > 0xffff) { + wpa_printf(MSG_ERROR, "Line %d: Invalid value '%s'", + line, pos); + return -1; + } + + *ret_val = val; + return 0; +} +#endif /* CONFIG_IEEE80211BE */ + + +static int hostapd_config_fill(struct hostapd_config *conf, + struct hostapd_bss_config *bss, + const char *buf, char *pos, int line) +{ + if (os_strcmp(buf, "interface") == 0) { + os_strlcpy(conf->bss[0]->iface, pos, + sizeof(conf->bss[0]->iface)); + } else if (os_strcmp(buf, "bridge") == 0) { + os_strlcpy(bss->bridge, pos, sizeof(bss->bridge)); + } else if (os_strcmp(buf, "bridge_hairpin") == 0) { + bss->bridge_hairpin = atoi(pos); + } else if (os_strcmp(buf, "vlan_bridge") == 0) { + os_strlcpy(bss->vlan_bridge, pos, sizeof(bss->vlan_bridge)); + } else if (os_strcmp(buf, "wds_bridge") == 0) { + os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge)); + } else if (os_strcmp(buf, "driver") == 0) { + int j; + const struct wpa_driver_ops *driver = NULL; + + for (j = 0; wpa_drivers[j]; j++) { + if (os_strcmp(pos, wpa_drivers[j]->name) == 0) { + driver = wpa_drivers[j]; + break; + } + } + if (!driver) { + wpa_printf(MSG_ERROR, + "Line %d: invalid/unknown driver '%s'", + line, pos); + return 1; + } + conf->driver = driver; + } else if (os_strcmp(buf, "driver_params") == 0) { + os_free(conf->driver_params); + conf->driver_params = os_strdup(pos); + } else if (os_strcmp(buf, "debug") == 0) { + wpa_printf(MSG_DEBUG, "Line %d: DEPRECATED: 'debug' configuration variable is not used anymore", + line); + } else if (os_strcmp(buf, "logger_syslog_level") == 0) { + bss->logger_syslog_level = atoi(pos); + } else if (os_strcmp(buf, "logger_stdout_level") == 0) { + bss->logger_stdout_level = atoi(pos); + } else if (os_strcmp(buf, "logger_syslog") == 0) { + bss->logger_syslog = atoi(pos); + } else if (os_strcmp(buf, "logger_stdout") == 0) { + bss->logger_stdout = atoi(pos); + } else if (os_strcmp(buf, "dump_file") == 0) { + wpa_printf(MSG_INFO, "Line %d: DEPRECATED: 'dump_file' configuration variable is not used anymore", + line); + } else if (os_strcmp(buf, "ssid") == 0) { + struct hostapd_ssid *ssid = &bss->ssid; + + ssid->ssid_len = os_strlen(pos); + if (ssid->ssid_len > SSID_MAX_LEN || ssid->ssid_len < 1) { + wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'", + line, pos); + return 1; + } + os_memcpy(ssid->ssid, pos, ssid->ssid_len); + ssid->ssid_set = 1; + ssid->short_ssid = ieee80211_crc32(ssid->ssid, ssid->ssid_len); + } else if (os_strcmp(buf, "ssid2") == 0) { + struct hostapd_ssid *ssid = &bss->ssid; + size_t slen; + char *str = wpa_config_parse_string(pos, &slen); + if (str == NULL || slen < 1 || slen > SSID_MAX_LEN) { + wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'", + line, pos); + os_free(str); + return 1; + } + os_memcpy(ssid->ssid, str, slen); + ssid->ssid_len = slen; + ssid->ssid_set = 1; + ssid->short_ssid = ieee80211_crc32(ssid->ssid, ssid->ssid_len); + os_free(str); + } else if (os_strcmp(buf, "utf8_ssid") == 0) { + bss->ssid.utf8_ssid = atoi(pos) > 0; + } else if (os_strcmp(buf, "macaddr_acl") == 0) { + enum macaddr_acl acl = atoi(pos); + + if (acl != ACCEPT_UNLESS_DENIED && + acl != DENY_UNLESS_ACCEPTED && + acl != USE_EXTERNAL_RADIUS_AUTH) { + wpa_printf(MSG_ERROR, "Line %d: unknown macaddr_acl %d", + line, acl); + return 1; + } + bss->macaddr_acl = acl; + } else if (os_strcmp(buf, "accept_mac_file") == 0) { + if (hostapd_config_read_maclist(pos, &bss->accept_mac, + &bss->num_accept_mac)) { + wpa_printf(MSG_ERROR, "Line %d: Failed to read accept_mac_file '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "deny_mac_file") == 0) { + if (hostapd_config_read_maclist(pos, &bss->deny_mac, + &bss->num_deny_mac)) { + wpa_printf(MSG_ERROR, "Line %d: Failed to read deny_mac_file '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "wds_sta") == 0) { + bss->wds_sta = atoi(pos); + } else if (os_strcmp(buf, "start_disabled") == 0) { + bss->start_disabled = atoi(pos); + } else if (os_strcmp(buf, "ap_isolate") == 0) { + bss->isolate = atoi(pos); + } else if (os_strcmp(buf, "ap_max_inactivity") == 0) { + bss->ap_max_inactivity = atoi(pos); + } else if (os_strcmp(buf, "skip_inactivity_poll") == 0) { + bss->skip_inactivity_poll = atoi(pos); + } else if (os_strcmp(buf, "bss_max_idle") == 0) { + int val = atoi(pos); + + if (val < 0 || val > 2) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid bss_max_idle value", line); + return 1; + } + bss->bss_max_idle = val; + } else if (os_strcmp(buf, "max_acceptable_idle_period") == 0) { + bss->max_acceptable_idle_period = atoi(pos); + } else if (os_strcmp(buf, "no_disconnect_on_group_keyerror") == 0) { + int val = atoi(pos); + + if (val < 0 || val > 1) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid no_disconnect_on_group_keyerror", + line); + return 1; + } + bss->no_disconnect_on_group_keyerror = val; + } else if (os_strcmp(buf, "config_id") == 0) { + os_free(bss->config_id); + bss->config_id = os_strdup(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); + } else if (os_strcmp(buf, "ieee80211d") == 0) { + conf->ieee80211d = atoi(pos); + } else if (os_strcmp(buf, "ieee80211h") == 0) { + conf->ieee80211h = atoi(pos); + } else if (os_strcmp(buf, "ieee8021x") == 0) { + bss->ieee802_1x = atoi(pos); + } else if (os_strcmp(buf, "eapol_version") == 0) { + int eapol_version = atoi(pos); +#ifdef CONFIG_MACSEC + int max_ver = 3; +#else /* CONFIG_MACSEC */ + 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); + return 1; + } + bss->eapol_version = eapol_version; + wpa_printf(MSG_DEBUG, "eapol_version=%d", bss->eapol_version); +#ifdef EAP_SERVER + } else if (os_strcmp(buf, "eap_authenticator") == 0) { + bss->eap_server = atoi(pos); + wpa_printf(MSG_ERROR, "Line %d: obsolete eap_authenticator used; this has been renamed to eap_server", line); + } else if (os_strcmp(buf, "eap_server") == 0) { + bss->eap_server = atoi(pos); + } else if (os_strcmp(buf, "eap_user_file") == 0) { + if (hostapd_config_read_eap_user(pos, bss)) + return 1; + } else if (os_strcmp(buf, "ca_cert") == 0) { + os_free(bss->ca_cert); + bss->ca_cert = os_strdup(pos); + } else if (os_strcmp(buf, "server_cert") == 0) { + os_free(bss->server_cert); + bss->server_cert = os_strdup(pos); + } else if (os_strcmp(buf, "server_cert2") == 0) { + os_free(bss->server_cert2); + bss->server_cert2 = os_strdup(pos); + } else if (os_strcmp(buf, "private_key") == 0) { + os_free(bss->private_key); + bss->private_key = os_strdup(pos); + } else if (os_strcmp(buf, "private_key2") == 0) { + os_free(bss->private_key2); + bss->private_key2 = os_strdup(pos); + } else if (os_strcmp(buf, "private_key_passwd") == 0) { + os_free(bss->private_key_passwd); + bss->private_key_passwd = os_strdup(pos); + } else if (os_strcmp(buf, "private_key_passwd2") == 0) { + os_free(bss->private_key_passwd2); + bss->private_key_passwd2 = os_strdup(pos); + } else if (os_strcmp(buf, "check_cert_subject") == 0) { + if (!pos[0]) { + wpa_printf(MSG_ERROR, "Line %d: unknown check_cert_subject '%s'", + line, pos); + return 1; + } + os_free(bss->check_cert_subject); + bss->check_cert_subject = os_strdup(pos); + if (!bss->check_cert_subject) + return 1; + } else if (os_strcmp(buf, "check_crl") == 0) { + bss->check_crl = atoi(pos); + } else if (os_strcmp(buf, "check_crl_strict") == 0) { + bss->check_crl_strict = atoi(pos); + } else if (os_strcmp(buf, "crl_reload_interval") == 0) { + bss->crl_reload_interval = atoi(pos); + } else if (os_strcmp(buf, "tls_session_lifetime") == 0) { + bss->tls_session_lifetime = atoi(pos); + } else if (os_strcmp(buf, "tls_flags") == 0) { + bss->tls_flags = parse_tls_flags(pos); + } else if (os_strcmp(buf, "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); + } else if (os_strcmp(buf, "ocsp_stapling_response_multi") == 0) { + os_free(bss->ocsp_stapling_response_multi); + bss->ocsp_stapling_response_multi = os_strdup(pos); + } else if (os_strcmp(buf, "dh_file") == 0) { + os_free(bss->dh_file); + bss->dh_file = os_strdup(pos); + } else if (os_strcmp(buf, "openssl_ciphers") == 0) { + os_free(bss->openssl_ciphers); + bss->openssl_ciphers = os_strdup(pos); + } else if (os_strcmp(buf, "openssl_ecdh_curves") == 0) { + os_free(bss->openssl_ecdh_curves); + bss->openssl_ecdh_curves = os_strdup(pos); + } else if (os_strcmp(buf, "fragment_size") == 0) { + bss->fragment_size = atoi(pos); +#ifdef EAP_SERVER_FAST + } else if (os_strcmp(buf, "pac_opaque_encr_key") == 0) { + os_free(bss->pac_opaque_encr_key); + bss->pac_opaque_encr_key = os_malloc(16); + if (bss->pac_opaque_encr_key == NULL) { + wpa_printf(MSG_ERROR, + "Line %d: No memory for pac_opaque_encr_key", + line); + return 1; + } else if (hexstr2bin(pos, bss->pac_opaque_encr_key, 16)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid pac_opaque_encr_key", + line); + return 1; + } + } else if (os_strcmp(buf, "eap_fast_a_id") == 0) { + size_t idlen = os_strlen(pos); + if (idlen & 1) { + wpa_printf(MSG_ERROR, "Line %d: Invalid eap_fast_a_id", + line); + return 1; + } + os_free(bss->eap_fast_a_id); + bss->eap_fast_a_id = os_malloc(idlen / 2); + if (bss->eap_fast_a_id == NULL || + hexstr2bin(pos, bss->eap_fast_a_id, idlen / 2)) { + wpa_printf(MSG_ERROR, "Line %d: Failed to parse eap_fast_a_id", + line); + os_free(bss->eap_fast_a_id); + bss->eap_fast_a_id = NULL; + return 1; + } else { + bss->eap_fast_a_id_len = idlen / 2; + } + } else if (os_strcmp(buf, "eap_fast_a_id_info") == 0) { + os_free(bss->eap_fast_a_id_info); + bss->eap_fast_a_id_info = os_strdup(pos); + } else if (os_strcmp(buf, "eap_fast_prov") == 0) { + bss->eap_fast_prov = atoi(pos); + } else if (os_strcmp(buf, "pac_key_lifetime") == 0) { + bss->pac_key_lifetime = atoi(pos); + } else if (os_strcmp(buf, "pac_key_refresh_time") == 0) { + bss->pac_key_refresh_time = atoi(pos); +#endif /* EAP_SERVER_FAST */ +#ifdef EAP_SERVER_TEAP + } else if (os_strcmp(buf, "eap_teap_auth") == 0) { + int val = atoi(pos); + + if (val < 0 || val > 2) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid eap_teap_auth value", + line); + return 1; + } + 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); + } else if (os_strcmp(buf, "eap_teap_method_sequence") == 0) { + bss->eap_teap_method_sequence = atoi(pos); +#endif /* EAP_SERVER_TEAP */ +#ifdef EAP_SERVER_SIM + } else if (os_strcmp(buf, "eap_sim_db") == 0) { + os_free(bss->eap_sim_db); + bss->eap_sim_db = os_strdup(pos); + } else if (os_strcmp(buf, "eap_sim_db_timeout") == 0) { + bss->eap_sim_db_timeout = atoi(pos); + } else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) { + bss->eap_sim_aka_result_ind = atoi(pos); + } else if (os_strcmp(buf, "eap_sim_id") == 0) { + bss->eap_sim_id = atoi(pos); + } else if (os_strcmp(buf, "imsi_privacy_key") == 0) { + os_free(bss->imsi_privacy_key); + bss->imsi_privacy_key = os_strdup(pos); + } else if (os_strcmp(buf, "eap_sim_aka_fast_reauth_limit") == 0) { + bss->eap_sim_aka_fast_reauth_limit = atoi(pos); +#endif /* EAP_SERVER_SIM */ +#ifdef EAP_SERVER_TNC + } else if (os_strcmp(buf, "tnc") == 0) { + bss->tnc = atoi(pos); +#endif /* EAP_SERVER_TNC */ +#ifdef EAP_SERVER_PWD + } else if (os_strcmp(buf, "pwd_group") == 0) { + bss->pwd_group = atoi(pos); +#endif /* EAP_SERVER_PWD */ +#ifdef CONFIG_ERP + } else if (os_strcmp(buf, "eap_server_erp") == 0) { + bss->eap_server_erp = atoi(pos); +#endif /* CONFIG_ERP */ +#endif /* EAP_SERVER */ + } else if (os_strcmp(buf, "eap_message") == 0) { + char *term; + os_free(bss->eap_req_id_text); + bss->eap_req_id_text = os_strdup(pos); + if (bss->eap_req_id_text == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Failed to allocate memory for eap_req_id_text", + line); + return 1; + } + bss->eap_req_id_text_len = os_strlen(bss->eap_req_id_text); + term = os_strstr(bss->eap_req_id_text, "\\0"); + if (term) { + *term++ = '\0'; + os_memmove(term, term + 1, + bss->eap_req_id_text_len - + (term - bss->eap_req_id_text) - 1); + bss->eap_req_id_text_len--; + } + } else if (os_strcmp(buf, "erp_send_reauth_start") == 0) { + bss->erp_send_reauth_start = atoi(pos); + } 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); + + if (val < 0 || val > 13) { + wpa_printf(MSG_ERROR, + "Line %d: invalid WEP key len %d (= %d bits)", + line, val, val * 8); + return 1; + } + bss->default_wep_key_len = val; + } else if (os_strcmp(buf, "wep_key_len_unicast") == 0) { + int val = atoi(pos); + + if (val < 0 || val > 13) { + wpa_printf(MSG_ERROR, + "Line %d: invalid WEP key len %d (= %d bits)", + line, val, val * 8); + return 1; + } + bss->individual_wep_key_len = val; + } else if (os_strcmp(buf, "wep_rekey_period") == 0) { + bss->wep_rekeying_period = atoi(pos); + if (bss->wep_rekeying_period < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid period %d", + 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) { + wpa_printf(MSG_ERROR, "Line %d: invalid period %d", + line, bss->eap_reauth_period); + return 1; + } + } else if (os_strcmp(buf, "eapol_key_index_workaround") == 0) { + bss->eapol_key_index_workaround = atoi(pos); +#ifdef CONFIG_IAPP + } else if (os_strcmp(buf, "iapp_interface") == 0) { + 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)) { + wpa_printf(MSG_ERROR, + "Line %d: invalid IP address '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "nas_identifier") == 0) { + os_free(bss->nas_identifier); + bss->nas_identifier = os_strdup(pos); +#ifndef CONFIG_NO_RADIUS + } else if (os_strcmp(buf, "radius_client_addr") == 0) { + if (hostapd_parse_ip_addr(pos, &bss->radius->client_addr)) { + wpa_printf(MSG_ERROR, + "Line %d: invalid IP address '%s'", + line, pos); + 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, + &bss->radius->num_auth_servers, pos, 1812, + &bss->radius->auth_server)) { + wpa_printf(MSG_ERROR, + "Line %d: invalid IP address '%s'", + line, pos); + return 1; + } + } else if (bss->radius->auth_server && + os_strcmp(buf, "auth_server_addr_replace") == 0) { + if (hostapd_parse_ip_addr(pos, + &bss->radius->auth_server->addr)) { + wpa_printf(MSG_ERROR, + "Line %d: invalid IP address '%s'", + line, pos); + return 1; + } + } else if (bss->radius->auth_server && + os_strcmp(buf, "auth_server_port") == 0) { + bss->radius->auth_server->port = atoi(pos); + } else if (bss->radius->auth_server && + os_strcmp(buf, "auth_server_shared_secret") == 0) { + int len = os_strlen(pos); + if (len == 0) { + /* RFC 2865, Ch. 3 */ + wpa_printf(MSG_ERROR, "Line %d: empty shared secret is not allowed", + line); + return 1; + } + os_free(bss->radius->auth_server->shared_secret); + bss->radius->auth_server->shared_secret = (u8 *) os_strdup(pos); + bss->radius->auth_server->shared_secret_len = len; + } else if (bss->radius->auth_server && + os_strcmp(buf, "auth_server_type") == 0) { + if (os_strcmp(pos, "UDP") == 0) { + bss->radius->auth_server->tls = false; +#ifdef CONFIG_RADIUS_TLS + } else if (os_strcmp(pos, "TLS") == 0) { + bss->radius->auth_server->tls = true; +#endif /* CONFIG_RADIUS_TLS */ + } else { + wpa_printf(MSG_ERROR, "Line %d: unsupported RADIUS type '%s'", + line, pos); + return 1; + } +#ifdef CONFIG_RADIUS_TLS + } else if (bss->radius->auth_server && + os_strcmp(buf, "auth_server_ca_cert") == 0) { + os_free(bss->radius->auth_server->ca_cert); + bss->radius->auth_server->ca_cert = os_strdup(pos); + } else if (bss->radius->auth_server && + os_strcmp(buf, "auth_server_client_cert") == 0) { + os_free(bss->radius->auth_server->client_cert); + bss->radius->auth_server->client_cert = os_strdup(pos); + } else if (bss->radius->auth_server && + os_strcmp(buf, "auth_server_private_key") == 0) { + os_free(bss->radius->auth_server->private_key); + bss->radius->auth_server->private_key = os_strdup(pos); + } else if (bss->radius->auth_server && + os_strcmp(buf, "auth_server_private_key_passwd") == 0) { + os_free(bss->radius->auth_server->private_key_passwd); + bss->radius->auth_server->private_key_passwd = os_strdup(pos); +#endif /* CONFIG_RADIUS_TLS */ + } else if (os_strcmp(buf, "acct_server_addr") == 0) { + if (hostapd_config_read_radius_addr( + &bss->radius->acct_servers, + &bss->radius->num_acct_servers, pos, 1813, + &bss->radius->acct_server)) { + wpa_printf(MSG_ERROR, + "Line %d: invalid IP address '%s'", + line, pos); + return 1; + } + } else if (bss->radius->acct_server && + os_strcmp(buf, "acct_server_addr_replace") == 0) { + if (hostapd_parse_ip_addr(pos, + &bss->radius->acct_server->addr)) { + wpa_printf(MSG_ERROR, + "Line %d: invalid IP address '%s'", + line, pos); + return 1; + } + } else if (bss->radius->acct_server && + os_strcmp(buf, "acct_server_port") == 0) { + bss->radius->acct_server->port = atoi(pos); + } else if (bss->radius->acct_server && + os_strcmp(buf, "acct_server_shared_secret") == 0) { + int len = os_strlen(pos); + if (len == 0) { + /* RFC 2865, Ch. 3 */ + wpa_printf(MSG_ERROR, "Line %d: empty shared secret is not allowed", + line); + return 1; + } + os_free(bss->radius->acct_server->shared_secret); + bss->radius->acct_server->shared_secret = (u8 *) os_strdup(pos); + bss->radius->acct_server->shared_secret_len = len; + } else if (bss->radius->acct_server && + os_strcmp(buf, "acct_server_type") == 0) { + if (os_strcmp(pos, "UDP") == 0) { + bss->radius->acct_server->tls = false; +#ifdef CONFIG_RADIUS_TLS + } else if (os_strcmp(pos, "TLS") == 0) { + bss->radius->acct_server->tls = true; +#endif /* CONFIG_RADIUS_TLS */ + } else { + wpa_printf(MSG_ERROR, "Line %d: unsupported RADIUS type '%s'", + line, pos); + return 1; + } +#ifdef CONFIG_RADIUS_TLS + } else if (bss->radius->acct_server && + os_strcmp(buf, "acct_server_ca_cert") == 0) { + os_free(bss->radius->acct_server->ca_cert); + bss->radius->acct_server->ca_cert = os_strdup(pos); + } else if (bss->radius->acct_server && + os_strcmp(buf, "acct_server_client_cert") == 0) { + os_free(bss->radius->acct_server->client_cert); + bss->radius->acct_server->client_cert = os_strdup(pos); + } else if (bss->radius->acct_server && + os_strcmp(buf, "acct_server_private_key") == 0) { + os_free(bss->radius->acct_server->private_key); + bss->radius->acct_server->private_key = os_strdup(pos); + } else if (bss->radius->acct_server && + os_strcmp(buf, "acct_server_private_key_passwd") == 0) { + os_free(bss->radius->acct_server->private_key_passwd); + bss->radius->acct_server->private_key_passwd = os_strdup(pos); +#endif /* CONFIG_RADIUS_TLS */ + } else if (os_strcmp(buf, "radius_retry_primary_interval") == 0) { + bss->radius->retry_primary_interval = atoi(pos); + } else if (os_strcmp(buf, + "radius_require_message_authenticator") == 0) { + bss->radius_require_message_authenticator = atoi(pos); + } else if (os_strcmp(buf, "radius_acct_interim_interval") == 0) { + bss->acct_interim_interval = atoi(pos); + } else if (os_strcmp(buf, "radius_request_cui") == 0) { + bss->radius_request_cui = atoi(pos); + } else if (os_strcmp(buf, "radius_auth_req_attr") == 0) { + struct hostapd_radius_attr *attr, *a; + attr = hostapd_parse_radius_attr(pos); + if (attr == NULL) { + wpa_printf(MSG_ERROR, + "Line %d: invalid radius_auth_req_attr", + line); + return 1; + } else if (bss->radius_auth_req_attr == NULL) { + bss->radius_auth_req_attr = attr; + } else { + a = bss->radius_auth_req_attr; + while (a->next) + a = a->next; + a->next = attr; + } + } else if (os_strcmp(buf, "radius_acct_req_attr") == 0) { + struct hostapd_radius_attr *attr, *a; + attr = hostapd_parse_radius_attr(pos); + if (attr == NULL) { + wpa_printf(MSG_ERROR, + "Line %d: invalid radius_acct_req_attr", + line); + return 1; + } else if (bss->radius_acct_req_attr == NULL) { + bss->radius_acct_req_attr = attr; + } else { + a = bss->radius_acct_req_attr; + while (a->next) + a = a->next; + a->next = attr; + } + } else if (os_strcmp(buf, "radius_req_attr_sqlite") == 0) { + os_free(bss->radius_req_attr_sqlite); + bss->radius_req_attr_sqlite = os_strdup(pos); + } else if (os_strcmp(buf, "radius_das_port") == 0) { + bss->radius_das_port = atoi(pos); + } else if (os_strcmp(buf, "radius_das_client") == 0) { + if (hostapd_parse_das_client(bss, pos) < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid DAS client", + line); + return 1; + } + } else if (os_strcmp(buf, "radius_das_time_window") == 0) { + bss->radius_das_time_window = atoi(pos); + } else if (os_strcmp(buf, "radius_das_require_event_timestamp") == 0) { + bss->radius_das_require_event_timestamp = atoi(pos); + } else if (os_strcmp(buf, "radius_das_require_message_authenticator") == + 0) { + bss->radius_das_require_message_authenticator = atoi(pos); +#endif /* CONFIG_NO_RADIUS */ + } else if (os_strcmp(buf, "auth_algs") == 0) { + bss->auth_algs = atoi(pos); + if (bss->auth_algs == 0) { + wpa_printf(MSG_ERROR, "Line %d: no authentication algorithms allowed", + line); + return 1; + } + } else if (os_strcmp(buf, "max_num_sta") == 0) { + bss->max_num_sta = atoi(pos); + if (bss->max_num_sta < 0 || + bss->max_num_sta > MAX_STA_COUNT) { + wpa_printf(MSG_ERROR, "Line %d: Invalid max_num_sta=%d; allowed range 0..%d", + line, bss->max_num_sta, MAX_STA_COUNT); + return 1; + } + } 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; + } else if (os_strcmp(buf, "wpa_strict_rekey") == 0) { + bss->wpa_strict_rekey = atoi(pos); + } else if (os_strcmp(buf, "wpa_gmk_rekey") == 0) { + bss->wpa_gmk_rekey = atoi(pos); + } else if (os_strcmp(buf, "wpa_ptk_rekey") == 0) { + bss->wpa_ptk_rekey = atoi(pos); + } else if (os_strcmp(buf, "wpa_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); + + if (*endp || val < 1 || val > (u32) -1) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid wpa_group_update_count=%lu; allowed range 1..4294967295", + line, val); + return 1; + } + bss->wpa_group_update_count = (u32) val; + } else if (os_strcmp(buf, "wpa_pairwise_update_count") == 0) { + char *endp; + unsigned long val = strtoul(pos, &endp, 0); + + if (*endp || val < 1 || val > (u32) -1) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid wpa_pairwise_update_count=%lu; allowed range 1..4294967295", + line, val); + return 1; + } + bss->wpa_pairwise_update_count = (u32) val; + } else if (os_strcmp(buf, "wpa_disable_eapol_key_retries") == 0) { + bss->wpa_disable_eapol_key_retries = atoi(pos); + } else if (os_strcmp(buf, "wpa_passphrase") == 0) { + int len = os_strlen(pos); + if (len < 8 || len > 63) { + wpa_printf(MSG_ERROR, "Line %d: invalid WPA passphrase length %d (expected 8..63)", + line, len); + return 1; + } + os_free(bss->ssid.wpa_passphrase); + bss->ssid.wpa_passphrase = os_strdup(pos); + if (bss->ssid.wpa_passphrase) { + hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk); + bss->ssid.wpa_passphrase_set = 1; + } + } else if (os_strcmp(buf, "wpa_psk") == 0) { + hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk); + bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk)); + if (bss->ssid.wpa_psk == NULL) + return 1; + if (hexstr2bin(pos, bss->ssid.wpa_psk->psk, PMK_LEN) || + pos[PMK_LEN * 2] != '\0') { + wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.", + line, pos); + hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk); + return 1; + } + bss->ssid.wpa_psk->group = 1; + os_free(bss->ssid.wpa_passphrase); + bss->ssid.wpa_passphrase = NULL; + bss->ssid.wpa_psk_set = 1; + } else if (os_strcmp(buf, "wpa_psk_file") == 0) { + os_free(bss->ssid.wpa_psk_file); + bss->ssid.wpa_psk_file = os_strdup(pos); + if (!bss->ssid.wpa_psk_file) { + wpa_printf(MSG_ERROR, "Line %d: allocation failed", + line); + return 1; + } + } else if (os_strcmp(buf, "wpa_key_mgmt") == 0) { + bss->wpa_key_mgmt = hostapd_config_parse_key_mgmt(line, pos); + if (bss->wpa_key_mgmt == -1) + return 1; + } else if (os_strcmp(buf, "wpa_psk_radius") == 0) { + bss->wpa_psk_radius = atoi(pos); + if (bss->wpa_psk_radius != PSK_RADIUS_IGNORED && + bss->wpa_psk_radius != PSK_RADIUS_ACCEPTED && + bss->wpa_psk_radius != PSK_RADIUS_REQUIRED && + bss->wpa_psk_radius != PSK_RADIUS_DURING_4WAY_HS) { + wpa_printf(MSG_ERROR, + "Line %d: unknown wpa_psk_radius %d", + line, bss->wpa_psk_radius); + return 1; + } + } else if (os_strcmp(buf, "wpa_pairwise") == 0) { + bss->wpa_pairwise = hostapd_config_parse_cipher(line, pos); + if (bss->wpa_pairwise == -1 || bss->wpa_pairwise == 0) + return 1; + if (bss->wpa_pairwise & + (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) { + wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "rsn_pairwise") == 0) { + bss->rsn_pairwise = hostapd_config_parse_cipher(line, pos); + if (bss->rsn_pairwise == -1 || bss->rsn_pairwise == 0) + return 1; + if (bss->rsn_pairwise & + (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) { + wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "group_cipher") == 0) { + bss->group_cipher = hostapd_config_parse_cipher(line, pos); + if (bss->group_cipher == -1 || bss->group_cipher == 0) + return 1; + if (bss->group_cipher != WPA_CIPHER_TKIP && + bss->group_cipher != WPA_CIPHER_CCMP && + bss->group_cipher != WPA_CIPHER_GCMP && + bss->group_cipher != WPA_CIPHER_GCMP_256 && + bss->group_cipher != WPA_CIPHER_CCMP_256) { + wpa_printf(MSG_ERROR, + "Line %d: unsupported group cipher suite '%s'", + line, pos); + return 1; + } +#ifdef CONFIG_RSN_PREAUTH + } else if (os_strcmp(buf, "rsn_preauth") == 0) { + bss->rsn_preauth = atoi(pos); + } else if (os_strcmp(buf, "rsn_preauth_interfaces") == 0) { + os_free(bss->rsn_preauth_interfaces); + bss->rsn_preauth_interfaces = os_strdup(pos); +#endif /* CONFIG_RSN_PREAUTH */ + } else if (os_strcmp(buf, "peerkey") == 0) { + wpa_printf(MSG_INFO, + "Line %d: Obsolete peerkey parameter ignored", line); +#ifdef CONFIG_IEEE80211R_AP + } else if (os_strcmp(buf, "mobility_domain") == 0) { + if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN || + hexstr2bin(pos, bss->mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid mobility_domain '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "r1_key_holder") == 0) { + if (os_strlen(pos) != 2 * FT_R1KH_ID_LEN || + hexstr2bin(pos, bss->r1_key_holder, FT_R1KH_ID_LEN) != 0) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid r1_key_holder '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "r0_key_lifetime") == 0) { + /* DEPRECATED: Use ft_r0_key_lifetime instead. */ + bss->r0_key_lifetime = atoi(pos) * 60; + } else if (os_strcmp(buf, "ft_r0_key_lifetime") == 0) { + bss->r0_key_lifetime = atoi(pos); + } else if (os_strcmp(buf, "r1_max_key_lifetime") == 0) { + bss->r1_max_key_lifetime = atoi(pos); + } else if (os_strcmp(buf, "reassociation_deadline") == 0) { + bss->reassociation_deadline = atoi(pos); + } else if (os_strcmp(buf, "rkh_pos_timeout") == 0) { + bss->rkh_pos_timeout = atoi(pos); + } else if (os_strcmp(buf, "rkh_neg_timeout") == 0) { + bss->rkh_neg_timeout = atoi(pos); + } else if (os_strcmp(buf, "rkh_pull_timeout") == 0) { + bss->rkh_pull_timeout = atoi(pos); + } else if (os_strcmp(buf, "rkh_pull_retries") == 0) { + bss->rkh_pull_retries = atoi(pos); + } else if (os_strcmp(buf, "r0kh") == 0) { + if (add_r0kh(bss, pos) < 0) { + wpa_printf(MSG_DEBUG, "Line %d: Invalid r0kh '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "r1kh") == 0) { + if (add_r1kh(bss, pos) < 0) { + wpa_printf(MSG_DEBUG, "Line %d: Invalid r1kh '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "rxkh_file") == 0) { + os_free(bss->rxkh_file); + bss->rxkh_file = os_strdup(pos); + if (!bss->rxkh_file) { + wpa_printf(MSG_ERROR, "Line %d: allocation failed", + line); + return 1; + } + if (hostapd_config_read_rxkh_file(bss, pos)) { + wpa_printf(MSG_DEBUG, + "Line %d: failed to read rxkh_file '%s'", + line, pos); + /* Allow the file to be created later and read into + * already operating AP context. */ + } + } else if (os_strcmp(buf, "pmk_r1_push") == 0) { + bss->pmk_r1_push = atoi(pos); + } else if (os_strcmp(buf, "ft_over_ds") == 0) { + bss->ft_over_ds = atoi(pos); + } else if (os_strcmp(buf, "ft_psk_generate_local") == 0) { + bss->ft_psk_generate_local = atoi(pos); +#endif /* CONFIG_IEEE80211R_AP */ +#ifndef CONFIG_NO_CTRL_IFACE + } else if (os_strcmp(buf, "ctrl_interface") == 0) { + os_free(bss->ctrl_interface); + bss->ctrl_interface = os_strdup(pos); + } else if (os_strcmp(buf, "ctrl_interface_group") == 0) { +#ifndef CONFIG_NATIVE_WINDOWS + struct group *grp; + char *endp; + const char *group = pos; + + grp = getgrnam(group); + if (grp) { + bss->ctrl_interface_gid = grp->gr_gid; + bss->ctrl_interface_gid_set = 1; + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d (from group name '%s')", + bss->ctrl_interface_gid, group); + return 0; + } + + /* Group name not found - try to parse this as gid */ + bss->ctrl_interface_gid = strtol(group, &endp, 10); + if (*group == '\0' || *endp != '\0') { + wpa_printf(MSG_DEBUG, "Line %d: Invalid group '%s'", + line, group); + return 1; + } + bss->ctrl_interface_gid_set = 1; + wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", + bss->ctrl_interface_gid); +#endif /* CONFIG_NATIVE_WINDOWS */ +#endif /* CONFIG_NO_CTRL_IFACE */ +#ifdef RADIUS_SERVER + } else if (os_strcmp(buf, "radius_server_clients") == 0) { + os_free(bss->radius_server_clients); + bss->radius_server_clients = os_strdup(pos); + } else if (os_strcmp(buf, "radius_server_auth_port") == 0) { + bss->radius_server_auth_port = atoi(pos); + } else if (os_strcmp(buf, "radius_server_acct_port") == 0) { + bss->radius_server_acct_port = atoi(pos); + } else if (os_strcmp(buf, "radius_server_ipv6") == 0) { + bss->radius_server_ipv6 = atoi(pos); +#endif /* RADIUS_SERVER */ + } else if (os_strcmp(buf, "use_pae_group_addr") == 0) { + bss->use_pae_group_addr = atoi(pos); + } else if (os_strcmp(buf, "hw_mode") == 0) { + if (os_strcmp(pos, "a") == 0) + conf->hw_mode = HOSTAPD_MODE_IEEE80211A; + else if (os_strcmp(pos, "b") == 0) + conf->hw_mode = HOSTAPD_MODE_IEEE80211B; + else if (os_strcmp(pos, "g") == 0) + conf->hw_mode = HOSTAPD_MODE_IEEE80211G; + else if (os_strcmp(pos, "ad") == 0) + conf->hw_mode = HOSTAPD_MODE_IEEE80211AD; + else if (os_strcmp(pos, "any") == 0) + conf->hw_mode = HOSTAPD_MODE_IEEE80211ANY; + else { + wpa_printf(MSG_ERROR, "Line %d: unknown hw_mode '%s'", + line, pos); + return 1; + } + conf->hw_mode_set = true; + } else if (os_strcmp(buf, "wps_rf_bands") == 0) { + if (os_strcmp(pos, "ad") == 0) + bss->wps_rf_bands = WPS_RF_60GHZ; + else if (os_strcmp(pos, "a") == 0) + bss->wps_rf_bands = WPS_RF_50GHZ; + else if (os_strcmp(pos, "g") == 0 || + os_strcmp(pos, "b") == 0) + bss->wps_rf_bands = WPS_RF_24GHZ; + else if (os_strcmp(pos, "ag") == 0 || + os_strcmp(pos, "ga") == 0) + bss->wps_rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ; + else { + wpa_printf(MSG_ERROR, + "Line %d: unknown wps_rf_band '%s'", + line, pos); + return 1; + } + } 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 + wpa_printf(MSG_ERROR, "Line %d: tries to enable ACS but CONFIG_ACS disabled", + line); + return 1; +#else /* CONFIG_ACS */ + conf->acs = 1; + conf->channel = 0; +#endif /* CONFIG_ACS */ + } else { + 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, "enable_background_radar") == 0) { + conf->enable_background_radar = atoi(pos); + } else if (os_strcmp(buf, "min_tx_power") == 0) { + int val = atoi(pos); + + if (val < 0 || val > 255) { + wpa_printf(MSG_ERROR, + "Line %d: invalid min_tx_power %d (expected 0..255)", + line, val); + return 1; + } + conf->min_tx_power = val; + } else if (os_strcmp(buf, "beacon_int") == 0) { + int val = atoi(pos); + /* MIB defines range as 1..65535, but very small values + * cause problems with the current implementation. + * Since it is unlikely that this small numbers are + * useful in real life scenarios, do not allow beacon + * period to be set below 10 TU. */ + if (val < 10 || val > 65535) { + wpa_printf(MSG_ERROR, + "Line %d: invalid beacon_int %d (expected 10..65535)", + line, val); + return 1; + } + conf->beacon_int = val; +#ifdef CONFIG_ACS + } else if (os_strcmp(buf, "acs_num_scans") == 0) { + int val = atoi(pos); + if (val <= 0 || val > 100) { + wpa_printf(MSG_ERROR, "Line %d: invalid acs_num_scans %d (expected 1..100)", + line, val); + return 1; + } + conf->acs_num_scans = val; + } else if (os_strcmp(buf, "acs_chan_bias") == 0) { + if (hostapd_config_parse_acs_chan_bias(conf, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid acs_chan_bias", + line); + return -1; + } +#endif /* CONFIG_ACS */ + } else if (os_strcmp(buf, "dtim_period") == 0) { + int val = atoi(pos); + + if (val < 1 || val > 255) { + wpa_printf(MSG_ERROR, "Line %d: invalid dtim_period %d", + line, val); + return 1; + } + bss->dtim_period = val; + } else if (os_strcmp(buf, "bss_load_update_period") == 0) { + int val = atoi(pos); + + if (val < 0 || val > 100) { + wpa_printf(MSG_ERROR, + "Line %d: invalid bss_load_update_period %d", + line, val); + return 1; + } + bss->bss_load_update_period = val; + } else if (os_strcmp(buf, "chan_util_avg_period") == 0) { + int val = atoi(pos); + + if (val < 0) { + wpa_printf(MSG_ERROR, + "Line %d: invalid chan_util_avg_period", + line); + return 1; + } + bss->chan_util_avg_period = val; + } else if (os_strcmp(buf, "rts_threshold") == 0) { + conf->rts_threshold = atoi(pos); + if (conf->rts_threshold < -1 || conf->rts_threshold > 65535) { + wpa_printf(MSG_ERROR, + "Line %d: invalid rts_threshold %d", + line, conf->rts_threshold); + return 1; + } + } else if (os_strcmp(buf, "fragm_threshold") == 0) { + conf->fragm_threshold = atoi(pos); + if (conf->fragm_threshold == -1) { + /* allow a value of -1 */ + } else if (conf->fragm_threshold < 256 || + conf->fragm_threshold > 2346) { + wpa_printf(MSG_ERROR, + "Line %d: invalid fragm_threshold %d", + line, conf->fragm_threshold); + return 1; + } + } else if (os_strcmp(buf, "send_probe_response") == 0) { + int val = atoi(pos); + if (val != 0 && val != 1) { + wpa_printf(MSG_ERROR, "Line %d: invalid send_probe_response %d (expected 0 or 1)", + line, val); + return 1; + } + bss->send_probe_response = val; + } else if (os_strcmp(buf, "supported_rates") == 0) { + if (hostapd_parse_intlist(&conf->supported_rates, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid rate list", + line); + return 1; + } + } else if (os_strcmp(buf, "basic_rates") == 0) { + if (hostapd_parse_intlist(&conf->basic_rates, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid rate list", + line); + return 1; + } + } else if (os_strcmp(buf, "beacon_rate") == 0) { + int val; + + if (os_strncmp(pos, "ht:", 3) == 0) { + val = atoi(pos + 3); + if (val < 0 || val > 31) { + wpa_printf(MSG_ERROR, + "Line %d: invalid beacon_rate HT-MCS %d", + line, val); + return 1; + } + conf->rate_type = BEACON_RATE_HT; + conf->beacon_rate = val; + } else if (os_strncmp(pos, "vht:", 4) == 0) { + val = atoi(pos + 4); + if (val < 0 || val > 9) { + wpa_printf(MSG_ERROR, + "Line %d: invalid beacon_rate VHT-MCS %d", + line, val); + return 1; + } + conf->rate_type = BEACON_RATE_VHT; + conf->beacon_rate = val; + } else 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) { + wpa_printf(MSG_ERROR, + "Line %d: invalid legacy beacon_rate %d", + line, val); + return 1; + } + conf->rate_type = BEACON_RATE_LEGACY; + conf->beacon_rate = val; + } + } else if (os_strcmp(buf, "preamble") == 0) { + if (atoi(pos)) + conf->preamble = SHORT_PREAMBLE; + else + conf->preamble = LONG_PREAMBLE; + } else if (os_strcmp(buf, "ignore_broadcast_ssid") == 0) { + 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) { + wpa_printf(MSG_ERROR, + "Invalid wep_default_key index %d", + bss->ssid.wep.idx); + return 1; + } + } else if (os_strcmp(buf, "wep_key0") == 0 || + os_strcmp(buf, "wep_key1") == 0 || + os_strcmp(buf, "wep_key2") == 0 || + os_strcmp(buf, "wep_key3") == 0) { + if (hostapd_config_read_wep(&bss->ssid.wep, + buf[7] - '0', pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid WEP key '%s'", + 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); + } else if (os_strcmp(buf, "per_sta_vif") == 0) { + bss->ssid.per_sta_vif = atoi(pos); + } else if (os_strcmp(buf, "vlan_file") == 0) { + if (hostapd_config_read_vlan_file(bss, pos)) { + wpa_printf(MSG_ERROR, "Line %d: failed to read VLAN file '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "vlan_naming") == 0) { + bss->ssid.vlan_naming = atoi(pos); + if (bss->ssid.vlan_naming >= DYNAMIC_VLAN_NAMING_END || + bss->ssid.vlan_naming < 0) { + wpa_printf(MSG_ERROR, + "Line %d: invalid naming scheme %d", + line, bss->ssid.vlan_naming); + return 1; + } +#ifdef CONFIG_FULL_DYNAMIC_VLAN + } else if (os_strcmp(buf, "vlan_tagged_interface") == 0) { + os_free(bss->ssid.vlan_tagged_interface); + bss->ssid.vlan_tagged_interface = os_strdup(pos); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ +#endif /* CONFIG_NO_VLAN */ + } else if (os_strcmp(buf, "ap_table_max_size") == 0) { + conf->ap_table_max_size = atoi(pos); + } 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->tx_queue, buf, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid TX queue item", + line); + return 1; + } + } else if (os_strcmp(buf, "wme_enabled") == 0 || + os_strcmp(buf, "wmm_enabled") == 0) { + bss->wmm_enabled = atoi(pos); + } else if (os_strcmp(buf, "uapsd_advertisement_enabled") == 0) { + bss->wmm_uapsd = atoi(pos); + } else if (os_strncmp(buf, "wme_ac_", 7) == 0 || + os_strncmp(buf, "wmm_ac_", 7) == 0) { + if (hostapd_config_wmm_ac(conf->wmm_ac_params, buf, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid WMM ac item", + line); + return 1; + } + } else if (os_strcmp(buf, "bss") == 0) { + if (hostapd_config_bss(conf, pos)) { + wpa_printf(MSG_ERROR, "Line %d: invalid bss item", + line); + return 1; + } + } else if (os_strcmp(buf, "bssid") == 0) { + if (hwaddr_aton(pos, bss->bssid)) { + wpa_printf(MSG_ERROR, "Line %d: invalid bssid item", + line); + return 1; + } + } else if (os_strcmp(buf, "use_driver_iface_addr") == 0) { + conf->use_driver_iface_addr = atoi(pos); + } else if (os_strcmp(buf, "ieee80211w") == 0) { + bss->ieee80211w = atoi(pos); + } else if (os_strcmp(buf, "group_mgmt_cipher") == 0) { + if (os_strcmp(pos, "AES-128-CMAC") == 0) { + bss->group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC; + } else if (os_strcmp(pos, "BIP-GMAC-128") == 0) { + bss->group_mgmt_cipher = WPA_CIPHER_BIP_GMAC_128; + } else if (os_strcmp(pos, "BIP-GMAC-256") == 0) { + bss->group_mgmt_cipher = WPA_CIPHER_BIP_GMAC_256; + } else if (os_strcmp(pos, "BIP-CMAC-256") == 0) { + bss->group_mgmt_cipher = WPA_CIPHER_BIP_CMAC_256; + } else { + wpa_printf(MSG_ERROR, "Line %d: invalid group_mgmt_cipher: %s", + 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) { + wpa_printf(MSG_ERROR, "Line %d: invalid assoc_sa_query_max_timeout", + line); + return 1; + } + } else if (os_strcmp(buf, "assoc_sa_query_retry_timeout") == 0) { + bss->assoc_sa_query_retry_timeout = atoi(pos); + if (bss->assoc_sa_query_retry_timeout == 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid assoc_sa_query_retry_timeout", + line); + return 1; + } +#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 */ + } else if (os_strcmp(buf, "ieee80211n") == 0) { + conf->ieee80211n = atoi(pos); + } else if (os_strcmp(buf, "ht_capab") == 0) { + if (hostapd_config_ht_capab(conf, pos) < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid ht_capab", + line); + return 1; + } + } else if (os_strcmp(buf, "require_ht") == 0) { + conf->require_ht = atoi(pos); + } else if (os_strcmp(buf, "ht_vht_twt_responder") == 0) { + conf->ht_vht_twt_responder = atoi(pos); + } else if (os_strcmp(buf, "obss_interval") == 0) { + conf->obss_interval = atoi(pos); +#ifdef CONFIG_IEEE80211AC + } else if (os_strcmp(buf, "ieee80211ac") == 0) { + conf->ieee80211ac = atoi(pos); + } else if (os_strcmp(buf, "vht_capab") == 0) { + if (hostapd_config_vht_capab(conf, pos) < 0) { + wpa_printf(MSG_ERROR, "Line %d: invalid vht_capab", + line); + return 1; + } + } else if (os_strcmp(buf, "require_vht") == 0) { + conf->require_vht = atoi(pos); + } else if (os_strcmp(buf, "vht_oper_chwidth") == 0) { + conf->vht_oper_chwidth = atoi(pos); + } else if (os_strcmp(buf, "vht_oper_centr_freq_seg0_idx") == 0) { + conf->vht_oper_centr_freq_seg0_idx = atoi(pos); + } else if (os_strcmp(buf, "vht_oper_centr_freq_seg1_idx") == 0) { + conf->vht_oper_centr_freq_seg1_idx = atoi(pos); + } else if (os_strcmp(buf, "vendor_vht") == 0) { + bss->vendor_vht = atoi(pos); + } else if (os_strcmp(buf, "use_sta_nsts") == 0) { + bss->use_sta_nsts = atoi(pos); +#endif /* CONFIG_IEEE80211AC */ +#ifdef CONFIG_IEEE80211AX + } else if (os_strcmp(buf, "ieee80211ax") == 0) { + conf->ieee80211ax = atoi(pos); + } else if (os_strcmp(buf, "require_he") == 0) { + conf->require_he = atoi(pos); + } else if (os_strcmp(buf, "he_su_beamformer") == 0) { + conf->he_phy_capab.he_su_beamformer = atoi(pos); + } else if (os_strcmp(buf, "he_su_beamformee") == 0) { + conf->he_phy_capab.he_su_beamformee = atoi(pos); + } else if (os_strcmp(buf, "he_mu_beamformer") == 0) { + conf->he_phy_capab.he_mu_beamformer = atoi(pos); + } else if (os_strcmp(buf, "he_bss_color") == 0) { + conf->he_op.he_bss_color = atoi(pos) & 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_er_su_disable") == 0) { + conf->he_op.he_er_su_disable = atoi(pos); + } else if (os_strcmp(buf, "he_basic_mcs_nss_set") == 0) { + conf->he_op.he_basic_mcs_nss_set = atoi(pos); + } else if (os_strcmp(buf, "he_mu_edca_qos_info_param_count") == 0) { + conf->he_mu_edca.he_qos_info |= + set_he_cap(atoi(pos), HE_QOS_INFO_EDCA_PARAM_SET_COUNT); + } else if (os_strcmp(buf, "he_mu_edca_qos_info_q_ack") == 0) { + conf->he_mu_edca.he_qos_info |= + set_he_cap(atoi(pos), HE_QOS_INFO_Q_ACK); + } else if (os_strcmp(buf, "he_mu_edca_qos_info_queue_request") == 0) { + conf->he_mu_edca.he_qos_info |= + set_he_cap(atoi(pos), HE_QOS_INFO_QUEUE_REQUEST); + } else if (os_strcmp(buf, "he_mu_edca_qos_info_txop_request") == 0) { + conf->he_mu_edca.he_qos_info |= + set_he_cap(atoi(pos), HE_QOS_INFO_TXOP_REQUEST); + } else if (os_strcmp(buf, "he_mu_edca_ac_be_aifsn") == 0) { + conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN); + } else if (os_strcmp(buf, "he_mu_edca_ac_be_acm") == 0) { + conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM); + } else if (os_strcmp(buf, "he_mu_edca_ac_be_aci") == 0) { + conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI); + } else if (os_strcmp(buf, "he_mu_edca_ac_be_ecwmin") == 0) { + conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN); + } else if (os_strcmp(buf, "he_mu_edca_ac_be_ecwmax") == 0) { + conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX); + } else if (os_strcmp(buf, "he_mu_edca_ac_be_timer") == 0) { + conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_TIMER_IDX] = + atoi(pos) & 0xff; + } else if (os_strcmp(buf, "he_mu_edca_ac_bk_aifsn") == 0) { + conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN); + } else if (os_strcmp(buf, "he_mu_edca_ac_bk_acm") == 0) { + conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM); + } else if (os_strcmp(buf, "he_mu_edca_ac_bk_aci") == 0) { + conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI); + } else if (os_strcmp(buf, "he_mu_edca_ac_bk_ecwmin") == 0) { + conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN); + } else if (os_strcmp(buf, "he_mu_edca_ac_bk_ecwmax") == 0) { + conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX); + } else if (os_strcmp(buf, "he_mu_edca_ac_bk_timer") == 0) { + conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_TIMER_IDX] = + atoi(pos) & 0xff; + } else if (os_strcmp(buf, "he_mu_edca_ac_vi_aifsn") == 0) { + conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN); + } else if (os_strcmp(buf, "he_mu_edca_ac_vi_acm") == 0) { + conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM); + } else if (os_strcmp(buf, "he_mu_edca_ac_vi_aci") == 0) { + conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI); + } else if (os_strcmp(buf, "he_mu_edca_ac_vi_ecwmin") == 0) { + conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN); + } else if (os_strcmp(buf, "he_mu_edca_ac_vi_ecwmax") == 0) { + conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX); + } else if (os_strcmp(buf, "he_mu_edca_ac_vi_timer") == 0) { + conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_TIMER_IDX] = + atoi(pos) & 0xff; + } else if (os_strcmp(buf, "he_mu_edca_ac_vo_aifsn") == 0) { + conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN); + } else if (os_strcmp(buf, "he_mu_edca_ac_vo_acm") == 0) { + conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM); + } else if (os_strcmp(buf, "he_mu_edca_ac_vo_aci") == 0) { + conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI); + } else if (os_strcmp(buf, "he_mu_edca_ac_vo_ecwmin") == 0) { + conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN); + } else if (os_strcmp(buf, "he_mu_edca_ac_vo_ecwmax") == 0) { + conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ECW_IDX] |= + set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX); + } else if (os_strcmp(buf, "he_mu_edca_ac_vo_timer") == 0) { + 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) & 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_6ghz_reg_pwr_type") == 0) { + conf->he_6ghz_reg_pwr_type = atoi(pos); + if (conf->he_6ghz_reg_pwr_type > HE_REG_INFO_6GHZ_AP_TYPE_MAX) { + wpa_printf(MSG_ERROR, + "Line %d: invalid he_6ghz_reg_pwr_type value", + line); + return 1; + } + } else if (os_strcmp(buf, "reg_def_cli_eirp_psd") == 0) { + conf->reg_def_cli_eirp_psd = atoi(pos); + } else if (os_strcmp(buf, "reg_sub_cli_eirp_psd") == 0) { + conf->reg_sub_cli_eirp_psd = atoi(pos); + } else if (os_strcmp(buf, "reg_def_cli_eirp") == 0) { + conf->reg_def_cli_eirp = atoi(pos); + } 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; + } else if (os_strcmp(buf, "mbssid") == 0) { + int mbssid = atoi(pos); + if (mbssid < 0 || mbssid > ENHANCED_MBSSID_ENABLED) { + wpa_printf(MSG_ERROR, + "Line %d: invalid mbssid (%d): '%s'.", + line, mbssid, pos); + return 1; + } + conf->mbssid = mbssid; +#endif /* CONFIG_IEEE80211AX */ + } else if (os_strcmp(buf, "max_listen_interval") == 0) { + bss->max_listen_interval = atoi(pos); + } else if (os_strcmp(buf, "disable_pmksa_caching") == 0) { + bss->disable_pmksa_caching = atoi(pos); + } else if (os_strcmp(buf, "okc") == 0) { + bss->okc = atoi(pos); +#ifdef CONFIG_WPS + } else if (os_strcmp(buf, "wps_state") == 0) { + bss->wps_state = atoi(pos); + if (bss->wps_state < 0 || bss->wps_state > 2) { + wpa_printf(MSG_ERROR, "Line %d: invalid wps_state", + line); + return 1; + } + } else if (os_strcmp(buf, "wps_independent") == 0) { + bss->wps_independent = atoi(pos); + } else if (os_strcmp(buf, "ap_setup_locked") == 0) { + bss->ap_setup_locked = atoi(pos); + } else if (os_strcmp(buf, "uuid") == 0) { + if (uuid_str2bin(pos, bss->uuid)) { + wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line); + return 1; + } + } else if (os_strcmp(buf, "wps_pin_requests") == 0) { + os_free(bss->wps_pin_requests); + bss->wps_pin_requests = os_strdup(pos); + } else if (os_strcmp(buf, "device_name") == 0) { + if (os_strlen(pos) > WPS_DEV_NAME_MAX_LEN) { + wpa_printf(MSG_ERROR, "Line %d: Too long " + "device_name", line); + return 1; + } + os_free(bss->device_name); + bss->device_name = os_strdup(pos); + } else if (os_strcmp(buf, "manufacturer") == 0) { + if (os_strlen(pos) > 64) { + wpa_printf(MSG_ERROR, "Line %d: Too long manufacturer", + line); + return 1; + } + os_free(bss->manufacturer); + bss->manufacturer = os_strdup(pos); + } else if (os_strcmp(buf, "model_name") == 0) { + if (os_strlen(pos) > 32) { + wpa_printf(MSG_ERROR, "Line %d: Too long model_name", + line); + return 1; + } + os_free(bss->model_name); + bss->model_name = os_strdup(pos); + } else if (os_strcmp(buf, "model_number") == 0) { + if (os_strlen(pos) > 32) { + wpa_printf(MSG_ERROR, "Line %d: Too long model_number", + line); + return 1; + } + os_free(bss->model_number); + bss->model_number = os_strdup(pos); + } else if (os_strcmp(buf, "serial_number") == 0) { + if (os_strlen(pos) > 32) { + wpa_printf(MSG_ERROR, "Line %d: Too long serial_number", + line); + return 1; + } + os_free(bss->serial_number); + bss->serial_number = os_strdup(pos); + } else if (os_strcmp(buf, "device_type") == 0) { + if (wps_dev_type_str2bin(pos, bss->device_type)) + return 1; + } else if (os_strcmp(buf, "config_methods") == 0) { + os_free(bss->config_methods); + bss->config_methods = os_strdup(pos); + } else if (os_strcmp(buf, "os_version") == 0) { + if (hexstr2bin(pos, bss->os_version, 4)) { + wpa_printf(MSG_ERROR, "Line %d: invalid os_version", + line); + return 1; + } + } else if (os_strcmp(buf, "ap_pin") == 0) { + os_free(bss->ap_pin); + if (*pos == '\0') + bss->ap_pin = NULL; + else + bss->ap_pin = os_strdup(pos); + } else if (os_strcmp(buf, "skip_cred_build") == 0) { + bss->skip_cred_build = atoi(pos); + } else if (os_strcmp(buf, "extra_cred") == 0) { + os_free(bss->extra_cred); + bss->extra_cred = (u8 *) os_readfile(pos, &bss->extra_cred_len); + if (bss->extra_cred == NULL) { + wpa_printf(MSG_ERROR, "Line %d: could not read Credentials from '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "wps_cred_processing") == 0) { + bss->wps_cred_processing = atoi(pos); + } else if (os_strcmp(buf, "wps_cred_add_sae") == 0) { + bss->wps_cred_add_sae = atoi(pos); + } else if (os_strcmp(buf, "ap_settings") == 0) { + os_free(bss->ap_settings); + bss->ap_settings = + (u8 *) os_readfile(pos, &bss->ap_settings_len); + if (bss->ap_settings == NULL) { + wpa_printf(MSG_ERROR, "Line %d: could not read AP Settings from '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "multi_ap_backhaul_ssid") == 0) { + size_t slen; + char *str = wpa_config_parse_string(pos, &slen); + + if (!str || slen < 1 || slen > SSID_MAX_LEN) { + wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'", + line, pos); + os_free(str); + return 1; + } + os_memcpy(bss->multi_ap_backhaul_ssid.ssid, str, slen); + bss->multi_ap_backhaul_ssid.ssid_len = slen; + bss->multi_ap_backhaul_ssid.ssid_set = 1; + os_free(str); + } else if (os_strcmp(buf, "multi_ap_backhaul_wpa_passphrase") == 0) { + int len = os_strlen(pos); + + if (len < 8 || len > 63) { + wpa_printf(MSG_ERROR, + "Line %d: invalid WPA passphrase length %d (expected 8..63)", + line, len); + return 1; + } + os_free(bss->multi_ap_backhaul_ssid.wpa_passphrase); + bss->multi_ap_backhaul_ssid.wpa_passphrase = os_strdup(pos); + if (bss->multi_ap_backhaul_ssid.wpa_passphrase) { + hostapd_config_clear_wpa_psk( + &bss->multi_ap_backhaul_ssid.wpa_psk); + bss->multi_ap_backhaul_ssid.wpa_passphrase_set = 1; + } + } else if (os_strcmp(buf, "multi_ap_backhaul_wpa_psk") == 0) { + hostapd_config_clear_wpa_psk( + &bss->multi_ap_backhaul_ssid.wpa_psk); + bss->multi_ap_backhaul_ssid.wpa_psk = + os_zalloc(sizeof(struct hostapd_wpa_psk)); + if (!bss->multi_ap_backhaul_ssid.wpa_psk) + return 1; + if (hexstr2bin(pos, bss->multi_ap_backhaul_ssid.wpa_psk->psk, + PMK_LEN) || + pos[PMK_LEN * 2] != '\0') { + wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.", + line, pos); + hostapd_config_clear_wpa_psk( + &bss->multi_ap_backhaul_ssid.wpa_psk); + return 1; + } + bss->multi_ap_backhaul_ssid.wpa_psk->group = 1; + os_free(bss->multi_ap_backhaul_ssid.wpa_passphrase); + bss->multi_ap_backhaul_ssid.wpa_passphrase = NULL; + bss->multi_ap_backhaul_ssid.wpa_psk_set = 1; + } else if (os_strcmp(buf, "upnp_iface") == 0) { + os_free(bss->upnp_iface); + bss->upnp_iface = os_strdup(pos); + } else if (os_strcmp(buf, "friendly_name") == 0) { + os_free(bss->friendly_name); + bss->friendly_name = os_strdup(pos); + } else if (os_strcmp(buf, "manufacturer_url") == 0) { + os_free(bss->manufacturer_url); + bss->manufacturer_url = os_strdup(pos); + } else if (os_strcmp(buf, "model_description") == 0) { + os_free(bss->model_description); + bss->model_description = os_strdup(pos); + } else if (os_strcmp(buf, "model_url") == 0) { + os_free(bss->model_url); + bss->model_url = os_strdup(pos); + } else if (os_strcmp(buf, "upc") == 0) { + os_free(bss->upc); + bss->upc = os_strdup(pos); + } else if (os_strcmp(buf, "pbc_in_m1") == 0) { + bss->pbc_in_m1 = atoi(pos); + } 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); + if (bss->wps_nfc_dev_pw_id < 0x10 || + bss->wps_nfc_dev_pw_id > 0xffff) { + wpa_printf(MSG_ERROR, "Line %d: Invalid wps_nfc_dev_pw_id value", + line); + return 1; + } + bss->wps_nfc_pw_from_config = 1; + } else if (os_strcmp(buf, "wps_nfc_dh_pubkey") == 0) { + wpabuf_free(bss->wps_nfc_dh_pubkey); + bss->wps_nfc_dh_pubkey = wpabuf_parse_bin(pos); + bss->wps_nfc_pw_from_config = 1; + } else if (os_strcmp(buf, "wps_nfc_dh_privkey") == 0) { + wpabuf_free(bss->wps_nfc_dh_privkey); + bss->wps_nfc_dh_privkey = wpabuf_parse_bin(pos); + bss->wps_nfc_pw_from_config = 1; + } else if (os_strcmp(buf, "wps_nfc_dev_pw") == 0) { + wpabuf_free(bss->wps_nfc_dev_pw); + bss->wps_nfc_dev_pw = wpabuf_parse_bin(pos); + bss->wps_nfc_pw_from_config = 1; +#endif /* CONFIG_WPS_NFC */ +#endif /* CONFIG_WPS */ +#ifdef CONFIG_P2P_MANAGER + } else if (os_strcmp(buf, "manage_p2p") == 0) { + if (atoi(pos)) + bss->p2p |= P2P_MANAGE; + else + bss->p2p &= ~P2P_MANAGE; + } else if (os_strcmp(buf, "allow_cross_connection") == 0) { + if (atoi(pos)) + bss->p2p |= P2P_ALLOW_CROSS_CONNECTION; + else + bss->p2p &= ~P2P_ALLOW_CROSS_CONNECTION; +#endif /* CONFIG_P2P_MANAGER */ + } else if (os_strcmp(buf, "disassoc_low_ack") == 0) { + bss->disassoc_low_ack = atoi(pos); + } else if (os_strcmp(buf, "tdls_prohibit") == 0) { + if (atoi(pos)) + bss->tdls |= TDLS_PROHIBIT; + else + bss->tdls &= ~TDLS_PROHIBIT; + } else if (os_strcmp(buf, "tdls_prohibit_chan_switch") == 0) { + if (atoi(pos)) + bss->tdls |= TDLS_PROHIBIT_CHAN_SWITCH; + else + bss->tdls &= ~TDLS_PROHIBIT_CHAN_SWITCH; +#ifdef CONFIG_RSN_TESTING + } else if (os_strcmp(buf, "rsn_testing") == 0) { + extern int rsn_testing; + rsn_testing = atoi(pos); +#endif /* CONFIG_RSN_TESTING */ + } else if (os_strcmp(buf, "time_advertisement") == 0) { + bss->time_advertisement = atoi(pos); + } else if (os_strcmp(buf, "time_zone") == 0) { + size_t tz_len = os_strlen(pos); + if (tz_len < 4 || tz_len > 255) { + wpa_printf(MSG_DEBUG, "Line %d: invalid time_zone", + line); + return 1; + } + os_free(bss->time_zone); + bss->time_zone = os_strdup(pos); + if (bss->time_zone == NULL) + return 1; +#ifdef CONFIG_WNM_AP + } else if (os_strcmp(buf, "wnm_sleep_mode") == 0) { + bss->wnm_sleep_mode = atoi(pos); + } else if (os_strcmp(buf, "wnm_sleep_mode_no_keys") == 0) { + bss->wnm_sleep_mode_no_keys = atoi(pos); + } else if (os_strcmp(buf, "bss_transition") == 0) { + bss->bss_transition = atoi(pos); +#endif /* CONFIG_WNM_AP */ +#ifdef CONFIG_INTERWORKING + } else if (os_strcmp(buf, "interworking") == 0) { + bss->interworking = atoi(pos); + } else if (os_strcmp(buf, "access_network_type") == 0) { + bss->access_network_type = atoi(pos); + if (bss->access_network_type < 0 || + bss->access_network_type > 15) { + wpa_printf(MSG_ERROR, + "Line %d: invalid access_network_type", + line); + return 1; + } + } else if (os_strcmp(buf, "internet") == 0) { + bss->internet = atoi(pos); + } else if (os_strcmp(buf, "asra") == 0) { + bss->asra = atoi(pos); + } else if (os_strcmp(buf, "esr") == 0) { + bss->esr = atoi(pos); + } else if (os_strcmp(buf, "uesa") == 0) { + bss->uesa = atoi(pos); + } else if (os_strcmp(buf, "venue_group") == 0) { + bss->venue_group = atoi(pos); + bss->venue_info_set = 1; + } else if (os_strcmp(buf, "venue_type") == 0) { + bss->venue_type = atoi(pos); + bss->venue_info_set = 1; + } else if (os_strcmp(buf, "hessid") == 0) { + if (hwaddr_aton(pos, bss->hessid)) { + wpa_printf(MSG_ERROR, "Line %d: invalid hessid", line); + return 1; + } + } else if (os_strcmp(buf, "roaming_consortium") == 0) { + if (parse_roaming_consortium(bss, pos, line) < 0) + return 1; + } else if (os_strcmp(buf, "venue_name") == 0) { + if (parse_venue_name(bss, pos, line) < 0) + return 1; + } else if (os_strcmp(buf, "venue_url") == 0) { + if (parse_venue_url(bss, pos, line) < 0) + return 1; + } else if (os_strcmp(buf, "network_auth_type") == 0) { + u8 auth_type; + u16 redirect_url_len; + if (hexstr2bin(pos, &auth_type, 1)) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid network_auth_type '%s'", + line, pos); + return 1; + } + if (auth_type == 0 || auth_type == 2) + redirect_url_len = os_strlen(pos + 2); + else + redirect_url_len = 0; + os_free(bss->network_auth_type); + bss->network_auth_type = os_malloc(redirect_url_len + 3 + 1); + if (bss->network_auth_type == NULL) + return 1; + *bss->network_auth_type = auth_type; + WPA_PUT_LE16(bss->network_auth_type + 1, redirect_url_len); + if (redirect_url_len) + os_memcpy(bss->network_auth_type + 3, pos + 2, + redirect_url_len); + bss->network_auth_type_len = 3 + redirect_url_len; + } else if (os_strcmp(buf, "ipaddr_type_availability") == 0) { + if (hexstr2bin(pos, &bss->ipaddr_type_availability, 1)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid ipaddr_type_availability '%s'", + line, pos); + bss->ipaddr_type_configured = 0; + return 1; + } + bss->ipaddr_type_configured = 1; + } else if (os_strcmp(buf, "domain_name") == 0) { + int j, num_domains, domain_len, domain_list_len = 0; + char *tok_start, *tok_prev; + u8 *domain_list, *domain_ptr; + + domain_list_len = os_strlen(pos) + 1; + domain_list = os_malloc(domain_list_len); + if (domain_list == NULL) + return 1; + + domain_ptr = domain_list; + tok_prev = pos; + num_domains = 1; + while ((tok_prev = os_strchr(tok_prev, ','))) { + num_domains++; + tok_prev++; + } + tok_prev = pos; + for (j = 0; j < num_domains; j++) { + tok_start = os_strchr(tok_prev, ','); + if (tok_start) { + domain_len = tok_start - tok_prev; + *domain_ptr = domain_len; + os_memcpy(domain_ptr + 1, tok_prev, domain_len); + domain_ptr += domain_len + 1; + tok_prev = ++tok_start; + } else { + domain_len = os_strlen(tok_prev); + *domain_ptr = domain_len; + os_memcpy(domain_ptr + 1, tok_prev, domain_len); + domain_ptr += domain_len + 1; + } + } + + os_free(bss->domain_name); + bss->domain_name = domain_list; + bss->domain_name_len = domain_list_len; + } else if (os_strcmp(buf, "anqp_3gpp_cell_net") == 0) { + if (parse_3gpp_cell_net(bss, pos, line) < 0) + return 1; + } else if (os_strcmp(buf, "nai_realm") == 0) { + if (parse_nai_realm(bss, pos, line) < 0) + return 1; + } else if (os_strcmp(buf, "anqp_elem") == 0) { + if (parse_anqp_elem(bss, pos, line) < 0) + return 1; + } else if (os_strcmp(buf, "gas_frag_limit") == 0) { + int val = atoi(pos); + + if (val <= 0) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid gas_frag_limit '%s'", + line, pos); + return 1; + } + bss->gas_frag_limit = val; + } else if (os_strcmp(buf, "gas_comeback_delay") == 0) { + bss->gas_comeback_delay = atoi(pos); +#endif /* CONFIG_INTERWORKING */ + } else if (os_strcmp(buf, "qos_map_set") == 0) { + if (parse_qos_map_set(bss, pos, line) < 0) + return 1; +#ifdef CONFIG_RADIUS_TEST + } else if (os_strcmp(buf, "dump_msk_file") == 0) { + os_free(bss->dump_msk_file); + bss->dump_msk_file = os_strdup(pos); +#endif /* CONFIG_RADIUS_TEST */ +#ifdef CONFIG_PROXYARP + } else if (os_strcmp(buf, "proxy_arp") == 0) { + bss->proxy_arp = atoi(pos); +#endif /* CONFIG_PROXYARP */ +#ifdef CONFIG_HS20 + } else if (os_strcmp(buf, "hs20") == 0) { + bss->hs20 = atoi(pos); + } else if (os_strcmp(buf, "hs20_release") == 0) { + int val = atoi(pos); + + if (val < 1 || val > (HS20_VERSION >> 4) + 1) { + wpa_printf(MSG_ERROR, + "Line %d: Unsupported hs20_release: %s", + line, pos); + return 1; + } + bss->hs20_release = val; + } else if (os_strcmp(buf, "disable_dgaf") == 0) { + bss->disable_dgaf = atoi(pos); + } else if (os_strcmp(buf, "na_mcast_to_ucast") == 0) { + bss->na_mcast_to_ucast = atoi(pos); + } else if (os_strcmp(buf, "osen") == 0) { + bss->osen = atoi(pos); + } else if (os_strcmp(buf, "anqp_domain_id") == 0) { + bss->anqp_domain_id = atoi(pos); + } else if (os_strcmp(buf, "hs20_deauth_req_timeout") == 0) { + bss->hs20_deauth_req_timeout = atoi(pos); + } else if (os_strcmp(buf, "hs20_oper_friendly_name") == 0) { + if (hs20_parse_oper_friendly_name(bss, pos, line) < 0) + return 1; + } else if (os_strcmp(buf, "hs20_wan_metrics") == 0) { + if (hs20_parse_wan_metrics(bss, pos, line) < 0) + return 1; + } else if (os_strcmp(buf, "hs20_conn_capab") == 0) { + if (hs20_parse_conn_capab(bss, pos, line) < 0) { + return 1; + } + } else if (os_strcmp(buf, "hs20_operating_class") == 0) { + u8 *oper_class; + size_t oper_class_len; + oper_class_len = os_strlen(pos); + if (oper_class_len < 2 || (oper_class_len & 0x01)) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid hs20_operating_class '%s'", + line, pos); + return 1; + } + oper_class_len /= 2; + oper_class = os_malloc(oper_class_len); + if (oper_class == NULL) + return 1; + if (hexstr2bin(pos, oper_class, oper_class_len)) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid hs20_operating_class '%s'", + line, pos); + os_free(oper_class); + return 1; + } + os_free(bss->hs20_operating_class); + bss->hs20_operating_class = oper_class; + bss->hs20_operating_class_len = oper_class_len; + } else if (os_strcmp(buf, "hs20_icon") == 0) { + if (hs20_parse_icon(bss, pos) < 0) { + wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_icon '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "osu_ssid") == 0) { + if (hs20_parse_osu_ssid(bss, pos, line) < 0) + return 1; + } else if (os_strcmp(buf, "osu_server_uri") == 0) { + if (hs20_parse_osu_server_uri(bss, pos, line) < 0) + return 1; + } else if (os_strcmp(buf, "osu_friendly_name") == 0) { + if (hs20_parse_osu_friendly_name(bss, pos, line) < 0) + return 1; + } else if (os_strcmp(buf, "osu_nai") == 0) { + if (hs20_parse_osu_nai(bss, pos, line) < 0) + return 1; + } else if (os_strcmp(buf, "osu_nai2") == 0) { + if (hs20_parse_osu_nai2(bss, pos, line) < 0) + return 1; + } else if (os_strcmp(buf, "osu_method_list") == 0) { + if (hs20_parse_osu_method_list(bss, pos, line) < 0) + return 1; + } else if (os_strcmp(buf, "osu_icon") == 0) { + if (hs20_parse_osu_icon(bss, pos, line) < 0) + return 1; + } else if (os_strcmp(buf, "osu_service_desc") == 0) { + if (hs20_parse_osu_service_desc(bss, pos, line) < 0) + return 1; + } else if (os_strcmp(buf, "operator_icon") == 0) { + if (hs20_parse_operator_icon(bss, pos, line) < 0) + return 1; + } else if (os_strcmp(buf, "subscr_remediation_url") == 0) { + os_free(bss->subscr_remediation_url); + bss->subscr_remediation_url = os_strdup(pos); + } else if (os_strcmp(buf, "subscr_remediation_method") == 0) { + bss->subscr_remediation_method = atoi(pos); + } else if (os_strcmp(buf, "hs20_t_c_filename") == 0) { + os_free(bss->t_c_filename); + bss->t_c_filename = os_strdup(pos); + } else if (os_strcmp(buf, "hs20_t_c_timestamp") == 0) { + bss->t_c_timestamp = strtol(pos, NULL, 0); + } else if (os_strcmp(buf, "hs20_t_c_server_url") == 0) { + os_free(bss->t_c_server_url); + bss->t_c_server_url = os_strdup(pos); + } else if (os_strcmp(buf, "hs20_sim_provisioning_url") == 0) { + os_free(bss->hs20_sim_provisioning_url); + bss->hs20_sim_provisioning_url = os_strdup(pos); +#endif /* CONFIG_HS20 */ +#ifdef CONFIG_MBO + } else if (os_strcmp(buf, "mbo") == 0) { + bss->mbo_enabled = atoi(pos); + } else if (os_strcmp(buf, "mbo_cell_data_conn_pref") == 0) { + bss->mbo_cell_data_conn_pref = atoi(pos); + } else if (os_strcmp(buf, "oce") == 0) { + bss->oce = atoi(pos); +#endif /* CONFIG_MBO */ +#ifdef CONFIG_TESTING_OPTIONS +#define PARSE_TEST_PROBABILITY(_val) \ + } else if (os_strcmp(buf, #_val) == 0) { \ + char *end; \ + \ + conf->_val = strtod(pos, &end); \ + if (*end || conf->_val < 0.0 || \ + conf->_val > 1.0) { \ + wpa_printf(MSG_ERROR, \ + "Line %d: Invalid value '%s'", \ + line, pos); \ + return 1; \ + } + PARSE_TEST_PROBABILITY(ignore_probe_probability) + PARSE_TEST_PROBABILITY(ignore_auth_probability) + PARSE_TEST_PROBABILITY(ignore_assoc_probability) + PARSE_TEST_PROBABILITY(ignore_reassoc_probability) + PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability) + } else if (os_strcmp(buf, "ecsa_ie_only") == 0) { + conf->ecsa_ie_only = atoi(pos); + } else if (os_strcmp(buf, "bss_load_test") == 0) { + WPA_PUT_LE16(bss->bss_load_test, atoi(pos)); + pos = os_strchr(pos, ':'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid bss_load_test", + line); + return 1; + } + pos++; + bss->bss_load_test[2] = atoi(pos); + pos = os_strchr(pos, ':'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "Line %d: Invalid bss_load_test", + line); + return 1; + } + pos++; + WPA_PUT_LE16(&bss->bss_load_test[3], atoi(pos)); + bss->bss_load_test_set = 1; + } else if (os_strcmp(buf, "radio_measurements") == 0) { + /* + * DEPRECATED: This parameter will be removed in the future. + * Use rrm_neighbor_report instead. + */ + int val = atoi(pos); + + if (val & BIT(0)) + bss->radio_measurements[0] |= + WLAN_RRM_CAPS_NEIGHBOR_REPORT; + } else if (os_strcmp(buf, "own_ie_override") == 0) { + struct wpabuf *tmp; + size_t len = os_strlen(pos) / 2; + + tmp = wpabuf_alloc(len); + if (!tmp) + return 1; + + if (hexstr2bin(pos, wpabuf_put(tmp, len), len)) { + wpabuf_free(tmp); + wpa_printf(MSG_ERROR, + "Line %d: Invalid own_ie_override '%s'", + line, pos); + return 1; + } + + wpabuf_free(bss->own_ie_override); + bss->own_ie_override = tmp; + } else if (os_strcmp(buf, "sae_reflection_attack") == 0) { + bss->sae_reflection_attack = atoi(pos); + } else if (os_strcmp(buf, "sae_commit_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); + } else if (os_strcmp(buf, "eap_skip_prot_success") == 0) { + bss->eap_skip_prot_success = atoi(pos); + } else if (os_strcmp(buf, "delay_eapol_tx") == 0) { + conf->delay_eapol_tx = atoi(pos); + } else if (os_strcmp(buf, "eapol_m1_elements") == 0) { + if (parse_wpabuf_hex(line, buf, &bss->eapol_m1_elements, pos)) + return 1; + } else if (os_strcmp(buf, "eapol_m3_elements") == 0) { + if (parse_wpabuf_hex(line, buf, &bss->eapol_m3_elements, pos)) + return 1; + } else if (os_strcmp(buf, "eapol_m3_no_encrypt") == 0) { + bss->eapol_m3_no_encrypt = atoi(pos); + } else if (os_strcmp(buf, "test_assoc_comeback_type") == 0) { + bss->test_assoc_comeback_type = atoi(pos); + } else if (os_strcmp(buf, "presp_elements") == 0) { + if (parse_wpabuf_hex(line, buf, &bss->presp_elements, pos)) + return 1; +#endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_SAE + } else if (os_strcmp(buf, "sae_password") == 0) { + if (parse_sae_password(bss, pos) < 0) { + wpa_printf(MSG_ERROR, "Line %d: Invalid sae_password", + line); + return 1; + } + } else if (os_strcmp(buf, "sae_password_file") == 0) { + if (parse_sae_password_file(bss, pos) < 0) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid sae_password in file", + line); + return 1; + } +#endif /* CONFIG_SAE */ + } else if (os_strcmp(buf, "vendor_elements") == 0) { + if (parse_wpabuf_hex(line, buf, &bss->vendor_elements, pos)) + return 1; + } 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 || + 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) { + if (hostapd_parse_intlist(&bss->sae_groups, pos)) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid sae_groups value '%s'", + line, pos); + return 1; + } + } 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) { + wpa_printf(MSG_ERROR, "Line %d: Invalid local_pwr_constraint %d (expected 0..255)", + line, val); + return 1; + } + conf->local_pwr_constraint = val; + } else if (os_strcmp(buf, "spectrum_mgmt_required") == 0) { + conf->spectrum_mgmt_required = atoi(pos); + } else if (os_strcmp(buf, "wowlan_triggers") == 0) { + os_free(bss->wowlan_triggers); + bss->wowlan_triggers = os_strdup(pos); +#ifdef CONFIG_FST + } else if (os_strcmp(buf, "fst_group_id") == 0) { + size_t len = os_strlen(pos); + + if (!len || len >= sizeof(conf->fst_cfg.group_id)) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid fst_group_id value '%s'", + line, pos); + return 1; + } + + if (conf->fst_cfg.group_id[0]) { + wpa_printf(MSG_ERROR, + "Line %d: Duplicate fst_group value '%s'", + line, pos); + return 1; + } + + os_strlcpy(conf->fst_cfg.group_id, pos, + sizeof(conf->fst_cfg.group_id)); + } else if (os_strcmp(buf, "fst_priority") == 0) { + char *endp; + long int val; + + if (!*pos) { + wpa_printf(MSG_ERROR, + "Line %d: fst_priority value not supplied (expected 1..%u)", + line, FST_MAX_PRIO_VALUE); + return -1; + } + + val = strtol(pos, &endp, 0); + if (*endp || val < 1 || val > FST_MAX_PRIO_VALUE) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid fst_priority %ld (%s) (expected 1..%u)", + line, val, pos, FST_MAX_PRIO_VALUE); + return 1; + } + conf->fst_cfg.priority = (u8) val; + } else if (os_strcmp(buf, "fst_llt") == 0) { + char *endp; + long int val; + + if (!*pos) { + wpa_printf(MSG_ERROR, + "Line %d: fst_llt value not supplied (expected 1..%u)", + line, FST_MAX_LLT_MS); + return -1; + } + val = strtol(pos, &endp, 0); + if (*endp || val < 1 || + (unsigned long int) val > FST_MAX_LLT_MS) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid fst_llt %ld (%s) (expected 1..%u)", + line, val, pos, FST_MAX_LLT_MS); + return 1; + } + conf->fst_cfg.llt = (u32) val; +#endif /* CONFIG_FST */ + } else if (os_strcmp(buf, "track_sta_max_num") == 0) { + conf->track_sta_max_num = atoi(pos); + } else if (os_strcmp(buf, "track_sta_max_age") == 0) { + conf->track_sta_max_age = atoi(pos); + } else if (os_strcmp(buf, "no_probe_resp_if_seen_on") == 0) { + os_free(bss->no_probe_resp_if_seen_on); + bss->no_probe_resp_if_seen_on = os_strdup(pos); + } else if (os_strcmp(buf, "no_auth_if_seen_on") == 0) { + os_free(bss->no_auth_if_seen_on); + bss->no_auth_if_seen_on = os_strdup(pos); + } else if (os_strcmp(buf, "lci") == 0) { + wpabuf_free(conf->lci); + conf->lci = wpabuf_parse_bin(pos); + if (conf->lci && wpabuf_len(conf->lci) == 0) { + wpabuf_free(conf->lci); + conf->lci = NULL; + } + } else if (os_strcmp(buf, "civic") == 0) { + wpabuf_free(conf->civic); + conf->civic = wpabuf_parse_bin(pos); + if (conf->civic && wpabuf_len(conf->civic) == 0) { + wpabuf_free(conf->civic); + conf->civic = NULL; + } + } else if (os_strcmp(buf, "rrm_neighbor_report") == 0) { + if (atoi(pos)) + bss->radio_measurements[0] |= + WLAN_RRM_CAPS_NEIGHBOR_REPORT; + } else if (os_strcmp(buf, "rrm_beacon_report") == 0) { + if (atoi(pos)) + bss->radio_measurements[0] |= + WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE | + WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE | + WLAN_RRM_CAPS_BEACON_REPORT_TABLE; + } else if (os_strcmp(buf, "rrm_link_measurement_report") == 0) { + if (atoi(pos)) + bss->radio_measurements[0] |= + WLAN_RRM_CAPS_LINK_MEASUREMENT; + } else if (os_strcmp(buf, "gas_address3") == 0) { + bss->gas_address3 = atoi(pos); + } else if (os_strcmp(buf, "stationary_ap") == 0) { + conf->stationary_ap = atoi(pos); + } else if (os_strcmp(buf, "ftm_responder") == 0) { + bss->ftm_responder = atoi(pos); + } else if (os_strcmp(buf, "ftm_initiator") == 0) { + bss->ftm_initiator = atoi(pos); +#ifdef CONFIG_FILS + } else if (os_strcmp(buf, "fils_cache_id") == 0) { + if (hexstr2bin(pos, bss->fils_cache_id, FILS_CACHE_ID_LEN)) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid fils_cache_id '%s'", + line, pos); + return 1; + } + bss->fils_cache_id_set = 1; + } else if (os_strcmp(buf, "fils_realm") == 0) { + if (parse_fils_realm(bss, pos) < 0) + return 1; + } else if (os_strcmp(buf, "fils_dh_group") == 0) { + bss->fils_dh_group = atoi(pos); + } else if (os_strcmp(buf, "dhcp_server") == 0) { + if (hostapd_parse_ip_addr(pos, &bss->dhcp_server)) { + wpa_printf(MSG_ERROR, + "Line %d: invalid IP address '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "dhcp_rapid_commit_proxy") == 0) { + bss->dhcp_rapid_commit_proxy = atoi(pos); + } else if (os_strcmp(buf, "fils_hlp_wait_time") == 0) { + bss->fils_hlp_wait_time = atoi(pos); + } else if (os_strcmp(buf, "dhcp_server_port") == 0) { + bss->dhcp_server_port = atoi(pos); + } else if (os_strcmp(buf, "dhcp_relay_port") == 0) { + bss->dhcp_relay_port = atoi(pos); + } 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, "bridge_multicast_to_unicast") == 0) { + bss->bridge_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_extra_conf_req_name") == 0) { + os_free(bss->dpp_extra_conf_req_name); + bss->dpp_extra_conf_req_name = os_strdup(pos); + } else if (os_strcmp(buf, "dpp_extra_conf_req_value") == 0) { + os_free(bss->dpp_extra_conf_req_value); + bss->dpp_extra_conf_req_value = os_strdup(pos); + } else if (os_strcmp(buf, "dpp_connector") == 0) { + os_free(bss->dpp_connector); + bss->dpp_connector = os_strdup(pos); + } else if (os_strcmp(buf, "dpp_netaccesskey") == 0) { + if (parse_wpabuf_hex(line, buf, &bss->dpp_netaccesskey, pos)) + return 1; + } else if (os_strcmp(buf, "dpp_netaccesskey_expiry") == 0) { + bss->dpp_netaccesskey_expiry = strtol(pos, NULL, 0); + } else if (os_strcmp(buf, "dpp_csign") == 0) { + if (parse_wpabuf_hex(line, buf, &bss->dpp_csign, pos)) + return 1; +#ifdef CONFIG_DPP2 + } else if (os_strcmp(buf, "dpp_controller") == 0) { + if (hostapd_dpp_controller_parse(bss, pos)) + return 1; + } else if (os_strcmp(buf, "dpp_relay_port") == 0) { + bss->dpp_relay_port = atoi(pos); + } 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 + } else if (os_strcmp(buf, "owe_transition_bssid") == 0) { + if (hwaddr_aton(pos, bss->owe_transition_bssid)) { + wpa_printf(MSG_ERROR, + "Line %d: invalid owe_transition_bssid", + line); + return 1; + } + } else if (os_strcmp(buf, "owe_transition_ssid") == 0) { + size_t slen; + char *str = wpa_config_parse_string(pos, &slen); + + if (!str || slen < 1 || slen > SSID_MAX_LEN) { + wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'", + line, pos); + os_free(str); + return 1; + } + os_memcpy(bss->owe_transition_ssid, str, slen); + bss->owe_transition_ssid_len = slen; + os_free(str); + } else if (os_strcmp(buf, "owe_transition_ifname") == 0) { + os_strlcpy(bss->owe_transition_ifname, pos, + sizeof(bss->owe_transition_ifname)); + } else if (os_strcmp(buf, "owe_groups") == 0) { + if (hostapd_parse_intlist(&bss->owe_groups, pos)) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid owe_groups value '%s'", + line, pos); + return 1; + } + } else if (os_strcmp(buf, "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); + } else if (os_strcmp(buf, "multi_ap") == 0) { + int val = atoi(pos); + + if (val < 0 || val > 3) { + wpa_printf(MSG_ERROR, "Line %d: Invalid multi_ap '%s'", + line, buf); + return -1; + } + + bss->multi_ap = val; + } else if (os_strcmp(buf, "multi_ap_profile") == 0) { + int val = atoi(pos); + + if (val < MULTI_AP_PROFILE_1 || val > MULTI_AP_PROFILE_MAX) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid multi_ap_profile '%s'", + line, buf); + return -1; + } + bss->multi_ap_profile = val; + } else if (os_strcmp(buf, "multi_ap_client_disallow") == 0) { + int val = atoi(pos); + + if (val < 0 || val > 3) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid multi_ap_client_allow '%s'", + line, buf); + return -1; + } + bss->multi_ap_client_disallow = val; + } else if (os_strcmp(buf, "multi_ap_vlanid") == 0) { + int val = atoi(pos); + + if (val < 0 || val > MAX_VLAN_ID) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid multi_ap_vlan_id '%s'", + line, buf); + return -1; + } + bss->multi_ap_vlanid = val; + } else if (os_strcmp(buf, "rssi_reject_assoc_rssi") == 0) { + 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); + + if (val < 0 || val > AIRTIME_MODE_MAX) { + wpa_printf(MSG_ERROR, "Line %d: Unknown airtime_mode", + line); + return 1; + } + conf->airtime_mode = val; + } else if (os_strcmp(buf, "airtime_update_interval") == 0) { + conf->airtime_update_interval = atoi(pos); + } else if (os_strcmp(buf, "airtime_bss_weight") == 0) { + bss->airtime_weight = atoi(pos); + } else if (os_strcmp(buf, "airtime_bss_limit") == 0) { + int val = atoi(pos); + + if (val < 0 || val > 1) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid airtime_bss_limit (must be 0 or 1)", + line); + return 1; + } + bss->airtime_limit = val; + } else if (os_strcmp(buf, "airtime_sta_weight") == 0) { + if (add_airtime_weight(bss, pos) < 0) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid airtime weight '%s'", + line, pos); + return 1; + } +#endif /* CONFIG_AIRTIME_POLICY */ +#ifdef CONFIG_MACSEC + } else if (os_strcmp(buf, "macsec_policy") == 0) { + int macsec_policy = atoi(pos); + + if (macsec_policy < 0 || macsec_policy > 1) { + wpa_printf(MSG_ERROR, + "Line %d: invalid macsec_policy (%d): '%s'.", + line, macsec_policy, pos); + return 1; + } + bss->macsec_policy = macsec_policy; + } else if (os_strcmp(buf, "macsec_integ_only") == 0) { + int macsec_integ_only = atoi(pos); + + if (macsec_integ_only < 0 || macsec_integ_only > 1) { + wpa_printf(MSG_ERROR, + "Line %d: invalid macsec_integ_only (%d): '%s'.", + line, macsec_integ_only, pos); + return 1; + } + bss->macsec_integ_only = macsec_integ_only; + } else if (os_strcmp(buf, "macsec_replay_protect") == 0) { + int macsec_replay_protect = atoi(pos); + + if (macsec_replay_protect < 0 || macsec_replay_protect > 1) { + wpa_printf(MSG_ERROR, + "Line %d: invalid macsec_replay_protect (%d): '%s'.", + line, macsec_replay_protect, pos); + return 1; + } + bss->macsec_replay_protect = macsec_replay_protect; + } else if (os_strcmp(buf, "macsec_replay_window") == 0) { + bss->macsec_replay_window = atoi(pos); + } else if (os_strcmp(buf, "macsec_offload") == 0) { + int macsec_offload = atoi(pos); + + if (macsec_offload < 0 || macsec_offload > 2) { + wpa_printf(MSG_ERROR, + "Line %d: invalid macsec_offload (%d): '%s'.", + line, macsec_offload, pos); + return 1; + } + bss->macsec_offload = macsec_offload; + } else if (os_strcmp(buf, "macsec_port") == 0) { + int macsec_port = atoi(pos); + + if (macsec_port < 1 || macsec_port > 65534) { + wpa_printf(MSG_ERROR, + "Line %d: invalid macsec_port (%d): '%s'.", + line, macsec_port, pos); + return 1; + } + bss->macsec_port = macsec_port; + } else if (os_strcmp(buf, "mka_priority") == 0) { + int mka_priority = atoi(pos); + + if (mka_priority < 0 || mka_priority > 255) { + wpa_printf(MSG_ERROR, + "Line %d: invalid mka_priority (%d): '%s'.", + line, mka_priority, pos); + return 1; + } + bss->mka_priority = mka_priority; + } else if (os_strcmp(buf, "macsec_csindex") == 0) { + int macsec_csindex = atoi(pos); + + if (macsec_csindex < 0 || macsec_csindex > 1) { + wpa_printf(MSG_ERROR, + "Line %d: invalid macsec_csindex (%d): '%s'.", + line, macsec_csindex, pos); + return 1; + } + bss->macsec_csindex = macsec_csindex; + } else if (os_strcmp(buf, "mka_cak") == 0) { + size_t len = os_strlen(pos); + + if (len > 2 * MACSEC_CAK_MAX_LEN || + (len != 2 * 16 && len != 2 * 32) || + hexstr2bin(pos, bss->mka_cak, len / 2)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CAK '%s'.", + line, pos); + return 1; + } + bss->mka_cak_len = len / 2; + bss->mka_psk_set |= MKA_PSK_SET_CAK; + } else if (os_strcmp(buf, "mka_ckn") == 0) { + size_t len = os_strlen(pos); + + if (len > 2 * MACSEC_CKN_MAX_LEN || /* too long */ + len < 2 || /* too short */ + len % 2 != 0 /* not an integral number of bytes */) { + wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.", + line, pos); + return 1; + } + bss->mka_ckn_len = len / 2; + if (hexstr2bin(pos, bss->mka_ckn, bss->mka_ckn_len)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.", + line, pos); + return -1; + } + 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); + } else if (os_strcmp(buf, "disable_11be") == 0) { + bss->disable_11be = !!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); + } else if (os_strcmp(buf, "pasn_noauth") == 0) { + bss->pasn_noauth = 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 if (os_strcmp(buf, "rnr") == 0) { + bss->rnr = atoi(pos); + } else if (os_strcmp(buf, "ssid_protection") == 0) { + int val = atoi(pos); + + if (val < 0 || val > 1) + return 1; + bss->ssid_protection = val; +#ifdef CONFIG_IEEE80211BE + } else if (os_strcmp(buf, "ieee80211be") == 0) { + conf->ieee80211be = atoi(pos); + } else if (os_strcmp(buf, "eht_oper_chwidth") == 0) { + conf->eht_oper_chwidth = atoi(pos); + } else if (os_strcmp(buf, "eht_oper_centr_freq_seg0_idx") == 0) { + conf->eht_oper_centr_freq_seg0_idx = atoi(pos); + } else if (os_strcmp(buf, "eht_su_beamformer") == 0) { + conf->eht_phy_capab.su_beamformer = atoi(pos); + } else if (os_strcmp(buf, "eht_su_beamformee") == 0) { + conf->eht_phy_capab.su_beamformee = atoi(pos); + } else if (os_strcmp(buf, "eht_mu_beamformer") == 0) { + conf->eht_phy_capab.mu_beamformer = atoi(pos); + } else if (os_strcmp(buf, "eht_default_pe_duration") == 0) { + conf->eht_default_pe_duration = atoi(pos); + } else if (os_strcmp(buf, "punct_bitmap") == 0) { + if (get_u16(pos, line, &conf->punct_bitmap)) + return 1; + } else if (os_strcmp(buf, "punct_acs_threshold") == 0) { + int val = atoi(pos); + + if (val < 0 || val > 100) { + wpa_printf(MSG_ERROR, + "Line %d: punct_acs_threshold must be between 0 and 100", + line); + return 1; + } + conf->punct_acs_threshold = val; + } else if (os_strcmp(buf, "mld_ap") == 0) { + bss->mld_ap = !!atoi(pos); + } else if (os_strcmp(buf, "mld_addr") == 0) { + if (hwaddr_aton(pos, bss->mld_addr)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid mld_addr", + line); + return 1; + } + } else if (os_strcmp(buf, "eht_bw320_offset") == 0) { + conf->eht_bw320_offset = atoi(pos); +#ifdef CONFIG_TESTING_OPTIONS + } else if (os_strcmp(buf, "eht_oper_puncturing_override") == 0) { + if (get_u16(pos, line, &bss->eht_oper_puncturing_override)) + return 1; + } else if (os_strcmp(buf, "mld_indicate_disabled") == 0) { + bss->mld_indicate_disabled = atoi(pos); +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_IEEE80211BE */ + } else { + wpa_printf(MSG_ERROR, + "Line %d: unknown configuration item '%s'", + line, buf); + return 1; + } + + return 0; +} + + +/** + * hostapd_config_read - Read and parse a configuration file + * @fname: Configuration file name (including path, if needed) + * Returns: Allocated configuration data structure + */ +struct hostapd_config * hostapd_config_read(const char *fname) +{ + struct hostapd_config *conf; + FILE *f; + char buf[4096], *pos; + int line = 0; + int errors = 0; + size_t i; + + f = fopen(fname, "r"); + if (f == NULL) { + wpa_printf(MSG_ERROR, "Could not open configuration file '%s' " + "for reading.", fname); + return NULL; + } + + conf = hostapd_config_defaults(); + if (conf == NULL) { + fclose(f); + return NULL; + } + + /* set default driver based on configuration */ + conf->driver = wpa_drivers[0]; + if (conf->driver == NULL) { + wpa_printf(MSG_ERROR, "No driver wrappers registered!"); + hostapd_config_free(conf); + fclose(f); + return NULL; + } + + conf->last_bss = conf->bss[0]; + + while (fgets(buf, sizeof(buf), f)) { + struct hostapd_bss_config *bss; + + bss = conf->last_bss; + line++; + + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + if (buf[0] == '\0') + continue; + + pos = os_strchr(buf, '='); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "Line %d: invalid line '%s'", + line, buf); + errors++; + continue; + } + *pos = '\0'; + pos++; + errors += hostapd_config_fill(conf, bss, buf, pos, line); + } + + fclose(f); + + for (i = 0; i < conf->num_bss; i++) + hostapd_set_security_params(conf->bss[i], 1); + + if (hostapd_config_check(conf, 1)) + errors++; + +#ifndef WPA_IGNORE_CONFIG_ERRORS + if (errors) { + wpa_printf(MSG_ERROR, "%d errors found in configuration file " + "'%s'", errors, fname); + hostapd_config_free(conf); + conf = NULL; + } +#endif /* WPA_IGNORE_CONFIG_ERRORS */ + + return conf; +} + + +int hostapd_set_iface(struct hostapd_config *conf, + struct hostapd_bss_config *bss, const char *field, + char *value) +{ + int errors; + size_t i; + + errors = hostapd_config_fill(conf, bss, field, value, 0); + if (errors) { + wpa_printf(MSG_INFO, "Failed to set configuration field '%s' " + "to value '%s'", field, value); + return -1; + } + + for (i = 0; i < conf->num_bss; i++) + hostapd_set_security_params(conf->bss[i], 0); + + if (hostapd_config_check(conf, 0)) { + wpa_printf(MSG_ERROR, "Configuration check failed"); + return -1; + } + + return 0; +} diff --git a/hostapd/config_file.h b/hostapd/config_file.h new file mode 100644 index 000000000000..9ef6ac829130 --- /dev/null +++ b/hostapd/config_file.h @@ -0,0 +1,19 @@ +/* + * hostapd / Configuration file parser + * Copyright (c) 2003-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef CONFIG_FILE_H +#define CONFIG_FILE_H + +struct hostapd_config * hostapd_config_read(const char *fname); +int hostapd_config_read_rxkh_file(struct hostapd_bss_config *conf, + const char *fname); +int hostapd_set_iface(struct hostapd_config *conf, + struct hostapd_bss_config *bss, const char *field, + char *value); + +#endif /* CONFIG_FILE_H */ diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c new file mode 100644 index 000000000000..39b9ef59dc57 --- /dev/null +++ b/hostapd/ctrl_iface.c @@ -0,0 +1,5864 @@ +/* + * hostapd / UNIX domain socket -based control interface + * Copyright (c) 2004-2018, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS + +#ifdef CONFIG_TESTING_OPTIONS +#ifdef __NetBSD__ +#include +#else +#include +#endif +#include +#endif /* CONFIG_TESTING_OPTIONS */ + +#include +#include +#include + +#ifdef CONFIG_CTRL_IFACE_UDP +#include +#endif /* CONFIG_CTRL_IFACE_UDP */ + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/module_tests.h" +#include "common/version.h" +#include "common/ieee802_11_defs.h" +#include "common/ctrl_iface_common.h" +#ifdef CONFIG_DPP +#include "common/dpp.h" +#endif /* CONFIG_DPP */ +#include "common/wpa_ctrl.h" +#include "common/ptksa_cache.h" +#include "common/hw_features_common.h" +#include "common/nan_de.h" +#include "crypto/tls.h" +#include "drivers/driver.h" +#include "eapol_auth/eapol_auth_sm.h" +#include "radius/radius_client.h" +#include "radius/radius_server.h" +#include "l2_packet/l2_packet.h" +#include "ap/hostapd.h" +#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" +#include "ap/ctrl_iface_ap.h" +#include "ap/ap_drv_ops.h" +#include "ap/hs20.h" +#include "ap/wnm_ap.h" +#include "ap/wpa_auth.h" +#include "ap/beacon.h" +#include "ap/neighbor_db.h" +#include "ap/rrm.h" +#include "ap/dpp_hostapd.h" +#include "ap/dfs.h" +#include "ap/nan_usd_ap.h" +#include "wps/wps_defs.h" +#include "wps/wps.h" +#include "fst/fst_ctrl_iface.h" +#include "config_file.h" +#include "ctrl_iface.h" + + +#define HOSTAPD_CLI_DUP_VALUE_MAX_LEN 256 + +#ifdef CONFIG_CTRL_IFACE_UDP +#define HOSTAPD_CTRL_IFACE_PORT 8877 +#define HOSTAPD_CTRL_IFACE_PORT_LIMIT 50 +#define HOSTAPD_GLOBAL_CTRL_IFACE_PORT 8878 +#define HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT 50 +#endif /* CONFIG_CTRL_IFACE_UDP */ + +static void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, + enum wpa_msg_type type, + const char *buf, size_t len); + + +static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd, + struct sockaddr_storage *from, + socklen_t fromlen, const char *input) +{ + return ctrl_iface_attach(&hapd->ctrl_dst, from, fromlen, input); +} + + +static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd, + struct sockaddr_storage *from, + socklen_t fromlen) +{ + return ctrl_iface_detach(&hapd->ctrl_dst, from, fromlen); +} + + +static int hostapd_ctrl_iface_level(struct hostapd_data *hapd, + struct sockaddr_storage *from, + socklen_t fromlen, + char *level) +{ + return ctrl_iface_level(&hapd->ctrl_dst, from, fromlen, level); +} + + +static int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd, + const char *txtaddr) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE NEW_STA %s", txtaddr); + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (sta) + return 0; + + wpa_printf(MSG_DEBUG, "Add new STA " MACSTR " based on ctrl_iface " + "notification", MAC2STR(addr)); + sta = ap_sta_add(hapd, addr); + if (sta == NULL) + return -1; + + hostapd_new_assoc_sta(hapd, sta, 0); + return 0; +} + + +#ifdef NEED_AP_MLME +static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd, + const char *txtaddr) +{ + u8 addr[ETH_ALEN]; + u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN]; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE SA_QUERY %s", txtaddr); + + if (hwaddr_aton(txtaddr, addr) || + os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) + return -1; + + ieee802_11_send_sa_query_req(hapd, addr, trans_id); + + return 0; +} +#endif /* NEED_AP_MLME */ + + +#ifdef CONFIG_WPS +static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt) +{ + char *pin = os_strchr(txt, ' '); + char *timeout_txt; + int timeout; + u8 addr_buf[ETH_ALEN], *addr = NULL; + char *pos; + + if (pin == NULL) + return -1; + *pin++ = '\0'; + + timeout_txt = os_strchr(pin, ' '); + if (timeout_txt) { + *timeout_txt++ = '\0'; + timeout = atoi(timeout_txt); + pos = os_strchr(timeout_txt, ' '); + if (pos) { + *pos++ = '\0'; + if (hwaddr_aton(pos, addr_buf) == 0) + addr = addr_buf; + } + } else + timeout = 0; + + return hostapd_wps_add_pin(hapd, addr, txt, pin, timeout); +} + + +static int hostapd_ctrl_iface_wps_check_pin( + struct hostapd_data *hapd, char *cmd, char *buf, size_t buflen) +{ + char pin[9]; + size_t len; + char *pos; + int ret; + + wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN", + (u8 *) cmd, os_strlen(cmd)); + for (pos = cmd, len = 0; *pos != '\0'; pos++) { + if (*pos < '0' || *pos > '9') + continue; + pin[len++] = *pos; + if (len == 9) { + wpa_printf(MSG_DEBUG, "WPS: Too long PIN"); + return -1; + } + } + if (len != 4 && len != 8) { + wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len); + return -1; + } + pin[len] = '\0'; + + if (len == 8) { + unsigned int pin_val; + pin_val = atoi(pin); + if (!wps_pin_valid(pin_val)) { + wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit"); + ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n"); + if (os_snprintf_error(buflen, ret)) + return -1; + return ret; + } + } + + ret = os_snprintf(buf, buflen, "%s", pin); + if (os_snprintf_error(buflen, ret)) + return -1; + + return ret; +} + + +#ifdef CONFIG_WPS_NFC +static int hostapd_ctrl_iface_wps_nfc_tag_read(struct hostapd_data *hapd, + char *pos) +{ + size_t len; + struct wpabuf *buf; + int ret; + + len = os_strlen(pos); + if (len & 0x01) + return -1; + len /= 2; + + buf = wpabuf_alloc(len); + if (buf == NULL) + return -1; + if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) { + wpabuf_free(buf); + return -1; + } + + ret = hostapd_wps_nfc_tag_read(hapd, buf); + wpabuf_free(buf); + + return ret; +} + + +static int hostapd_ctrl_iface_wps_nfc_config_token(struct hostapd_data *hapd, + char *cmd, char *reply, + size_t max_len) +{ + int ndef; + struct wpabuf *buf; + int res; + + if (os_strcmp(cmd, "WPS") == 0) + ndef = 0; + else if (os_strcmp(cmd, "NDEF") == 0) + ndef = 1; + else + return -1; + + buf = hostapd_wps_nfc_config_token(hapd, ndef); + if (buf == NULL) + return -1; + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; +} + + +static int hostapd_ctrl_iface_wps_nfc_token_gen(struct hostapd_data *hapd, + char *reply, size_t max_len, + int ndef) +{ + struct wpabuf *buf; + int res; + + buf = hostapd_wps_nfc_token_gen(hapd, ndef); + if (buf == NULL) + return -1; + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; +} + + +static int hostapd_ctrl_iface_wps_nfc_token(struct hostapd_data *hapd, + char *cmd, char *reply, + size_t max_len) +{ + if (os_strcmp(cmd, "WPS") == 0) + return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply, + max_len, 0); + + if (os_strcmp(cmd, "NDEF") == 0) + return hostapd_ctrl_iface_wps_nfc_token_gen(hapd, reply, + max_len, 1); + + if (os_strcmp(cmd, "enable") == 0) + return hostapd_wps_nfc_token_enable(hapd); + + if (os_strcmp(cmd, "disable") == 0) { + hostapd_wps_nfc_token_disable(hapd); + return 0; + } + + return -1; +} + + +static int hostapd_ctrl_iface_nfc_get_handover_sel(struct hostapd_data *hapd, + char *cmd, char *reply, + size_t max_len) +{ + struct wpabuf *buf; + int res; + char *pos; + int ndef; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + if (os_strcmp(cmd, "WPS") == 0) + ndef = 0; + else if (os_strcmp(cmd, "NDEF") == 0) + ndef = 1; + else + return -1; + + if (os_strcmp(pos, "WPS-CR") == 0) + buf = hostapd_wps_nfc_hs_cr(hapd, ndef); + else + buf = NULL; + if (buf == NULL) + return -1; + + res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf), + wpabuf_len(buf)); + reply[res++] = '\n'; + reply[res] = '\0'; + + wpabuf_free(buf); + + return res; +} + + +static int hostapd_ctrl_iface_nfc_report_handover(struct hostapd_data *hapd, + char *cmd) +{ + size_t len; + struct wpabuf *req, *sel; + int ret; + char *pos, *role, *type, *pos2; + + role = cmd; + pos = os_strchr(role, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + type = pos; + pos = os_strchr(type, ' '); + if (pos == NULL) + return -1; + *pos++ = '\0'; + + pos2 = os_strchr(pos, ' '); + if (pos2 == NULL) + return -1; + *pos2++ = '\0'; + + len = os_strlen(pos); + if (len & 0x01) + return -1; + len /= 2; + + req = wpabuf_alloc(len); + if (req == NULL) + return -1; + if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) { + wpabuf_free(req); + return -1; + } + + len = os_strlen(pos2); + if (len & 0x01) { + wpabuf_free(req); + return -1; + } + len /= 2; + + sel = wpabuf_alloc(len); + if (sel == NULL) { + wpabuf_free(req); + return -1; + } + if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) { + wpabuf_free(req); + wpabuf_free(sel); + return -1; + } + + if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0) { + ret = hostapd_wps_nfc_report_handover(hapd, req, sel); + } else { + wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover " + "reported: role=%s type=%s", role, type); + ret = -1; + } + wpabuf_free(req); + wpabuf_free(sel); + + return ret; +} + +#endif /* CONFIG_WPS_NFC */ + + +static int hostapd_ctrl_iface_wps_ap_pin(struct hostapd_data *hapd, char *txt, + char *buf, size_t buflen) +{ + int timeout = 300; + char *pos; + const char *pin_txt; + + pos = os_strchr(txt, ' '); + if (pos) + *pos++ = '\0'; + + if (os_strcmp(txt, "disable") == 0) { + hostapd_wps_ap_pin_disable(hapd); + return os_snprintf(buf, buflen, "OK\n"); + } + + if (os_strcmp(txt, "random") == 0) { + if (pos) + timeout = atoi(pos); + pin_txt = hostapd_wps_ap_pin_random(hapd, timeout); + if (pin_txt == NULL) + return -1; + return os_snprintf(buf, buflen, "%s", pin_txt); + } + + if (os_strcmp(txt, "get") == 0) { + pin_txt = hostapd_wps_ap_pin_get(hapd); + if (pin_txt == NULL) + return -1; + return os_snprintf(buf, buflen, "%s", pin_txt); + } + + if (os_strcmp(txt, "set") == 0) { + char *pin; + if (pos == NULL) + return -1; + pin = pos; + pos = os_strchr(pos, ' '); + if (pos) { + *pos++ = '\0'; + timeout = atoi(pos); + } + if (os_strlen(pin) > buflen) + return -1; + if (hostapd_wps_ap_pin_set(hapd, pin, timeout) < 0) + return -1; + return os_snprintf(buf, buflen, "%s", pin); + } + + return -1; +} + + +static int hostapd_ctrl_iface_wps_config(struct hostapd_data *hapd, char *txt) +{ + char *pos; + char *ssid, *auth, *encr = NULL, *key = NULL; + + ssid = txt; + pos = os_strchr(txt, ' '); + if (!pos) + return -1; + *pos++ = '\0'; + + auth = pos; + pos = os_strchr(pos, ' '); + if (pos) { + *pos++ = '\0'; + encr = pos; + pos = os_strchr(pos, ' '); + if (pos) { + *pos++ = '\0'; + key = pos; + } + } + + return hostapd_wps_config_ap(hapd, ssid, auth, encr, key); +} + + +static const char * pbc_status_str(enum pbc_status status) +{ + switch (status) { + case WPS_PBC_STATUS_DISABLE: + return "Disabled"; + case WPS_PBC_STATUS_ACTIVE: + return "Active"; + case WPS_PBC_STATUS_TIMEOUT: + return "Timed-out"; + case WPS_PBC_STATUS_OVERLAP: + return "Overlap"; + default: + return "Unknown"; + } +} + + +static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd, + char *buf, size_t buflen) +{ + int ret; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + ret = os_snprintf(pos, end - pos, "PBC Status: %s\n", + pbc_status_str(hapd->wps_stats.pbc_status)); + + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, "Last WPS result: %s\n", + (hapd->wps_stats.status == WPS_STATUS_SUCCESS ? + "Success": + (hapd->wps_stats.status == WPS_STATUS_FAILURE ? + "Failed" : "None"))); + + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + /* If status == Failure - Add possible Reasons */ + if(hapd->wps_stats.status == WPS_STATUS_FAILURE && + hapd->wps_stats.failure_reason > 0) { + ret = os_snprintf(pos, end - pos, + "Failure Reason: %s\n", + wps_ei_str(hapd->wps_stats.failure_reason)); + + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + if (hapd->wps_stats.status) { + ret = os_snprintf(pos, end - pos, "Peer Address: " MACSTR "\n", + MAC2STR(hapd->wps_stats.peer_addr)); + + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + return pos - buf; +} + +#endif /* CONFIG_WPS */ + +#ifdef CONFIG_HS20 + +static int hostapd_ctrl_iface_hs20_wnm_notif(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + const char *url; + + if (hwaddr_aton(cmd, addr)) + return -1; + url = cmd + 17; + if (*url == '\0') { + url = NULL; + } else { + if (*url != ' ') + return -1; + url++; + if (*url == '\0') + url = NULL; + } + + return hs20_send_wnm_notification(hapd, addr, 1, url); +} + + +static int hostapd_ctrl_iface_hs20_deauth_req(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + int code, reauth_delay, ret; + const char *pos; + size_t url_len; + struct wpabuf *req; + + /* [URL] */ + if (hwaddr_aton(cmd, addr)) + return -1; + + pos = os_strchr(cmd, ' '); + if (pos == NULL) + return -1; + pos++; + code = atoi(pos); + + pos = os_strchr(pos, ' '); + if (pos == NULL) + return -1; + pos++; + reauth_delay = atoi(pos); + + url_len = 0; + pos = os_strchr(pos, ' '); + if (pos) { + pos++; + url_len = os_strlen(pos); + } + + req = wpabuf_alloc(4 + url_len); + if (req == NULL) + return -1; + wpabuf_put_u8(req, code); + wpabuf_put_le16(req, reauth_delay); + wpabuf_put_u8(req, url_len); + if (pos) + wpabuf_put_data(req, pos, url_len); + + wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " MACSTR + " to indicate imminent deauthentication (code=%d " + "reauth_delay=%d)", MAC2STR(addr), code, reauth_delay); + ret = hs20_send_wnm_notification_deauth_req(hapd, addr, req); + wpabuf_free(req); + return ret; +} + +#endif /* CONFIG_HS20 */ + + +#ifdef CONFIG_INTERWORKING + +static int hostapd_ctrl_iface_set_qos_map_set(struct hostapd_data *hapd, + const char *cmd) +{ + u8 qos_map_set[16 + 2 * 21], count = 0; + const char *pos = cmd; + int val, ret; + + for (;;) { + if (count == sizeof(qos_map_set)) { + wpa_printf(MSG_ERROR, "Too many qos_map_set parameters"); + return -1; + } + + val = atoi(pos); + if (val < 0 || val > 255) { + wpa_printf(MSG_INFO, "Invalid QoS Map Set"); + return -1; + } + + qos_map_set[count++] = val; + pos = os_strchr(pos, ','); + if (!pos) + break; + pos++; + } + + if (count < 16 || count & 1) { + wpa_printf(MSG_INFO, "Invalid QoS Map Set"); + return -1; + } + + ret = hostapd_drv_set_qos_map(hapd, qos_map_set, count); + if (ret) { + wpa_printf(MSG_INFO, "Failed to set QoS Map Set"); + return -1; + } + + os_memcpy(hapd->conf->qos_map_set, qos_map_set, count); + hapd->conf->qos_map_set_len = count; + + return 0; +} + + +static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + struct wpabuf *buf; + u8 *qos_map_set = hapd->conf->qos_map_set; + u8 qos_map_set_len = hapd->conf->qos_map_set_len; + int ret; + + if (!qos_map_set_len) { + wpa_printf(MSG_INFO, "QoS Map Set is not set"); + return -1; + } + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "Station " MACSTR " not found " + "for QoS Map Configuration message", + MAC2STR(addr)); + return -1; + } + + if (!sta->qos_map_enabled) { + wpa_printf(MSG_DEBUG, "Station " MACSTR " did not indicate " + "support for QoS Map", MAC2STR(addr)); + return -1; + } + + buf = wpabuf_alloc(2 + 2 + qos_map_set_len); + if (buf == NULL) + return -1; + + wpabuf_put_u8(buf, WLAN_ACTION_QOS); + wpabuf_put_u8(buf, QOS_QOS_MAP_CONFIG); + + /* QoS Map Set Element */ + wpabuf_put_u8(buf, WLAN_EID_QOS_MAP_SET); + wpabuf_put_u8(buf, qos_map_set_len); + wpabuf_put_data(buf, qos_map_set, qos_map_set_len); + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + wpabuf_head(buf), wpabuf_len(buf)); + wpabuf_free(buf); + + return ret; +} + +#endif /* CONFIG_INTERWORKING */ + + +#ifdef CONFIG_WNM_AP + +static int hostapd_ctrl_iface_coloc_intf_req(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + const char *pos; + unsigned int auto_report, timeout; + + if (hwaddr_aton(cmd, addr)) { + wpa_printf(MSG_DEBUG, "Invalid STA MAC address"); + return -1; + } + + sta = ap_get_sta(hapd, addr); + if (!sta) { + wpa_printf(MSG_DEBUG, "Station " MACSTR + " not found for Collocated Interference Request", + MAC2STR(addr)); + return -1; + } + + pos = cmd + 17; + if (*pos != ' ') + return -1; + pos++; + auto_report = atoi(pos); + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + timeout = atoi(pos); + + return wnm_send_coloc_intf_req(hapd, sta, auto_report, timeout); +} + +#endif /* CONFIG_WNM_AP */ + + +static int hostapd_ctrl_iface_get_key_mgmt(struct hostapd_data *hapd, + char *buf, size_t buflen) +{ + int ret = 0; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + WPA_ASSERT(hapd->conf->wpa_key_mgmt); + + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + ret = os_snprintf(pos, end - pos, "WPA-PSK "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + ret = os_snprintf(pos, end - pos, "WPA-EAP "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#ifdef CONFIG_IEEE80211R_AP + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { + ret = os_snprintf(pos, end - pos, "FT-PSK "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { + ret = os_snprintf(pos, end - pos, "FT-EAP "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#ifdef CONFIG_SHA384 + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) { + ret = os_snprintf(pos, end - pos, "FT-EAP-SHA384 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_SHA384 */ +#ifdef CONFIG_SAE + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) { + ret = os_snprintf(pos, end - pos, "FT-SAE "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) { + ret = os_snprintf(pos, end - pos, "FT-SAE-EXT-KEY "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_SAE */ +#ifdef CONFIG_FILS + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) { + ret = os_snprintf(pos, end - pos, "FT-FILS-SHA256 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) { + ret = os_snprintf(pos, end - pos, "FT-FILS-SHA384 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_FILS */ +#endif /* CONFIG_IEEE80211R_AP */ + 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)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { + ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#ifdef CONFIG_SAE + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) { + ret = os_snprintf(pos, end - pos, "SAE "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) { + ret = os_snprintf(pos, end - pos, "SAE-EXT-KEY "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_SAE */ + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) { + ret = os_snprintf(pos, end - pos, "WPA-EAP-SUITE-B "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & + WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { + ret = os_snprintf(pos, end - pos, + "WPA-EAP-SUITE-B-192 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#ifdef CONFIG_FILS + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) { + ret = os_snprintf(pos, end - pos, "FILS-SHA256 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) { + ret = os_snprintf(pos, end - pos, "FILS-SHA384 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_FILS */ + +#ifdef CONFIG_OWE + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) { + ret = os_snprintf(pos, end - pos, "OWE "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_OWE */ + +#ifdef CONFIG_DPP + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) { + ret = os_snprintf(pos, end - pos, "DPP "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_DPP */ +#ifdef CONFIG_SHA384 + if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA384) { + ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA384 "); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } +#endif /* CONFIG_SHA384 */ + + if (pos > buf && *(pos - 1) == ' ') { + *(pos - 1) = '\0'; + pos--; + } + + return pos - buf; +} + + +static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd, + char *buf, size_t buflen) +{ + int ret; + char *pos, *end; + + pos = buf; + end = buf + buflen; + + ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n" + "ssid=%s\n", + MAC2STR(hapd->own_addr), + wpa_ssid_txt(hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len)); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + if ((hapd->conf->config_id)) { + ret = os_snprintf(pos, end - pos, "config_id=%s\n", + hapd->conf->config_id); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + +#ifdef CONFIG_WPS + ret = os_snprintf(pos, end - pos, "wps_state=%s\n", + hapd->conf->wps_state == 0 ? "disabled" : + (hapd->conf->wps_state == 1 ? "not configured" : + "configured")); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + if (hapd->conf->wps_state && hapd->conf->wpa && + hapd->conf->ssid.wpa_passphrase) { + ret = os_snprintf(pos, end - pos, "passphrase=%s\n", + hapd->conf->ssid.wpa_passphrase); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + if (hapd->conf->wps_state && hapd->conf->wpa && + hapd->conf->ssid.wpa_psk && + hapd->conf->ssid.wpa_psk->group) { + char hex[PMK_LEN * 2 + 1]; + wpa_snprintf_hex(hex, sizeof(hex), + hapd->conf->ssid.wpa_psk->psk, PMK_LEN); + ret = os_snprintf(pos, end - pos, "psk=%s\n", hex); + if (os_snprintf_error(end - pos, ret)) + 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) { + ret = os_snprintf(pos, end - pos, "wpa=%d\n", hapd->conf->wpa); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) { + ret = os_snprintf(pos, end - pos, "key_mgmt="); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + pos += hostapd_ctrl_iface_get_key_mgmt(hapd, pos, end - pos); + + ret = os_snprintf(pos, end - pos, "\n"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + if (hapd->conf->wpa) { + ret = os_snprintf(pos, end - pos, "group_cipher=%s\n", + wpa_cipher_txt(hapd->conf->wpa_group)); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + if ((hapd->conf->wpa & WPA_PROTO_RSN) && hapd->conf->rsn_pairwise) { + ret = os_snprintf(pos, end - pos, "rsn_pairwise_cipher="); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + ret = wpa_write_ciphers(pos, end, hapd->conf->rsn_pairwise, + " "); + if (ret < 0) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, "\n"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + + if ((hapd->conf->wpa & WPA_PROTO_WPA) && hapd->conf->wpa_pairwise) { + ret = os_snprintf(pos, end - pos, "wpa_pairwise_cipher="); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + ret = wpa_write_ciphers(pos, end, hapd->conf->wpa_pairwise, + " "); + if (ret < 0) + return pos - buf; + pos += ret; + + ret = os_snprintf(pos, end - pos, "\n"); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + 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; +} + + +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; + int ret = 0; + + value = os_strchr(cmd, ' '); + if (value == NULL) + return -1; + *value++ = '\0'; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value); + if (0) { +#ifdef CONFIG_WPS_TESTING + } else if (os_strcasecmp(cmd, "wps_version_number") == 0) { + long int val; + val = strtol(value, NULL, 0); + if (val < 0 || val > 0xff) { + ret = -1; + wpa_printf(MSG_DEBUG, "WPS: Invalid " + "wps_version_number %ld", val); + } else { + wps_version_number = val; + wpa_printf(MSG_DEBUG, "WPS: Testing - force WPS " + "version %u.%u", + (wps_version_number & 0xf0) >> 4, + wps_version_number & 0x0f); + hostapd_wps_update_ie(hapd); + } + } else if (os_strcasecmp(cmd, "wps_testing_stub_cred") == 0) { + wps_testing_stub_cred = atoi(value); + wpa_printf(MSG_DEBUG, "WPS: Testing - stub_cred=%d", + wps_testing_stub_cred); + } else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) { + wps_corrupt_pkhash = atoi(value); + wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d", + wps_corrupt_pkhash); +#endif /* CONFIG_WPS_TESTING */ +#ifdef CONFIG_TESTING_OPTIONS + } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) { + hapd->ext_mgmt_frame_handling = atoi(value); + } else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) { + hapd->ext_eapol_frame_io = atoi(value); + } 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); + hapd->dpp_config_obj_override = os_strdup(value); + } else if (os_strcasecmp(cmd, "dpp_discovery_override") == 0) { + os_free(hapd->dpp_discovery_override); + hapd->dpp_discovery_override = os_strdup(value); + } else if (os_strcasecmp(cmd, "dpp_groups_override") == 0) { + os_free(hapd->dpp_groups_override); + hapd->dpp_groups_override = os_strdup(value); + } else if (os_strcasecmp(cmd, + "dpp_ignore_netaccesskey_mismatch") == 0) { + hapd->dpp_ignore_netaccesskey_mismatch = atoi(value); + } else if (os_strcasecmp(cmd, "dpp_test") == 0) { + dpp_test = atoi(value); + } else if (os_strcasecmp(cmd, "dpp_version_override") == 0) { + dpp_version_override = atoi(value); +#endif /* CONFIG_DPP */ +#endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_MBO + } else if (os_strcasecmp(cmd, "mbo_assoc_disallow") == 0) { + int val; + + if (!hapd->conf->mbo_enabled) + return -1; + + val = atoi(value); + if (val < 0 || val > MBO_ASSOC_DISALLOW_REASON_LOW_RSSI) + return -1; + + hapd->mbo_assoc_disallow = val; + ieee802_11_update_beacons(hapd->iface); + + /* + * TODO: Need to configure drivers that do AP MLME offload with + * disallowing station logic. + */ +#endif /* CONFIG_MBO */ +#ifdef CONFIG_DPP + } else if (os_strcasecmp(cmd, "dpp_configurator_params") == 0) { + os_free(hapd->dpp_configurator_params); + hapd->dpp_configurator_params = os_strdup(value); +#ifdef CONFIG_DPP2 + dpp_controller_set_params(hapd->iface->interfaces->dpp, value); +#endif /* CONFIG_DPP2 */ + } 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) + return ret; + + if (os_strcasecmp(cmd, "deny_mac_file") == 0) { + hostapd_disassoc_deny_mac(hapd); + } else if (os_strcasecmp(cmd, "accept_mac_file") == 0) { + hostapd_disassoc_accept_mac(hapd); + } else if (os_strcasecmp(cmd, "ssid") == 0) { + hostapd_neighbor_sync_own_report(hapd); + } else if (os_strncmp(cmd, "wme_ac_", 7) == 0 || + os_strncmp(cmd, "wmm_ac_", 7) == 0) { + hapd->parameter_set_count++; + if (ieee802_11_update_beacons(hapd->iface)) + wpa_printf(MSG_DEBUG, + "Failed to update beacons with WMM parameters"); + } 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; +} + + +static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd, + char *buf, size_t buflen) +{ + int res; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE GET '%s'", cmd); + + if (os_strcmp(cmd, "version") == 0) { + res = os_snprintf(buf, buflen, "%s", VERSION_STR); + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } else if (os_strcmp(cmd, "tls_library") == 0) { + res = tls_get_library_version(buf, buflen); + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } + + return -1; +} + + +static int hostapd_ctrl_iface_enable(struct hostapd_iface *iface) +{ + if (hostapd_enable_iface(iface) < 0) { + wpa_printf(MSG_ERROR, "Enabling of interface failed"); + return -1; + } + return 0; +} + + +static int hostapd_ctrl_iface_reload(struct hostapd_iface *iface) +{ + if (hostapd_reload_iface(iface) < 0) { + wpa_printf(MSG_ERROR, "Reloading of interface failed"); + return -1; + } + return 0; +} + + +static int hostapd_ctrl_iface_reload_bss(struct hostapd_data *bss) +{ + if (hostapd_reload_bss_only(bss) < 0) { + wpa_printf(MSG_ERROR, "Reloading of BSS failed"); + return -1; + } + return 0; +} + + +static int hostapd_ctrl_iface_disable(struct hostapd_iface *iface) +{ + if (hostapd_disable_iface(iface) < 0) { + wpa_printf(MSG_ERROR, "Disabling of interface failed"); + return -1; + } + return 0; +} + + +static int +hostapd_ctrl_iface_kick_mismatch_psk_sta_iter(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx) +{ + struct hostapd_wpa_psk *psk; + const u8 *pmk; + int pmk_len; + int pmk_match; + int sta_match; + int bss_match; + int reason; + + pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len); + + for (psk = hapd->conf->ssid.wpa_psk; pmk && psk; psk = psk->next) { + pmk_match = PMK_LEN == pmk_len && + os_memcmp(psk->psk, pmk, pmk_len) == 0; + sta_match = psk->group == 0 && + ether_addr_equal(sta->addr, psk->addr); + bss_match = psk->group == 1; + + if (pmk_match && (sta_match || bss_match)) + return 0; + } + + wpa_printf(MSG_INFO, "STA " MACSTR + " PSK/passphrase no longer valid - disconnect", + MAC2STR(sta->addr)); + reason = WLAN_REASON_PREV_AUTH_NOT_VALID; + hostapd_drv_sta_deauth(hapd, sta->addr, reason); + ap_sta_deauthenticate(hapd, sta, reason); + + return 0; +} + + +static int hostapd_ctrl_iface_reload_wpa_psk(struct hostapd_data *hapd) +{ + struct hostapd_bss_config *conf = hapd->conf; + int err; + + hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk); + + err = hostapd_setup_wpa_psk(conf); + if (err < 0) { + wpa_printf(MSG_ERROR, "Reloading WPA-PSK passwords failed: %d", + err); + return -1; + } + + ap_for_each_sta(hapd, hostapd_ctrl_iface_kick_mismatch_psk_sta_iter, + NULL); + + return 0; +} + + +#ifdef CONFIG_IEEE80211R_AP + +static int hostapd_ctrl_iface_get_rxkhs(struct hostapd_data *hapd, + char *buf, size_t buflen) +{ + int ret, start_pos; + char *pos, *end; + struct ft_remote_r0kh *r0kh; + struct ft_remote_r1kh *r1kh; + struct hostapd_bss_config *conf = hapd->conf; + + pos = buf; + end = buf + buflen; + + for (r0kh = conf->r0kh_list; r0kh; r0kh=r0kh->next) { + start_pos = pos - buf; + ret = os_snprintf(pos, end - pos, "r0kh=" MACSTR " ", + MAC2STR(r0kh->addr)); + if (os_snprintf_error(end - pos, ret)) + return start_pos; + pos += ret; + if (r0kh->id_len + 1 >= (size_t) (end - pos)) + return start_pos; + os_memcpy(pos, r0kh->id, r0kh->id_len); + pos += r0kh->id_len; + *pos++ = ' '; + pos += wpa_snprintf_hex(pos, end - pos, r0kh->key, + sizeof(r0kh->key)); + ret = os_snprintf(pos, end - pos, "\n"); + if (os_snprintf_error(end - pos, ret)) + return start_pos; + pos += ret; + } + + for (r1kh = conf->r1kh_list; r1kh; r1kh=r1kh->next) { + start_pos = pos - buf; + ret = os_snprintf(pos, end - pos, "r1kh=" MACSTR " " MACSTR " ", + MAC2STR(r1kh->addr), MAC2STR(r1kh->id)); + if (os_snprintf_error(end - pos, ret)) + return start_pos; + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, r1kh->key, + sizeof(r1kh->key)); + ret = os_snprintf(pos, end - pos, "\n"); + if (os_snprintf_error(end - pos, ret)) + return start_pos; + pos += ret; + } + + return pos - buf; +} + + +static int hostapd_ctrl_iface_reload_rxkhs(struct hostapd_data *hapd) +{ + struct hostapd_bss_config *conf = hapd->conf; + int err; + + hostapd_config_clear_rxkhs(conf); + + err = hostapd_config_read_rxkh_file(conf, conf->rxkh_file); + if (err < 0) { + wpa_printf(MSG_ERROR, "Reloading RxKHs failed: %d", + err); + return -1; + } + + return 0; +} + +#endif /* CONFIG_IEEE80211R_AP */ + + +#ifdef CONFIG_TESTING_OPTIONS + +static int hostapd_ctrl_iface_radar(struct hostapd_data *hapd, char *cmd) +{ + union wpa_event_data data; + char *pos, *param; + enum wpa_event_type event; + + wpa_printf(MSG_DEBUG, "RADAR TEST: %s", cmd); + + os_memset(&data, 0, sizeof(data)); + + param = os_strchr(cmd, ' '); + if (param == NULL) + return -1; + *param++ = '\0'; + + if (os_strcmp(cmd, "DETECTED") == 0) + event = EVENT_DFS_RADAR_DETECTED; + else if (os_strcmp(cmd, "CAC-FINISHED") == 0) + event = EVENT_DFS_CAC_FINISHED; + else if (os_strcmp(cmd, "CAC-ABORTED") == 0) + event = EVENT_DFS_CAC_ABORTED; + else if (os_strcmp(cmd, "NOP-FINISHED") == 0) + event = EVENT_DFS_NOP_FINISHED; + else { + wpa_printf(MSG_DEBUG, "Unsupported RADAR test command: %s", + cmd); + return -1; + } + + pos = os_strstr(param, "freq="); + if (pos) + data.dfs_event.freq = atoi(pos + 5); + + pos = os_strstr(param, "ht_enabled=1"); + if (pos) + data.dfs_event.ht_enabled = 1; + + pos = os_strstr(param, "chan_offset="); + if (pos) + data.dfs_event.chan_offset = atoi(pos + 12); + + pos = os_strstr(param, "chan_width="); + if (pos) + data.dfs_event.chan_width = atoi(pos + 11); + + pos = os_strstr(param, "cf1="); + if (pos) + data.dfs_event.cf1 = atoi(pos + 4); + + pos = os_strstr(param, "cf2="); + if (pos) + data.dfs_event.cf2 = atoi(pos + 4); + + wpa_supplicant_event(hapd, event, &data); + + return 0; +} + + +static int hostapd_ctrl_iface_mgmt_tx(struct hostapd_data *hapd, char *cmd) +{ + size_t len; + u8 *buf; + int res; + + wpa_printf(MSG_DEBUG, "External MGMT TX: %s", cmd); + + len = os_strlen(cmd); + if (len & 1) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(cmd, buf, len) < 0) { + os_free(buf); + return -1; + } + + res = hostapd_drv_send_mlme(hapd, buf, len, 0, NULL, 0, 0); + os_free(buf); + return res; +} + + +static int hostapd_ctrl_iface_mgmt_tx_status_process(struct hostapd_data *hapd, + char *cmd) +{ + char *pos, *param; + size_t len; + u8 *buf; + int stype = 0, ok = 0; + union wpa_event_data event; + + if (!hapd->ext_mgmt_frame_handling) + return -1; + + /* stype= ok=<0/1> buf= */ + + wpa_printf(MSG_DEBUG, "External MGMT TX status process: %s", cmd); + + pos = cmd; + param = os_strstr(pos, "stype="); + if (param) { + param += 6; + stype = atoi(param); + } + + param = os_strstr(pos, " ok="); + if (param) { + param += 4; + ok = atoi(param); + } + + param = os_strstr(pos, " buf="); + if (!param) + return -1; + param += 5; + + len = os_strlen(param); + if (len & 1) + return -1; + len /= 2; + + buf = os_malloc(len); + if (!buf || hexstr2bin(param, buf, len) < 0) { + os_free(buf); + return -1; + } + + os_memset(&event, 0, sizeof(event)); + event.tx_status.type = WLAN_FC_TYPE_MGMT; + event.tx_status.data = buf; + event.tx_status.data_len = len; + event.tx_status.stype = stype; + event.tx_status.ack = ok; + hapd->ext_mgmt_frame_handling = 0; + wpa_supplicant_event(hapd, EVENT_TX_STATUS, &event); + hapd->ext_mgmt_frame_handling = 1; + + os_free(buf); + + return 0; +} + + +static int hostapd_ctrl_iface_mgmt_rx_process(struct hostapd_data *hapd, + char *cmd) +{ + char *pos, *param; + size_t len; + u8 *buf; + int freq = 0, datarate = 0, ssi_signal = 0; + union wpa_event_data event; + + if (!hapd->ext_mgmt_frame_handling) + return -1; + + /* freq= datarate= ssi_signal= frame= */ + + wpa_printf(MSG_DEBUG, "External MGMT RX process: %s", cmd); + + pos = cmd; + param = os_strstr(pos, "freq="); + if (param) { + param += 5; + freq = atoi(param); + } + + param = os_strstr(pos, " datarate="); + if (param) { + param += 10; + datarate = atoi(param); + } + + param = os_strstr(pos, " ssi_signal="); + if (param) { + param += 12; + ssi_signal = atoi(param); + } + + param = os_strstr(pos, " frame="); + if (param == NULL) + return -1; + param += 7; + + len = os_strlen(param); + if (len & 1) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(param, buf, len) < 0) { + os_free(buf); + return -1; + } + + os_memset(&event, 0, sizeof(event)); + event.rx_mgmt.freq = freq; + event.rx_mgmt.frame = buf; + event.rx_mgmt.frame_len = len; + event.rx_mgmt.ssi_signal = ssi_signal; + event.rx_mgmt.datarate = datarate; + hapd->ext_mgmt_frame_handling = 0; + wpa_supplicant_event(hapd, EVENT_RX_MGMT, &event); + hapd->ext_mgmt_frame_handling = 1; + + os_free(buf); + + return 0; +} + + +static int hostapd_ctrl_iface_eapol_rx(struct hostapd_data *hapd, char *cmd) +{ + char *pos; + u8 src[ETH_ALEN], *buf; + int used; + size_t len; + + wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd); + + pos = cmd; + used = hwaddr_aton2(pos, src); + if (used < 0) + return -1; + pos += used; + while (*pos == ' ') + pos++; + + len = os_strlen(pos); + if (len & 1) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(pos, buf, len) < 0) { + os_free(buf); + return -1; + } + + ieee802_1x_receive(hapd, src, buf, len, FRAME_ENCRYPTION_UNKNOWN); + os_free(buf); + + return 0; +} + + +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; + u32 sum = 0; + const u16 *pos = buf; + + for (i = 0; i < len / 2; i++) + sum += *pos++; + + while (sum >> 16) + sum = (sum & 0xffff) + (sum >> 16); + + return sum ^ 0xffff; +} + + +#define HWSIM_PACKETLEN 1500 +#define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header)) + +static void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct hostapd_data *hapd = ctx; + const struct ether_header *eth; + struct ip ip; + const u8 *pos; + unsigned int i; + char extra[30]; + + if (len < sizeof(*eth) + sizeof(ip) || len > HWSIM_PACKETLEN) { + wpa_printf(MSG_DEBUG, + "test data: RX - ignore unexpected length %d", + (int) len); + return; + } + + eth = (const struct ether_header *) buf; + os_memcpy(&ip, eth + 1, sizeof(ip)); + pos = &buf[sizeof(*eth) + sizeof(ip)]; + + if (ip.ip_hl != 5 || ip.ip_v != 4 || + ntohs(ip.ip_len) > HWSIM_IP_LEN) { + wpa_printf(MSG_DEBUG, + "test data: RX - ignore unexpected IP header"); + return; + } + + 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"); + return; + } + pos++; + } + + extra[0] = '\0'; + 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); +} + + +static int hostapd_ctrl_iface_data_test_config(struct hostapd_data *hapd, + char *cmd) +{ + int enabled = atoi(cmd); + char *pos; + const char *ifname; + const u8 *addr = hapd->own_addr; + + if (!enabled) { + if (hapd->l2_test) { + l2_packet_deinit(hapd->l2_test); + hapd->l2_test = NULL; + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, + "test data: Disabled"); + } + return 0; + } + + if (hapd->l2_test) + return 0; + + pos = os_strstr(cmd, " ifname="); + if (pos) + ifname = pos + 8; + else + ifname = hapd->conf->iface; + +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) + addr = hapd->mld->mld_addr; +#endif /* CONFIG_IEEE80211BE */ + hapd->l2_test = l2_packet_init(ifname, addr, + ETHERTYPE_IP, hostapd_data_test_rx, + hapd, 1); + if (hapd->l2_test == NULL) + return -1; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: Enabled"); + + return 0; +} + + +static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd) +{ + u8 dst[ETH_ALEN], src[ETH_ALEN]; + char *pos, *pos2; + int used; + long int val; + u8 tos; + u8 buf[2 + HWSIM_PACKETLEN]; + struct ether_header *eth; + struct ip *ip; + u8 *dpos; + unsigned int i; + size_t send_len = HWSIM_IP_LEN; + + if (hapd->l2_test == NULL) + return -1; + + /* format: [len=] */ + + pos = cmd; + used = hwaddr_aton2(pos, dst); + if (used < 0) + return -1; + pos += used; + while (*pos == ' ') + pos++; + used = hwaddr_aton2(pos, src); + if (used < 0) + return -1; + pos += used; + + val = strtol(pos, &pos2, 0); + if (val < 0 || val > 0xff) + return -1; + tos = val; + + pos = os_strstr(pos2, " len="); + if (pos) { + i = atoi(pos + 5); + if (i < sizeof(*ip) || i > HWSIM_IP_LEN) + return -1; + send_len = i; + } + + eth = (struct ether_header *) &buf[2]; + os_memcpy(eth->ether_dhost, dst, ETH_ALEN); + os_memcpy(eth->ether_shost, src, ETH_ALEN); + eth->ether_type = htons(ETHERTYPE_IP); + ip = (struct ip *) (eth + 1); + os_memset(ip, 0, 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; + + if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, &buf[2], + sizeof(struct ether_header) + send_len) < 0) + return -1; + + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX dst=" MACSTR + " src=" MACSTR " tos=0x%x", MAC2STR(dst), MAC2STR(src), tos); + + return 0; +} + + +static int hostapd_ctrl_iface_data_test_frame(struct hostapd_data *hapd, + char *cmd) +{ + u8 *buf; + struct ether_header *eth; + struct l2_packet_data *l2 = NULL; + size_t len; + u16 ethertype; + int res = -1; + const char *ifname = hapd->conf->iface; + + if (os_strncmp(cmd, "ifname=", 7) == 0) { + cmd += 7; + ifname = cmd; + cmd = os_strchr(cmd, ' '); + if (cmd == NULL) + return -1; + *cmd++ = '\0'; + } + + len = os_strlen(cmd); + if (len & 1 || len < ETH_HLEN * 2) + return -1; + len /= 2; + + buf = os_malloc(len); + if (buf == NULL) + return -1; + + if (hexstr2bin(cmd, buf, len) < 0) + goto done; + + eth = (struct ether_header *) buf; + ethertype = ntohs(eth->ether_type); + + l2 = l2_packet_init(ifname, hapd->own_addr, ethertype, + hostapd_data_test_rx, hapd, 1); + if (l2 == NULL) + goto done; + + res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len); + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX frame res=%d", res); +done: + if (l2) + l2_packet_deinit(l2); + os_free(buf); + + return res < 0 ? -1 : 0; +} + + +static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd) +{ + struct sta_info *sta; + u8 addr[ETH_ALEN]; + u8 zero[WPA_TK_MAX_LEN]; + + os_memset(zero, 0, sizeof(zero)); + + if (hwaddr_aton(cmd, addr)) + return -1; + + 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; + + wpa_printf(MSG_INFO, "TESTING: Reset IPN for IGTK"); + + /* First, use a zero key to avoid any possible duplicate key + * avoidance in the driver. */ + if (hostapd_drv_set_key(hapd->conf->iface, hapd, + hapd->last_igtk_alg, + broadcast_ether_addr, + hapd->last_igtk_key_idx, 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, 0, 1, NULL, + 0, hapd->last_igtk, + hapd->last_igtk_len, + KEY_FLAG_GROUP_TX_DEFAULT); + } + + if (is_broadcast_ether_addr(addr)) { + if (hapd->last_gtk_alg == WPA_ALG_NONE) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Reset PN for GTK"); + + /* First, use a zero key to avoid any possible duplicate key + * avoidance in the driver. */ + if (hostapd_drv_set_key(hapd->conf->iface, hapd, + hapd->last_gtk_alg, + broadcast_ether_addr, + hapd->last_gtk_key_idx, 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, 0, 1, NULL, + 0, hapd->last_gtk, + hapd->last_gtk_len, + KEY_FLAG_GROUP_TX_DEFAULT); + } + + sta = ap_get_sta(hapd, addr); + if (!sta) + return -1; + + if (sta->last_tk_alg == WPA_ALG_NONE) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Reset PN for " MACSTR, + MAC2STR(sta->addr)); + + /* First, use a zero key to avoid any possible duplicate key avoidance + * in the driver. */ + if (hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg, + sta->addr, sta->last_tk_key_idx, 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, 0, 1, NULL, + 0, sta->last_tk, sta->last_tk_len, + KEY_FLAG_PAIRWISE_RX_TX); +} + + +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 key_flag */ + + alg = atoi(pos); + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + if (hwaddr_aton(pos, addr)) + return -1; + pos += 17; + if (*pos != ' ') + return -1; + pos++; + idx = atoi(pos); + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + set_tx = atoi(pos); + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + if (hexstr2bin(pos, seq, sizeof(seq)) < 0) + return -1; + pos += 2 * 6; + if (*pos != ' ') + return -1; + pos++; + 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, 0, + set_tx, seq, 6, key, key_len, key_flag); +} + + +static void restore_tk(void *ctx1, void *ctx2) +{ + struct hostapd_data *hapd = ctx1; + struct sta_info *sta = ctx2; + + wpa_printf(MSG_INFO, "TESTING: Restore TK for " MACSTR, + MAC2STR(sta->addr)); + /* This does not really restore the TSC properly, so this will result + * in replay protection issues for now since there is no clean way of + * preventing encryption of a single EAPOL frame. */ + hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg, + sta->addr, sta->last_tk_key_idx, 0, 1, NULL, 0, + sta->last_tk, sta->last_tk_len, + KEY_FLAG_PAIRWISE_RX_TX); +} + + +static int hostapd_ctrl_resend_m1(struct hostapd_data *hapd, const char *cmd) +{ + struct sta_info *sta; + u8 addr[ETH_ALEN]; + int plain = os_strstr(cmd, "plaintext") != NULL; + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->wpa_sm) + return -1; + + if (plain && sta->last_tk_alg == WPA_ALG_NONE) + plain = 0; /* no need for special processing */ + if (plain) { + wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR, + MAC2STR(sta->addr)); + hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE, + sta->addr, sta->last_tk_key_idx, 0, 0, NULL, + 0, NULL, 0, KEY_FLAG_PAIRWISE); + } + + wpa_printf(MSG_INFO, "TESTING: Send M1 to " MACSTR, MAC2STR(sta->addr)); + return wpa_auth_resend_m1(sta->wpa_sm, + os_strstr(cmd, "change-anonce") != NULL, + plain ? restore_tk : NULL, hapd, sta); +} + + +static int hostapd_ctrl_resend_m3(struct hostapd_data *hapd, const char *cmd) +{ + struct sta_info *sta; + u8 addr[ETH_ALEN]; + int plain = os_strstr(cmd, "plaintext") != NULL; + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->wpa_sm) + return -1; + + if (plain && sta->last_tk_alg == WPA_ALG_NONE) + plain = 0; /* no need for special processing */ + if (plain) { + wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR, + MAC2STR(sta->addr)); + hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE, + sta->addr, sta->last_tk_key_idx, 0, 0, NULL, + 0, NULL, 0, KEY_FLAG_PAIRWISE); + } + + wpa_printf(MSG_INFO, "TESTING: Send M3 to " MACSTR, MAC2STR(sta->addr)); + return wpa_auth_resend_m3(sta->wpa_sm, + plain ? restore_tk : NULL, hapd, sta); +} + + +static int hostapd_ctrl_resend_group_m1(struct hostapd_data *hapd, + const char *cmd) +{ + struct sta_info *sta; + u8 addr[ETH_ALEN]; + int plain = os_strstr(cmd, "plaintext") != NULL; + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->wpa_sm) + return -1; + + if (plain && sta->last_tk_alg == WPA_ALG_NONE) + plain = 0; /* no need for special processing */ + if (plain) { + wpa_printf(MSG_INFO, "TESTING: Clear TK for " MACSTR, + MAC2STR(sta->addr)); + hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_NONE, + sta->addr, sta->last_tk_key_idx, 0, 0, NULL, + 0, NULL, 0, KEY_FLAG_PAIRWISE); + } + + wpa_printf(MSG_INFO, + "TESTING: Send group M1 for the same GTK and zero RSC to " + MACSTR, MAC2STR(sta->addr)); + return wpa_auth_resend_group_m1(sta->wpa_sm, + plain ? restore_tk : NULL, hapd, sta); +} + + +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, + u16 punct_bitmap) +{ + u32 start_freq; + + if (is_6ghz_freq(params->freq)) { + const int bw_idx[] = { 20, 40, 80, 160, 320 }; + int idx, bw; + + /* The 6 GHz band requires HE to be enabled. */ + params->he_enabled = 1; + + if (params->center_freq1) { + if (params->freq == 5935) + idx = (params->center_freq1 - 5925) / 5; + else + idx = (params->center_freq1 - 5950) / 5; + + bw = center_idx_to_bw_6ghz(idx); + if (bw < 0 || bw > (int) ARRAY_SIZE(bw_idx) || + bw_idx[bw] != params->bandwidth) + return -1; + } + } else { /* Non-6 GHz channel */ + /* An EHT STA is also an HE STA as defined in + * IEEE P802.11be/D5.0, 4.3.16a. */ + if (params->he_enabled || params->eht_enabled) { + params->he_enabled = 1; + /* An HE STA is also a VHT STA if operating in the 5 GHz + * band and an HE STA is also an HT STA in the 2.4 GHz + * band as defined in IEEE Std 802.11ax-2021, 4.3.15a. + * A VHT STA is an HT STA as defined in IEEE + * Std 802.11, 4.3.15. */ + if (IS_5GHZ(params->freq)) + params->vht_enabled = 1; + + params->ht_enabled = 1; + } + } + + 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; + + if (punct_bitmap) + return -1; + break; + case 40: + if (params->center_freq2 || !params->sec_channel_offset) + return -1; + + if (punct_bitmap) + 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; + } + + if (params->center_freq2 && punct_bitmap) + 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; + case 320: + if (!params->center_freq1 || params->center_freq2 || + !params->sec_channel_offset) + return -1; + + switch (params->sec_channel_offset) { + case 1: + if (params->freq + 150 != params->center_freq1 && + params->freq + 110 != params->center_freq1 && + params->freq + 70 != params->center_freq1 && + params->freq + 30 != params->center_freq1 && + params->freq - 10 != params->center_freq1 && + params->freq - 50 != params->center_freq1 && + params->freq - 90 != params->center_freq1 && + params->freq - 130 != params->center_freq1) + return -1; + break; + case -1: + if (params->freq + 130 != params->center_freq1 && + params->freq + 90 != params->center_freq1 && + params->freq + 50 != params->center_freq1 && + params->freq + 10 != params->center_freq1 && + params->freq - 30 != params->center_freq1 && + params->freq - 70 != params->center_freq1 && + params->freq - 110 != params->center_freq1 && + params->freq - 150 != params->center_freq1) + return -1; + break; + } + break; + default: + return -1; + } + + if (!punct_bitmap) + return 0; + + if (!params->eht_enabled) { + wpa_printf(MSG_ERROR, + "Preamble puncturing supported only in EHT"); + return -1; + } + + if (params->freq >= 2412 && params->freq <= 2484) { + wpa_printf(MSG_ERROR, + "Preamble puncturing is not supported in 2.4 GHz"); + return -1; + } + + start_freq = params->center_freq1 - (params->bandwidth / 2); + if (!is_punct_bitmap_valid(params->bandwidth, + (params->freq - start_freq) / 20, + punct_bitmap)) { + wpa_printf(MSG_ERROR, "Invalid preamble puncturing bitmap"); + 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; + unsigned int num_err = 0; + int err = 0; + + ret = hostapd_parse_csa_settings(pos, &settings); + if (ret) + return ret; + + settings.link_id = -1; +#ifdef CONFIG_IEEE80211BE + if (iface->num_bss && iface->bss[0]->conf->mld_ap) + settings.link_id = iface->bss[0]->mld_link_id; +#endif /* CONFIG_IEEE80211BE */ + + ret = hostapd_ctrl_check_freq_params(&settings.freq_params, + settings.punct_bitmap); + 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; + case 320: + bandwidth = CHAN_WIDTH_320; + 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 */ + iface->is_ch_switch_dfs = true; + hostapd_switch_channel_fallback(iface, &settings.freq_params); + return 0; + } + + for (i = 0; i < iface->num_bss; i++) { + + /* Save CHAN_SWITCH VHT, HE, and EHT config */ + hostapd_chan_switch_config(iface->bss[i], + &settings.freq_params); + + err = hostapd_switch_channel(iface->bss[i], &settings); + if (err) { + ret = err; + num_err++; + } + } + + return (iface->num_bss == num_err) ? ret : 0; +#else /* NEED_AP_MLME */ + return -1; +#endif /* NEED_AP_MLME */ +} + + +#ifdef CONFIG_IEEE80211AX +static int hostapd_ctrl_iface_color_change(struct hostapd_iface *iface, + const char *pos) +{ +#ifdef NEED_AP_MLME + struct cca_settings settings; + struct hostapd_data *hapd = iface->bss[0]; + int ret, color; + unsigned int i; + char *end; + + os_memset(&settings, 0, sizeof(settings)); + + color = strtol(pos, &end, 10); + if (pos == end || color < 0 || color > 63) { + wpa_printf(MSG_ERROR, "color_change: Invalid color provided"); + return -1; + } + + /* Color value is expected to be [1-63]. If 0 comes, assumption is this + * is to disable the color. In this case no need to do CCA, just + * changing Beacon frames is sufficient. */ + if (color == 0) { + if (iface->conf->he_op.he_bss_color_disabled) { + wpa_printf(MSG_ERROR, + "color_change: Color is already disabled"); + return -1; + } + + iface->conf->he_op.he_bss_color_disabled = 1; + + for (i = 0; i < iface->num_bss; i++) + ieee802_11_set_beacon(iface->bss[i]); + + return 0; + } + + if (color == iface->conf->he_op.he_bss_color) { + if (!iface->conf->he_op.he_bss_color_disabled) { + wpa_printf(MSG_ERROR, + "color_change: Provided color is already set"); + return -1; + } + + iface->conf->he_op.he_bss_color_disabled = 0; + + for (i = 0; i < iface->num_bss; i++) + ieee802_11_set_beacon(iface->bss[i]); + + return 0; + } + + if (hapd->cca_in_progress) { + wpa_printf(MSG_ERROR, + "color_change: CCA is already in progress"); + return -1; + } + + iface->conf->he_op.he_bss_color_disabled = 0; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *bss = iface->bss[i]; + + hostapd_cleanup_cca_params(bss); + + bss->cca_color = color; + bss->cca_count = 10; + + if (hostapd_fill_cca_settings(bss, &settings)) { + wpa_printf(MSG_DEBUG, + "color_change: Filling CCA settings failed for color: %d\n", + color); + hostapd_cleanup_cca_params(bss); + continue; + } + + wpa_printf(MSG_DEBUG, "Setting user selected color: %d", color); + ret = hostapd_drv_switch_color(bss, &settings); + if (ret) + hostapd_cleanup_cca_params(bss); + + free_beacon_data(&settings.beacon_cca); + free_beacon_data(&settings.beacon_after); + } + + return 0; +#else /* NEED_AP_MLME */ + return -1; +#endif /* NEED_AP_MLME */ +} +#endif /* CONFIG_IEEE80211AX */ + + +static u8 hostapd_maxnss(struct hostapd_data *hapd, struct sta_info *sta) +{ + u8 *mcs_set = NULL; + u16 mcs_map; + u8 ht_rx_nss = 0; + u8 vht_rx_nss = 1; + u8 mcs; + bool ht_supported = false; + bool vht_supported = false; + int i; + + if (sta->ht_capabilities && (sta->flags & WLAN_STA_HT)) { + mcs_set = sta->ht_capabilities->supported_mcs_set; + ht_supported = true; + } + + if (sta->vht_capabilities && (sta->flags & WLAN_STA_VHT)) { + mcs_map = le_to_host16( + sta->vht_capabilities->vht_supported_mcs_set.rx_map); + vht_supported = true; + } + + if (ht_supported && mcs_set) { + if (mcs_set[0]) + ht_rx_nss++; + if (mcs_set[1]) + ht_rx_nss++; + if (mcs_set[2]) + ht_rx_nss++; + if (mcs_set[3]) + ht_rx_nss++; + } + if (vht_supported) { + for (i = 7; i >= 0; i--) { + mcs = (mcs_map >> (2 * i)) & 0x03; + if (mcs != 0x03) { + vht_rx_nss = i + 1; + break; + } + } + } + + return ht_rx_nss > vht_rx_nss ? ht_rx_nss : vht_rx_nss; +} + + +static char hostapd_ctrl_iface_notify_cw_htaction(struct hostapd_data *hapd, + const u8 *addr, u8 width) +{ + u8 buf[3]; + char ret; + + width = width >= 1 ? 1 : 0; + + buf[0] = WLAN_ACTION_HT; + buf[1] = WLAN_HT_ACTION_NOTIFY_CHANWIDTH; + buf[2] = width; + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + buf, sizeof(buf)); + if (ret) + wpa_printf(MSG_DEBUG, + "Failed to send Notify Channel Width frame to " + MACSTR, MAC2STR(addr)); + + return ret; +} + + +static char hostapd_ctrl_iface_notify_cw_vhtaction(struct hostapd_data *hapd, + const u8 *addr, u8 width) +{ + u8 buf[3]; + char ret; + + buf[0] = WLAN_ACTION_VHT; + buf[1] = WLAN_VHT_ACTION_OPMODE_NOTIF; + buf[2] = width; + + ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, + buf, sizeof(buf)); + if (ret) + wpa_printf(MSG_DEBUG, + "Failed to send Opeating Mode Notification frame to " + MACSTR, MAC2STR(addr)); + + return ret; +} + + +static char hostapd_ctrl_iface_notify_cw_change(struct hostapd_data *hapd, + const char *cmd) +{ + u8 cw, operating_mode = 0, nss; + struct sta_info *sta; + enum hostapd_hw_mode hw_mode; + + if (is_6ghz_freq(hapd->iface->freq)) { + wpa_printf(MSG_ERROR, "20/40 BSS coex not supported in 6 GHz"); + return -1; + } + + cw = atoi(cmd); + hw_mode = hapd->iface->current_mode->mode; + if ((hw_mode == HOSTAPD_MODE_IEEE80211G || + hw_mode == HOSTAPD_MODE_IEEE80211B) && + !(cw == 0 || cw == 1)) { + wpa_printf(MSG_ERROR, + "Channel width should be either 20 MHz or 40 MHz for 2.4 GHz band"); + return -1; + } + + switch (cw) { + case 0: + operating_mode = 0; + break; + case 1: + operating_mode = VHT_OPMODE_CHANNEL_40MHZ; + break; + case 2: + operating_mode = VHT_OPMODE_CHANNEL_80MHZ; + break; + case 3: + operating_mode = VHT_OPMODE_CHANNEL_160MHZ; + break; + default: + wpa_printf(MSG_ERROR, "Channel width should be between 0 to 3"); + return -1; + } + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if ((sta->flags & WLAN_STA_VHT) && sta->vht_capabilities) { + nss = hostapd_maxnss(hapd, sta) - 1; + hostapd_ctrl_iface_notify_cw_vhtaction(hapd, sta->addr, + operating_mode | + (u8) (nss << 4)); + continue; + } + + if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) == + WLAN_STA_HT && sta->ht_capabilities) + hostapd_ctrl_iface_notify_cw_htaction(hapd, sta->addr, + cw); + } + + return 0; +} + + +static int hostapd_ctrl_iface_mib(struct hostapd_data *hapd, char *reply, + int reply_size, const char *param) +{ +#ifdef RADIUS_SERVER + if (os_strcmp(param, "radius_server") == 0) { + return radius_server_get_mib(hapd->radius_srv, reply, + reply_size); + } +#endif /* RADIUS_SERVER */ + return -1; +} + + +static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd, + char *buf, size_t buflen) +{ + int ret; + 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: [] + * [nested=<0|1>] + */ + vendor_id = strtoul(cmd, &pos, 16); + if (!isblank((unsigned char) *pos)) + return -EINVAL; + + subcmd = strtoul(pos, &pos, 10); + + if (*pos != '\0') { + if (!isblank((unsigned char) *pos++)) + return -EINVAL; + + temp = os_strchr(pos, ' '); + data_len = temp ? (size_t) (temp - pos) : os_strlen(pos); + } + + if (data_len) { + data_len /= 2; + data = os_malloc(data_len); + if (!data) + return -ENOBUFS; + + if (hexstr2bin(pos, data, data_len)) { + wpa_printf(MSG_DEBUG, + "Vendor command: wrong parameter format"); + os_free(data); + return -EINVAL; + } + } + + 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); + return -ENOBUFS; + } + + ret = hostapd_drv_vendor_cmd(hapd, vendor_id, subcmd, data, data_len, + nested_attr_flag, reply); + + if (ret == 0) + ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply), + wpabuf_len(reply)); + + wpabuf_free(reply); + os_free(data); + + return ret; +} + + +static int hostapd_ctrl_iface_eapol_reauth(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->eapol_sm) + return -1; + + eapol_auth_reauthenticate(sta->eapol_sm); + return 0; +} + + +static int hostapd_ctrl_iface_eapol_set(struct hostapd_data *hapd, char *cmd) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + char *pos = cmd, *param; + + if (hwaddr_aton(pos, addr) || pos[17] != ' ') + return -1; + pos += 18; + param = pos; + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + *pos++ = '\0'; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->eapol_sm) + return -1; + + return eapol_auth_set_conf(sta->eapol_sm, param, pos); +} + + +static int hostapd_ctrl_iface_log_level(struct hostapd_data *hapd, char *cmd, + char *buf, size_t buflen) +{ + char *pos, *end, *stamp; + int ret; + + /* cmd: "LOG_LEVEL []" */ + if (*cmd == '\0') { + pos = buf; + end = buf + buflen; + ret = os_snprintf(pos, end - pos, "Current level: %s\n" + "Timestamp: %d\n", + debug_level_str(wpa_debug_level), + wpa_debug_timestamp); + if (os_snprintf_error(end - pos, ret)) + ret = 0; + + return ret; + } + + while (*cmd == ' ') + cmd++; + + stamp = os_strchr(cmd, ' '); + if (stamp) { + *stamp++ = '\0'; + while (*stamp == ' ') { + stamp++; + } + } + + if (os_strlen(cmd)) { + int level = str_to_debug_level(cmd); + if (level < 0) + return -1; + wpa_debug_level = level; + } + + if (stamp && os_strlen(stamp)) + wpa_debug_timestamp = atoi(stamp); + + os_memcpy(buf, "OK\n", 3); + return 3; +} + + +#ifdef NEED_AP_MLME +static int hostapd_ctrl_iface_track_sta_list(struct hostapd_data *hapd, + char *buf, size_t buflen) +{ + struct hostapd_iface *iface = hapd->iface; + char *pos, *end; + struct hostapd_sta_info *info; + struct os_reltime now; + + if (!iface->num_sta_seen) + return 0; + + sta_track_expire(iface, 0); + + pos = buf; + end = buf + buflen; + + os_get_reltime(&now); + dl_list_for_each_reverse(info, &iface->sta_seen, + struct hostapd_sta_info, list) { + struct os_reltime age; + int ret; + + os_reltime_sub(&now, &info->last_seen, &age); + ret = os_snprintf(pos, end - pos, MACSTR " %u %d\n", + MAC2STR(info->addr), (unsigned int) age.sec, + info->ssi_signal); + if (os_snprintf_error(end - pos, ret)) + break; + pos += ret; + } + + return pos - buf; +} +#endif /* NEED_AP_MLME */ + + +static int hostapd_ctrl_iface_req_lci(struct hostapd_data *hapd, + const char *cmd) +{ + u8 addr[ETH_ALEN]; + + if (hwaddr_aton(cmd, addr)) { + wpa_printf(MSG_INFO, "CTRL: REQ_LCI: Invalid MAC address"); + return -1; + } + + return hostapd_send_lci_req(hapd, addr); +} + + +static int hostapd_ctrl_iface_req_range(struct hostapd_data *hapd, char *cmd) +{ + u8 addr[ETH_ALEN]; + char *token, *context = NULL; + int random_interval, min_ap; + u8 responders[ETH_ALEN * RRM_RANGE_REQ_MAX_RESPONDERS]; + unsigned int n_responders; + + token = str_token(cmd, " ", &context); + if (!token || hwaddr_aton(token, addr)) { + wpa_printf(MSG_INFO, + "CTRL: REQ_RANGE - Bad destination address"); + return -1; + } + + token = str_token(cmd, " ", &context); + if (!token) + return -1; + + random_interval = atoi(token); + if (random_interval < 0 || random_interval > 0xffff) + return -1; + + token = str_token(cmd, " ", &context); + if (!token) + return -1; + + min_ap = atoi(token); + if (min_ap <= 0 || min_ap > WLAN_RRM_RANGE_REQ_MAX_MIN_AP) + return -1; + + n_responders = 0; + while ((token = str_token(cmd, " ", &context))) { + if (n_responders == RRM_RANGE_REQ_MAX_RESPONDERS) { + wpa_printf(MSG_INFO, + "CTRL: REQ_RANGE: Too many responders"); + return -1; + } + + if (hwaddr_aton(token, responders + n_responders * ETH_ALEN)) { + wpa_printf(MSG_INFO, + "CTRL: REQ_RANGE: Bad responder address"); + return -1; + } + + n_responders++; + } + + if (!n_responders) { + wpa_printf(MSG_INFO, + "CTRL: REQ_RANGE - No FTM responder address"); + return -1; + } + + return hostapd_send_range_req(hapd, addr, random_interval, min_ap, + responders, n_responders); +} + + +static int hostapd_ctrl_iface_req_beacon(struct hostapd_data *hapd, + const char *cmd, char *reply, + size_t reply_size) +{ + u8 addr[ETH_ALEN]; + const char *pos; + struct wpabuf *req; + int ret; + u8 req_mode = 0; + + if (hwaddr_aton(cmd, addr)) + return -1; + pos = os_strchr(cmd, ' '); + if (!pos) + return -1; + pos++; + if (os_strncmp(pos, "req_mode=", 9) == 0) { + int val = hex2byte(pos + 9); + + if (val < 0) + return -1; + req_mode = val; + pos += 11; + pos = os_strchr(pos, ' '); + if (!pos) + return -1; + pos++; + } + req = wpabuf_parse_bin(pos); + if (!req) + return -1; + + ret = hostapd_send_beacon_req(hapd, addr, req_mode, req); + wpabuf_free(req); + if (ret >= 0) + ret = os_snprintf(reply, reply_size, "%d", ret); + return ret; +} + + +static int hostapd_ctrl_iface_req_link_measurement(struct hostapd_data *hapd, + const char *cmd, char *reply, + size_t reply_size) +{ + u8 addr[ETH_ALEN]; + int ret; + + if (hwaddr_aton(cmd, addr)) { + wpa_printf(MSG_ERROR, + "CTRL: REQ_LINK_MEASUREMENT: Invalid MAC address"); + return -1; + } + + ret = hostapd_send_link_measurement_req(hapd, addr); + if (ret >= 0) + ret = os_snprintf(reply, reply_size, "%d", ret); + return ret; +} + + +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; + u8 bssid[ETH_ALEN]; + struct wpabuf *nr, *lci = NULL, *civic = NULL; + int stationary = 0; + int bss_parameters = 0; + char *tmp; + int ret = -1; + + if (!(hapd->conf->radio_measurements[0] & + WLAN_RRM_CAPS_NEIGHBOR_REPORT)) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Neighbor report is not enabled"); + return -1; + } + + if (hwaddr_aton(buf, bssid)) { + wpa_printf(MSG_ERROR, "CTRL: SET_NEIGHBOR: Bad BSSID"); + return -1; + } + + tmp = os_strstr(buf, "ssid="); + if (!tmp || ssid_parse(tmp + 5, &ssid)) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad or missing SSID"); + return -1; + } + buf = os_strchr(tmp + 6, tmp[5] == '"' ? '"' : ' '); + if (!buf) + return -1; + + tmp = os_strstr(buf, "nr="); + if (!tmp) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Missing Neighbor Report element"); + return -1; + } + + buf = os_strchr(tmp, ' '); + if (buf) + *buf++ = '\0'; + + nr = wpabuf_parse_bin(tmp + 3); + if (!nr) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad Neighbor Report element"); + return -1; + } + + if (!buf) + goto set; + + tmp = os_strstr(buf, "lci="); + if (tmp) { + buf = os_strchr(tmp, ' '); + if (buf) + *buf++ = '\0'; + lci = wpabuf_parse_bin(tmp + 4); + if (!lci) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad LCI subelement"); + goto fail; + } + } + + if (!buf) + goto set; + + tmp = os_strstr(buf, "civic="); + if (tmp) { + buf = os_strchr(tmp, ' '); + if (buf) + *buf++ = '\0'; + civic = wpabuf_parse_bin(tmp + 6); + if (!civic) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad civic subelement"); + goto fail; + } + } + + if (!buf) + goto set; + + if (os_strstr(buf, "stat")) + stationary = 1; + + tmp = os_strstr(buf, "bss_parameter="); + if (tmp) { + bss_parameters = atoi(tmp + 14); + if (bss_parameters < 0 || bss_parameters > 0xff) { + wpa_printf(MSG_ERROR, + "CTRL: SET_NEIGHBOR: Bad bss_parameters subelement"); + goto fail; + } + } + +set: + ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic, + stationary, bss_parameters); + +fail: + wpabuf_free(nr); + wpabuf_free(lci); + wpabuf_free(civic); + + return ret; +} + + +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; + + if (hwaddr_aton(buf, bssid)) { + wpa_printf(MSG_ERROR, "CTRL: REMOVE_NEIGHBOR: Bad BSSID"); + return -1; + } + + tmp = os_strstr(buf, "ssid="); + 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, ssidp); +} + + +static int hostapd_ctrl_driver_flags(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_flags); + if (os_snprintf_error(buflen, ret)) + return -1; + + pos = buf + ret; + end = buf + buflen; + + for (i = 0; i < 64; i++) { + if (iface->drv_flags & (1LLU << i)) { + ret = os_snprintf(pos, end - pos, "%s\n", + driver_flag_to_string(1LLU << i)); + if (os_snprintf_error(end - pos, ret)) + return -1; + pos += ret; + } + } + + return pos - 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_get_capability(struct hostapd_data *hapd, + const char *field, char *buf, + size_t buflen) +{ + wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s'", field); + +#ifdef CONFIG_DPP + if (os_strcmp(field, "dpp") == 0) { + int res; + +#ifdef CONFIG_DPP3 + res = os_snprintf(buf, buflen, "DPP=3"); +#elif defined(CONFIG_DPP2) + res = os_snprintf(buf, buflen, "DPP=2"); +#else /* CONFIG_DPP2 */ + res = os_snprintf(buf, buflen, "DPP=1"); +#endif /* CONFIG_DPP2 */ + if (os_snprintf_error(buflen, res)) + return -1; + return res; + } +#endif /* CONFIG_DPP */ + + wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'", + field); + + return -1; +} + + +#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 */ + + +#ifdef CONFIG_IEEE80211BE + +static int hostapd_ctrl_iface_enable_mld(struct hostapd_iface *iface) +{ + unsigned int i; + + if (!iface || !iface->bss[0]->conf->mld_ap) { + wpa_printf(MSG_ERROR, + "Trying to enable AP MLD on an interface that is not affiliated with an AP MLD"); + return -1; + } + + for (i = 0; i < iface->interfaces->count; ++i) { + struct hostapd_iface *h_iface = iface->interfaces->iface[i]; + struct hostapd_data *h_hapd = h_iface->bss[0]; + + if (!hostapd_is_ml_partner(h_hapd, iface->bss[0])) + continue; + + if (hostapd_enable_iface(h_iface)) { + wpa_printf(MSG_ERROR, "Enabling of AP MLD failed"); + return -1; + } + } + return 0; +} + + +static void hostapd_disable_iface_bss(struct hostapd_iface *iface) +{ + unsigned int i; + + for (i = 0; i < iface->num_bss; i++) + hostapd_bss_deinit_no_free(iface->bss[i]); +} + + +static int hostapd_ctrl_iface_disable_mld(struct hostapd_iface *iface) +{ + unsigned int i; + + if (!iface || !iface->bss[0]->conf->mld_ap) { + wpa_printf(MSG_ERROR, + "Trying to disable AP MLD on an interface that is not affiliated with an AP MLD."); + return -1; + } + + /* First, disable BSSs before stopping beaconing and doing driver + * deinit so that the broadcast Deauthentication frames go out. */ + + for (i = 0; i < iface->interfaces->count; ++i) { + struct hostapd_iface *h_iface = iface->interfaces->iface[i]; + struct hostapd_data *h_hapd = h_iface->bss[0]; + + if (!hostapd_is_ml_partner(h_hapd, iface->bss[0])) + continue; + + hostapd_disable_iface_bss(iface); + } + + /* Then, fully disable interfaces */ + for (i = 0; i < iface->interfaces->count; ++i) { + struct hostapd_iface *h_iface = iface->interfaces->iface[i]; + struct hostapd_data *h_hapd = h_iface->bss[0]; + + if (!hostapd_is_ml_partner(h_hapd, iface->bss[0])) + continue; + + if (hostapd_disable_iface(h_iface)) { + wpa_printf(MSG_ERROR, "Disabling AP MLD failed"); + return -1; + } + } + + return 0; +} + + +#ifdef CONFIG_TESTING_OPTIONS +static int hostapd_ctrl_iface_link_remove(struct hostapd_data *hapd, char *cmd, + char *buf, size_t buflen) +{ + int ret; + u32 count = atoi(cmd); + + if (!count) + count = 1; + + ret = hostapd_link_remove(hapd, count); + if (ret == 0) { + ret = os_snprintf(buf, buflen, "%s\n", "OK"); + if (os_snprintf_error(buflen, ret)) + ret = -1; + else + ret = 0; + } + + return ret; +} +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_IEEE80211BE */ + + +#ifdef CONFIG_NAN_USD + +static int hostapd_ctrl_nan_publish(struct hostapd_data *hapd, char *cmd, + char *buf, size_t buflen) +{ + char *token, *context = NULL; + int publish_id; + struct nan_publish_params params; + const char *service_name = NULL; + struct wpabuf *ssi = NULL; + int ret = -1; + enum nan_service_protocol_type srv_proto_type = 0; + + os_memset(¶ms, 0, sizeof(params)); + /* USD shall use both solicited and unsolicited transmissions */ + params.unsolicited = true; + params.solicited = true; + /* USD shall require FSD without GAS */ + params.fsd = true; + + while ((token = str_token(cmd, " ", &context))) { + if (os_strncmp(token, "service_name=", 13) == 0) { + service_name = token + 13; + continue; + } + + if (os_strncmp(token, "ttl=", 4) == 0) { + params.ttl = atoi(token + 4); + continue; + } + + if (os_strncmp(token, "srv_proto_type=", 15) == 0) { + srv_proto_type = atoi(token + 15); + continue; + } + + if (os_strncmp(token, "ssi=", 4) == 0) { + if (ssi) + goto fail; + ssi = wpabuf_parse_bin(token + 4); + if (!ssi) + goto fail; + continue; + } + + if (os_strcmp(token, "solicited=0") == 0) { + params.solicited = false; + continue; + } + + if (os_strcmp(token, "unsolicited=0") == 0) { + params.unsolicited = false; + continue; + } + + if (os_strcmp(token, "fsd=0") == 0) { + params.fsd = false; + continue; + } + + wpa_printf(MSG_INFO, "CTRL: Invalid NAN_PUBLISH parameter: %s", + token); + goto fail; + } + + publish_id = hostapd_nan_usd_publish(hapd, service_name, srv_proto_type, + ssi, ¶ms); + if (publish_id > 0) + ret = os_snprintf(buf, buflen, "%d", publish_id); +fail: + wpabuf_free(ssi); + return ret; +} + + +static int hostapd_ctrl_nan_cancel_publish(struct hostapd_data *hapd, + char *cmd) +{ + char *token, *context = NULL; + int publish_id = 0; + + while ((token = str_token(cmd, " ", &context))) { + if (sscanf(token, "publish_id=%i", &publish_id) == 1) + continue; + wpa_printf(MSG_INFO, + "CTRL: Invalid NAN_CANCEL_PUBLISH parameter: %s", + token); + return -1; + } + + if (publish_id <= 0) { + wpa_printf(MSG_INFO, + "CTRL: Invalid or missing NAN_CANCEL_PUBLISH publish_id"); + return -1; + } + + hostapd_nan_usd_cancel_publish(hapd, publish_id); + return 0; +} + + +static int hostapd_ctrl_nan_update_publish(struct hostapd_data *hapd, + char *cmd) +{ + char *token, *context = NULL; + int publish_id = 0; + struct wpabuf *ssi = NULL; + int ret = -1; + + while ((token = str_token(cmd, " ", &context))) { + if (sscanf(token, "publish_id=%i", &publish_id) == 1) + continue; + if (os_strncmp(token, "ssi=", 4) == 0) { + if (ssi) + goto fail; + ssi = wpabuf_parse_bin(token + 4); + if (!ssi) + goto fail; + continue; + } + wpa_printf(MSG_INFO, + "CTRL: Invalid NAN_UPDATE_PUBLISH parameter: %s", + token); + goto fail; + } + + if (publish_id <= 0) { + wpa_printf(MSG_INFO, + "CTRL: Invalid or missing NAN_UPDATE_PUBLISH publish_id"); + goto fail; + } + + ret = hostapd_nan_usd_update_publish(hapd, publish_id, ssi); +fail: + wpabuf_free(ssi); + return ret; +} + + +static int hostapd_ctrl_nan_subscribe(struct hostapd_data *hapd, char *cmd, + char *buf, size_t buflen) +{ + char *token, *context = NULL; + int subscribe_id; + struct nan_subscribe_params params; + const char *service_name = NULL; + struct wpabuf *ssi = NULL; + int ret = -1; + enum nan_service_protocol_type srv_proto_type = 0; + + os_memset(¶ms, 0, sizeof(params)); + + while ((token = str_token(cmd, " ", &context))) { + if (os_strncmp(token, "service_name=", 13) == 0) { + service_name = token + 13; + continue; + } + + if (os_strcmp(token, "active=1") == 0) { + params.active = true; + continue; + } + + if (os_strncmp(token, "ttl=", 4) == 0) { + params.ttl = atoi(token + 4); + continue; + } + + if (os_strncmp(token, "srv_proto_type=", 15) == 0) { + srv_proto_type = atoi(token + 15); + continue; + } + + if (os_strncmp(token, "ssi=", 4) == 0) { + if (ssi) + goto fail; + ssi = wpabuf_parse_bin(token + 4); + if (!ssi) + goto fail; + continue; + } + + wpa_printf(MSG_INFO, + "CTRL: Invalid NAN_SUBSCRIBE parameter: %s", + token); + goto fail; + } + + subscribe_id = hostapd_nan_usd_subscribe(hapd, service_name, + srv_proto_type, ssi, + ¶ms); + if (subscribe_id > 0) + ret = os_snprintf(buf, buflen, "%d", subscribe_id); +fail: + wpabuf_free(ssi); + return ret; +} + + +static int hostapd_ctrl_nan_cancel_subscribe(struct hostapd_data *hapd, + char *cmd) +{ + char *token, *context = NULL; + int subscribe_id = 0; + + while ((token = str_token(cmd, " ", &context))) { + if (sscanf(token, "subscribe_id=%i", &subscribe_id) == 1) + continue; + wpa_printf(MSG_INFO, + "CTRL: Invalid NAN_CANCEL_SUBSCRIBE parameter: %s", + token); + return -1; + } + + if (subscribe_id <= 0) { + wpa_printf(MSG_INFO, + "CTRL: Invalid or missing NAN_CANCEL_SUBSCRIBE subscribe_id"); + return -1; + } + + hostapd_nan_usd_cancel_subscribe(hapd, subscribe_id); + return 0; +} + + +static int hostapd_ctrl_nan_transmit(struct hostapd_data *hapd, char *cmd) +{ + char *token, *context = NULL; + int handle = 0; + int req_instance_id = 0; + struct wpabuf *ssi = NULL; + u8 peer_addr[ETH_ALEN]; + int ret = -1; + + os_memset(peer_addr, 0, ETH_ALEN); + + while ((token = str_token(cmd, " ", &context))) { + if (sscanf(token, "handle=%i", &handle) == 1) + continue; + + if (sscanf(token, "req_instance_id=%i", &req_instance_id) == 1) + continue; + + if (os_strncmp(token, "address=", 8) == 0) { + if (hwaddr_aton(token + 8, peer_addr) < 0) + return -1; + continue; + } + + if (os_strncmp(token, "ssi=", 4) == 0) { + if (ssi) + goto fail; + ssi = wpabuf_parse_bin(token + 4); + if (!ssi) + goto fail; + continue; + } + + wpa_printf(MSG_INFO, + "CTRL: Invalid NAN_TRANSMIT parameter: %s", + token); + goto fail; + } + + if (handle <= 0) { + wpa_printf(MSG_INFO, + "CTRL: Invalid or missing NAN_TRANSMIT handle"); + goto fail; + } + + if (is_zero_ether_addr(peer_addr)) { + wpa_printf(MSG_INFO, + "CTRL: Invalid or missing NAN_TRANSMIT address"); + goto fail; + } + + ret = hostapd_nan_usd_transmit(hapd, handle, ssi, NULL, peer_addr, + req_instance_id); +fail: + wpabuf_free(ssi); + return ret; +} + +#endif /* CONFIG_NAN_USD */ + + +static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, + char *buf, char *reply, + int reply_size, + struct sockaddr_storage *from, + socklen_t fromlen) +{ + int reply_len, res; + + os_memcpy(reply, "OK\n", 3); + reply_len = 3; + + if (os_strcmp(buf, "PING") == 0) { + os_memcpy(reply, "PONG\n", 5); + reply_len = 5; + } else if (os_strncmp(buf, "RELOG", 5) == 0) { + if (wpa_debug_reopen_file() < 0) + reply_len = -1; + } else if (os_strcmp(buf, "CLOSE_LOG") == 0) { + wpa_debug_stop_log(); + } else if (os_strncmp(buf, "NOTE ", 5) == 0) { + wpa_printf(MSG_INFO, "NOTE: %s", buf + 5); + } else if (os_strcmp(buf, "STATUS") == 0) { + reply_len = hostapd_ctrl_iface_status(hapd, reply, + reply_size); + } else if (os_strcmp(buf, "STATUS-DRIVER") == 0) { + reply_len = hostapd_drv_status(hapd, reply, reply_size); + } else if (os_strcmp(buf, "MIB") == 0) { + reply_len = ieee802_11_get_mib(hapd, reply, reply_size); + if (reply_len >= 0) { + res = wpa_get_mib(hapd->wpa_auth, reply + reply_len, + reply_size - reply_len); + if (res < 0) + reply_len = -1; + else + reply_len += res; + } + if (reply_len >= 0) { + res = ieee802_1x_get_mib(hapd, reply + reply_len, + reply_size - reply_len); + if (res < 0) + reply_len = -1; + else + reply_len += res; + } +#ifndef CONFIG_NO_RADIUS + if (reply_len >= 0) { + res = radius_client_get_mib(hapd->radius, + reply + reply_len, + reply_size - reply_len); + if (res < 0) + reply_len = -1; + else + reply_len += res; + } +#endif /* CONFIG_NO_RADIUS */ + } else if (os_strncmp(buf, "MIB ", 4) == 0) { + reply_len = hostapd_ctrl_iface_mib(hapd, reply, reply_size, + buf + 4); + } else if (os_strcmp(buf, "STA-FIRST") == 0) { + reply_len = hostapd_ctrl_iface_sta_first(hapd, reply, + reply_size); + } else if (os_strncmp(buf, "STA ", 4) == 0) { + reply_len = hostapd_ctrl_iface_sta(hapd, buf + 4, reply, + reply_size); + } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) { + reply_len = hostapd_ctrl_iface_sta_next(hapd, buf + 9, reply, + reply_size); + } else if (os_strcmp(buf, "ATTACH") == 0) { + if (hostapd_ctrl_iface_attach(hapd, from, fromlen, NULL)) + reply_len = -1; + } else if (os_strncmp(buf, "ATTACH ", 7) == 0) { + if (hostapd_ctrl_iface_attach(hapd, from, fromlen, buf + 7)) + reply_len = -1; + } else if (os_strcmp(buf, "DETACH") == 0) { + if (hostapd_ctrl_iface_detach(hapd, from, fromlen)) + reply_len = -1; + } else if (os_strncmp(buf, "LEVEL ", 6) == 0) { + if (hostapd_ctrl_iface_level(hapd, from, fromlen, + buf + 6)) + reply_len = -1; + } else if (os_strncmp(buf, "NEW_STA ", 8) == 0) { + if (hostapd_ctrl_iface_new_sta(hapd, buf + 8)) + reply_len = -1; + } else if (os_strncmp(buf, "DEAUTHENTICATE ", 15) == 0) { + if (hostapd_ctrl_iface_deauthenticate(hapd, buf + 15)) + reply_len = -1; + } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) { + if (hostapd_ctrl_iface_disassociate(hapd, buf + 13)) + reply_len = -1; +#ifdef CONFIG_TAXONOMY + } else if (os_strncmp(buf, "SIGNATURE ", 10) == 0) { + reply_len = hostapd_ctrl_iface_signature(hapd, buf + 10, + reply, reply_size); +#endif /* CONFIG_TAXONOMY */ + } else if (os_strncmp(buf, "POLL_STA ", 9) == 0) { + if (hostapd_ctrl_iface_poll_sta(hapd, buf + 9)) + reply_len = -1; + } else if (os_strcmp(buf, "STOP_AP") == 0) { + if (hostapd_ctrl_iface_stop_ap(hapd)) + reply_len = -1; +#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 */ +#ifdef CONFIG_WPS + } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) { + if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8)) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) { + reply_len = hostapd_ctrl_iface_wps_check_pin( + hapd, buf + 14, reply, reply_size); + } else if (os_strcmp(buf, "WPS_PBC") == 0) { + if (hostapd_wps_button_pushed(hapd, NULL)) + reply_len = -1; + } else if (os_strcmp(buf, "WPS_CANCEL") == 0) { + if (hostapd_wps_cancel(hapd)) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) { + reply_len = hostapd_ctrl_iface_wps_ap_pin(hapd, buf + 11, + reply, reply_size); + } else if (os_strncmp(buf, "WPS_CONFIG ", 11) == 0) { + if (hostapd_ctrl_iface_wps_config(hapd, buf + 11) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_GET_STATUS", 13) == 0) { + reply_len = hostapd_ctrl_iface_wps_get_status(hapd, reply, + reply_size); +#ifdef CONFIG_WPS_NFC + } else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) { + if (hostapd_ctrl_iface_wps_nfc_tag_read(hapd, buf + 17)) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_NFC_CONFIG_TOKEN ", 21) == 0) { + reply_len = hostapd_ctrl_iface_wps_nfc_config_token( + hapd, buf + 21, reply, reply_size); + } else if (os_strncmp(buf, "WPS_NFC_TOKEN ", 14) == 0) { + reply_len = hostapd_ctrl_iface_wps_nfc_token( + hapd, buf + 14, reply, reply_size); + } else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) { + reply_len = hostapd_ctrl_iface_nfc_get_handover_sel( + hapd, buf + 21, reply, reply_size); + } else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) { + if (hostapd_ctrl_iface_nfc_report_handover(hapd, buf + 20)) + reply_len = -1; +#endif /* CONFIG_WPS_NFC */ +#endif /* CONFIG_WPS */ +#ifdef CONFIG_INTERWORKING + } else if (os_strncmp(buf, "SET_QOS_MAP_SET ", 16) == 0) { + if (hostapd_ctrl_iface_set_qos_map_set(hapd, buf + 16)) + reply_len = -1; + } else if (os_strncmp(buf, "SEND_QOS_MAP_CONF ", 18) == 0) { + if (hostapd_ctrl_iface_send_qos_map_conf(hapd, buf + 18)) + reply_len = -1; +#endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_HS20 + } else if (os_strncmp(buf, "HS20_WNM_NOTIF ", 15) == 0) { + if (hostapd_ctrl_iface_hs20_wnm_notif(hapd, buf + 15)) + reply_len = -1; + } else if (os_strncmp(buf, "HS20_DEAUTH_REQ ", 16) == 0) { + if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16)) + reply_len = -1; +#endif /* CONFIG_HS20 */ +#ifdef CONFIG_WNM_AP + } else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) { + if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18)) + reply_len = -1; + } else if (os_strncmp(buf, "ESS_DISASSOC ", 13) == 0) { + if (hostapd_ctrl_iface_ess_disassoc(hapd, buf + 13)) + reply_len = -1; + } else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) { + if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11)) + reply_len = -1; + } else if (os_strncmp(buf, "COLOC_INTF_REQ ", 15) == 0) { + if (hostapd_ctrl_iface_coloc_intf_req(hapd, buf + 15)) + reply_len = -1; +#endif /* CONFIG_WNM_AP */ + } else if (os_strcmp(buf, "GET_CONFIG") == 0) { + reply_len = hostapd_ctrl_iface_get_config(hapd, reply, + reply_size); + } else if (os_strncmp(buf, "SET ", 4) == 0) { + if (hostapd_ctrl_iface_set(hapd, buf + 4)) + reply_len = -1; + } else if (os_strncmp(buf, "GET ", 4) == 0) { + reply_len = hostapd_ctrl_iface_get(hapd, buf + 4, reply, + reply_size); + } else if (os_strcmp(buf, "ENABLE") == 0) { + if (hostapd_ctrl_iface_enable(hapd->iface)) + reply_len = -1; + } else if (os_strcmp(buf, "RELOAD_WPA_PSK") == 0) { + if (hostapd_ctrl_iface_reload_wpa_psk(hapd)) + reply_len = -1; +#ifdef CONFIG_IEEE80211R_AP + } else if (os_strcmp(buf, "GET_RXKHS") == 0) { + reply_len = hostapd_ctrl_iface_get_rxkhs(hapd, reply, + reply_size); + } else if (os_strcmp(buf, "RELOAD_RXKHS") == 0) { + if (hostapd_ctrl_iface_reload_rxkhs(hapd)) + reply_len = -1; +#endif /* CONFIG_IEEE80211R_AP */ + } else if (os_strcmp(buf, "RELOAD_BSS") == 0) { + if (hostapd_ctrl_iface_reload_bss(hapd)) + reply_len = -1; + } else if (os_strcmp(buf, "RELOAD_CONFIG") == 0) { + if (hostapd_reload_config(hapd->iface)) + reply_len = -1; + } else if (os_strcmp(buf, "RELOAD") == 0) { + if (hostapd_ctrl_iface_reload(hapd->iface)) + reply_len = -1; + } else if (os_strcmp(buf, "DISABLE") == 0) { + if (hostapd_ctrl_iface_disable(hapd->iface)) + reply_len = -1; + } else if (os_strcmp(buf, "UPDATE_BEACON") == 0) { + if (ieee802_11_set_beacon(hapd)) + reply_len = -1; +#ifdef CONFIG_TESTING_OPTIONS + } else if (os_strncmp(buf, "RADAR ", 6) == 0) { + if (hostapd_ctrl_iface_radar(hapd, buf + 6)) + reply_len = -1; + } else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) { + if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8)) + reply_len = -1; + } else if (os_strncmp(buf, "MGMT_TX_STATUS_PROCESS ", 23) == 0) { + if (hostapd_ctrl_iface_mgmt_tx_status_process(hapd, + buf + 23) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "MGMT_RX_PROCESS ", 16) == 0) { + if (hostapd_ctrl_iface_mgmt_rx_process(hapd, buf + 16) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) { + if (hostapd_ctrl_iface_eapol_rx(hapd, buf + 9) < 0) + reply_len = -1; + } 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; + } else if (os_strncmp(buf, "DATA_TEST_TX ", 13) == 0) { + if (hostapd_ctrl_iface_data_test_tx(hapd, buf + 13) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) { + if (hostapd_ctrl_iface_data_test_frame(hapd, buf + 16) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) { + if (testing_set_fail_pattern(true, buf + 16) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) { + reply_len = testing_get_fail_pattern(true, reply, reply_size); + } else if (os_strncmp(buf, "TEST_FAIL ", 10) == 0) { + if (testing_set_fail_pattern(false, buf + 10) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "GET_FAIL") == 0) { + reply_len = testing_get_fail_pattern(false, reply, reply_size); + } else if (os_strncmp(buf, "RESET_PN ", 9) == 0) { + if (hostapd_ctrl_reset_pn(hapd, buf + 9) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "SET_KEY ", 8) == 0) { + if (hostapd_ctrl_set_key(hapd, buf + 8) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "RESEND_M1 ", 10) == 0) { + if (hostapd_ctrl_resend_m1(hapd, buf + 10) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "RESEND_M3 ", 10) == 0) { + if (hostapd_ctrl_resend_m3(hapd, buf + 10) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "RESEND_GROUP_M1 ", 16) == 0) { + if (hostapd_ctrl_resend_group_m1(hapd, buf + 16) < 0) + reply_len = -1; + } else if (os_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)) + reply_len = -1; +#ifdef CONFIG_IEEE80211AX + } else if (os_strncmp(buf, "COLOR_CHANGE ", 13) == 0) { + if (hostapd_ctrl_iface_color_change(hapd->iface, buf + 13)) + reply_len = -1; +#endif /* CONFIG_IEEE80211AX */ + } else if (os_strncmp(buf, "NOTIFY_CW_CHANGE ", 17) == 0) { + if (hostapd_ctrl_iface_notify_cw_change(hapd, buf + 17)) + reply_len = -1; + } else if (os_strncmp(buf, "VENDOR ", 7) == 0) { + reply_len = hostapd_ctrl_iface_vendor(hapd, buf + 7, reply, + reply_size); + } else if (os_strcmp(buf, "ERP_FLUSH") == 0) { + ieee802_1x_erp_flush(hapd); +#ifdef RADIUS_SERVER + radius_server_erp_flush(hapd->radius_srv); +#endif /* RADIUS_SERVER */ + } else if (os_strncmp(buf, "EAPOL_REAUTH ", 13) == 0) { + if (hostapd_ctrl_iface_eapol_reauth(hapd, buf + 13)) + reply_len = -1; + } else if (os_strncmp(buf, "EAPOL_SET ", 10) == 0) { + if (hostapd_ctrl_iface_eapol_set(hapd, buf + 10)) + reply_len = -1; + } else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) { + reply_len = hostapd_ctrl_iface_log_level( + hapd, buf + 9, reply, reply_size); +#ifdef NEED_AP_MLME + } else if (os_strcmp(buf, "TRACK_STA_LIST") == 0) { + reply_len = hostapd_ctrl_iface_track_sta_list( + hapd, reply, reply_size); +#endif /* NEED_AP_MLME */ + } else if (os_strcmp(buf, "PMKSA") == 0) { + reply_len = hostapd_ctrl_iface_pmksa_list(hapd, reply, + reply_size); + } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) { + hostapd_ctrl_iface_pmksa_flush(hapd); + } else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) { + if (hostapd_ctrl_iface_pmksa_add(hapd, buf + 10) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "SET_NEIGHBOR ", 13) == 0) { + if (hostapd_ctrl_iface_set_neighbor(hapd, buf + 13)) + reply_len = -1; + } 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; + } else if (os_strncmp(buf, "REQ_LCI ", 8) == 0) { + if (hostapd_ctrl_iface_req_lci(hapd, buf + 8)) + reply_len = -1; + } else if (os_strncmp(buf, "REQ_RANGE ", 10) == 0) { + if (hostapd_ctrl_iface_req_range(hapd, buf + 10)) + reply_len = -1; + } else if (os_strncmp(buf, "REQ_BEACON ", 11) == 0) { + reply_len = hostapd_ctrl_iface_req_beacon(hapd, buf + 11, + reply, reply_size); + } else if (os_strncmp(buf, "REQ_LINK_MEASUREMENT ", 21) == 0) { + reply_len = hostapd_ctrl_iface_req_link_measurement( + hapd, buf + 21, reply, reply_size); + } else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) { + reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply, + reply_size); + } else if (os_strcmp(buf, "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( + &hapd->conf->accept_mac, + &hapd->conf->num_accept_mac, buf + 19) || + hostapd_set_acl(hapd)) + 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_set_acl(hapd) || + hostapd_disassoc_accept_mac(hapd)) + reply_len = -1; + } else if (os_strcmp(buf + 11, "SHOW") == 0) { + reply_len = hostapd_ctrl_iface_acl_show_mac( + hapd->conf->accept_mac, + hapd->conf->num_accept_mac, reply, reply_size); + } else if (os_strcmp(buf + 11, "CLEAR") == 0) { + hostapd_ctrl_iface_acl_clear_list( + &hapd->conf->accept_mac, + &hapd->conf->num_accept_mac); + if (hostapd_set_acl(hapd) || + hostapd_disassoc_accept_mac(hapd)) + reply_len = -1; + } else { + reply_len = -1; + } + } else if (os_strncmp(buf, "DENY_ACL ", 9) == 0) { + if (os_strncmp(buf + 9, "ADD_MAC ", 8) == 0) { + if (hostapd_ctrl_iface_acl_add_mac( + &hapd->conf->deny_mac, + &hapd->conf->num_deny_mac, buf + 17) || + hostapd_set_acl(hapd) || + hostapd_disassoc_deny_mac(hapd)) + reply_len = -1; + } else if (os_strncmp(buf + 9, "DEL_MAC ", 8) == 0) { + if (hostapd_ctrl_iface_acl_del_mac( + &hapd->conf->deny_mac, + &hapd->conf->num_deny_mac, buf + 17) || + hostapd_set_acl(hapd)) + reply_len = -1; + } else if (os_strcmp(buf + 9, "SHOW") == 0) { + reply_len = hostapd_ctrl_iface_acl_show_mac( + hapd->conf->deny_mac, + hapd->conf->num_deny_mac, reply, reply_size); + } else if (os_strcmp(buf + 9, "CLEAR") == 0) { + hostapd_ctrl_iface_acl_clear_list( + &hapd->conf->deny_mac, + &hapd->conf->num_deny_mac); + if (hostapd_set_acl(hapd)) + reply_len = -1; + } else { + reply_len = -1; + } +#ifdef CONFIG_DPP + } else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) { + res = hostapd_dpp_qr_code(hapd, buf + 12); + if (res < 0) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%d", res); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_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) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%d", res); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) { + if (dpp_bootstrap_remove(hapd->iface->interfaces->dpp, + buf + 21) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) { + const char *uri; + + uri = dpp_bootstrap_get_uri(hapd->iface->interfaces->dpp, + atoi(buf + 22)); + if (!uri) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%s", uri); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_BOOTSTRAP_INFO ", 19) == 0) { + reply_len = 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; + } else if (os_strncmp(buf, "DPP_LISTEN ", 11) == 0) { + if (hostapd_dpp_listen(hapd, buf + 11) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "DPP_STOP_LISTEN") == 0) { + hostapd_dpp_stop(hapd); + hostapd_dpp_listen_stop(hapd); + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_ADD", 20) == 0) { + res = dpp_configurator_add(hapd->iface->interfaces->dpp, + buf + 20); + if (res < 0) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%d", res); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_SET ", 21) == 0) { + if (dpp_configurator_set(hapd->iface->interfaces->dpp, + buf + 20) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_REMOVE ", 24) == 0) { + if (dpp_configurator_remove(hapd->iface->interfaces->dpp, + buf + 24) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_SIGN ", 22) == 0) { + if (hostapd_dpp_configurator_sign(hapd, buf + 21) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_CONFIGURATOR_GET_KEY ", 25) == 0) { + reply_len = dpp_configurator_get_key_id( + hapd->iface->interfaces->dpp, + atoi(buf + 25), + reply, reply_size); + } else if (os_strncmp(buf, "DPP_PKEX_ADD ", 13) == 0) { + res = hostapd_dpp_pkex_add(hapd, buf + 12); + if (res < 0) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%d", res); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_PKEX_REMOVE ", 16) == 0) { + if (hostapd_dpp_pkex_remove(hapd, buf + 16) < 0) + reply_len = -1; +#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); + } else if (os_strncmp(buf, "DPP_RELAY_ADD_CONTROLLER ", 25) == 0) { + if (hostapd_dpp_add_controller(hapd, buf + 25) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_RELAY_REMOVE_CONTROLLER ", 28) == 0) { + hostapd_dpp_remove_controller(hapd, buf + 28); +#endif /* CONFIG_DPP2 */ +#ifdef CONFIG_DPP3 + } else if (os_strcmp(buf, "DPP_PUSH_BUTTON") == 0) { + if (hostapd_dpp_push_button(hapd, NULL) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_PUSH_BUTTON ", 16) == 0) { + if (hostapd_dpp_push_button(hapd, buf + 15) < 0) + reply_len = -1; +#endif /* CONFIG_DPP3 */ +#endif /* CONFIG_DPP */ +#ifdef CONFIG_NAN_USD + } else if (os_strncmp(buf, "NAN_PUBLISH ", 12) == 0) { + reply_len = hostapd_ctrl_nan_publish(hapd, buf + 12, reply, + reply_size); + } else if (os_strncmp(buf, "NAN_CANCEL_PUBLISH ", 19) == 0) { + if (hostapd_ctrl_nan_cancel_publish(hapd, buf + 19) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "NAN_UPDATE_PUBLISH ", 19) == 0) { + if (hostapd_ctrl_nan_update_publish(hapd, buf + 19) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "NAN_SUBSCRIBE ", 14) == 0) { + reply_len = hostapd_ctrl_nan_subscribe(hapd, buf + 14, reply, + reply_size); + } else if (os_strncmp(buf, "NAN_CANCEL_SUBSCRIBE ", 21) == 0) { + if (hostapd_ctrl_nan_cancel_subscribe(hapd, buf + 21) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "NAN_TRANSMIT ", 13) == 0) { + if (hostapd_ctrl_nan_transmit(hapd, buf + 13) < 0) + reply_len = -1; +#endif /* CONFIG_NAN_USD */ +#ifdef RADIUS_SERVER + } else if (os_strncmp(buf, "DAC_REQUEST ", 12) == 0) { + if (radius_server_dac_request(hapd->radius_srv, buf + 12) < 0) + reply_len = -1; +#endif /* RADIUS_SERVER */ + } else 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 */ +#ifdef CONFIG_IEEE80211BE + } else if (os_strcmp(buf, "ENABLE_MLD") == 0) { + if (hostapd_ctrl_iface_enable_mld(hapd->iface)) + reply_len = -1; + } else if (os_strcmp(buf, "DISABLE_MLD") == 0) { + if (hostapd_ctrl_iface_disable_mld(hapd->iface)) + reply_len = -1; +#ifdef CONFIG_TESTING_OPTIONS + } else if (os_strncmp(buf, "LINK_REMOVE ", 12) == 0) { + if (hostapd_ctrl_iface_link_remove(hapd, buf + 12, + reply, reply_size)) + reply_len = -1; +#endif /* CONFIG_TESTING_OPTIONS */ +#endif /* CONFIG_IEEE80211BE */ + } else { + os_memcpy(reply, "UNKNOWN COMMAND\n", 16); + reply_len = 16; + } + + if (reply_len < 0) { + os_memcpy(reply, "FAIL\n", 5); + reply_len = 5; + } + + return reply_len; +} + + +static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + char buf[4096]; + int res; + struct sockaddr_storage from; + socklen_t fromlen = sizeof(from); + char *reply, *pos = buf; + const int reply_size = 4096; + int reply_len; + int level = MSG_DEBUG; +#ifdef CONFIG_CTRL_IFACE_UDP + unsigned char lcookie[CTRL_IFACE_COOKIE_LEN]; +#endif /* CONFIG_CTRL_IFACE_UDP */ + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", + strerror(errno)); + return; + } + buf[res] = '\0'; + + reply = os_malloc(reply_size); + if (reply == NULL) { + if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", + strerror(errno)); + } + return; + } + +#ifdef CONFIG_CTRL_IFACE_UDP + if (os_strcmp(buf, "GET_COOKIE") == 0) { + os_memcpy(reply, "COOKIE=", 7); + 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, CTRL_IFACE_COOKIE_LEN) < 0) { + wpa_printf(MSG_DEBUG, + "CTRL: No cookie in the request - drop request"); + os_free(reply); + return; + } + + 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 * CTRL_IFACE_COOKIE_LEN; + while (*pos == ' ') + pos++; +#endif /* CONFIG_CTRL_IFACE_UDP */ + + if (os_strcmp(pos, "PING") == 0) + level = MSG_EXCESSIVE; + wpa_hexdump_ascii(level, "RX ctrl_iface", pos, res); + + reply_len = hostapd_ctrl_iface_receive_process(hapd, pos, + reply, reply_size, + &from, fromlen); + +#ifdef CONFIG_CTRL_IFACE_UDP +done: +#endif /* CONFIG_CTRL_IFACE_UDP */ + if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", + strerror(errno)); + } + os_free(reply); +} + + +#ifndef CONFIG_CTRL_IFACE_UDP +static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd) +{ + char *buf; + size_t len; + + if (hapd->conf->ctrl_interface == NULL) + return NULL; + + len = os_strlen(hapd->conf->ctrl_interface) + + os_strlen(hapd->conf->iface) + 2; + buf = os_malloc(len); + if (buf == NULL) + return NULL; + + os_snprintf(buf, len, "%s/%s", + hapd->conf->ctrl_interface, hapd->conf->iface); + buf[len - 1] = '\0'; + return buf; +} +#endif /* CONFIG_CTRL_IFACE_UDP */ + + +static void hostapd_ctrl_iface_msg_cb(void *ctx, int level, + enum wpa_msg_type type, + const char *txt, size_t len) +{ + struct hostapd_data *hapd = ctx; + if (hapd == NULL) + return; + hostapd_ctrl_iface_send(hapd, level, type, txt, len); +} + + +int hostapd_ctrl_iface_init(struct hostapd_data *hapd) +{ +#ifdef CONFIG_CTRL_IFACE_UDP + int port = HOSTAPD_CTRL_IFACE_PORT; + char p[32] = { 0 }; + char port_str[40], *tmp; + char *pos; + struct addrinfo hints = { 0 }, *res, *saveres; + int n; + + if (hapd->ctrl_sock > -1) { + wpa_printf(MSG_DEBUG, "ctrl_iface already exists!"); + return 0; + } + + if (hapd->conf->ctrl_interface == NULL) + return 0; + + pos = os_strstr(hapd->conf->ctrl_interface, "udp:"); + if (pos) { + pos += 4; + port = atoi(pos); + if (port <= 0) { + wpa_printf(MSG_ERROR, "Invalid ctrl_iface UDP port"); + goto fail; + } + } + + dl_list_init(&hapd->ctrl_dst); + hapd->ctrl_sock = -1; + os_get_random(hapd->ctrl_iface_cookie, CTRL_IFACE_COOKIE_LEN); + +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + hints.ai_flags = AI_PASSIVE; +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + hints.ai_family = AF_INET6; +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + hints.ai_family = AF_INET; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + hints.ai_socktype = SOCK_DGRAM; + +try_again: + os_snprintf(p, sizeof(p), "%d", port); + n = getaddrinfo(NULL, p, &hints, &res); + if (n) { + wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n)); + goto fail; + } + + saveres = res; + hapd->ctrl_sock = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + if (hapd->ctrl_sock < 0) { + wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); + goto fail; + } + + if (bind(hapd->ctrl_sock, res->ai_addr, res->ai_addrlen) < 0) { + port--; + if ((HOSTAPD_CTRL_IFACE_PORT - port) < + HOSTAPD_CTRL_IFACE_PORT_LIMIT && !pos) + goto try_again; + wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno)); + goto fail; + } + + freeaddrinfo(saveres); + + os_snprintf(port_str, sizeof(port_str), "udp:%d", port); + tmp = os_strdup(port_str); + if (tmp) { + os_free(hapd->conf->ctrl_interface); + hapd->conf->ctrl_interface = tmp; + } + wpa_printf(MSG_DEBUG, "ctrl_iface_init UDP port: %d", port); + + if (eloop_register_read_sock(hapd->ctrl_sock, + hostapd_ctrl_iface_receive, hapd, NULL) < + 0) { + hostapd_ctrl_iface_deinit(hapd); + return -1; + } + + hapd->msg_ctx = hapd; + wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb); + + return 0; + +fail: + if (hapd->ctrl_sock >= 0) + close(hapd->ctrl_sock); + return -1; +#else /* CONFIG_CTRL_IFACE_UDP */ + struct sockaddr_un addr; + int s = -1; + char *fname = NULL; + + if (hapd->ctrl_sock > -1) { + wpa_printf(MSG_DEBUG, "ctrl_iface already exists!"); + return 0; + } + + dl_list_init(&hapd->ctrl_dst); + + if (hapd->conf->ctrl_interface == NULL) + return 0; + + if (mkdir(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) { + if (errno == EEXIST) { + wpa_printf(MSG_DEBUG, "Using existing control " + "interface directory."); + } else { + wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s", + strerror(errno)); + goto fail; + } + } + + if (hapd->conf->ctrl_interface_gid_set && + lchown(hapd->conf->ctrl_interface, -1, + hapd->conf->ctrl_interface_gid) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", + strerror(errno)); + return -1; + } + + if (!hapd->conf->ctrl_interface_gid_set && + hapd->iface->interfaces->ctrl_iface_group && + lchown(hapd->conf->ctrl_interface, -1, + hapd->iface->interfaces->ctrl_iface_group) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", + strerror(errno)); + return -1; + } + +#ifdef ANDROID + /* + * Android is using umask 0077 which would leave the control interface + * directory without group access. This breaks things since Wi-Fi + * framework assumes that this directory can be accessed by other + * applications in the wifi group. Fix this by adding group access even + * if umask value would prevent this. + */ + if (chmod(hapd->conf->ctrl_interface, S_IRWXU | S_IRWXG) < 0) { + wpa_printf(MSG_ERROR, "CTRL: Could not chmod directory: %s", + strerror(errno)); + /* Try to continue anyway */ + } +#endif /* ANDROID */ + + if (os_strlen(hapd->conf->ctrl_interface) + 1 + + os_strlen(hapd->conf->iface) >= sizeof(addr.sun_path)) + goto fail; + + s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno)); + goto fail; + } + + os_memset(&addr, 0, sizeof(addr)); +#ifdef __FreeBSD__ + addr.sun_len = sizeof(addr); +#endif /* __FreeBSD__ */ + addr.sun_family = AF_UNIX; + fname = hostapd_ctrl_iface_path(hapd); + if (fname == NULL) + goto fail; + os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path)); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s", + strerror(errno)); + if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" + " allow connections - assuming it was left" + "over from forced program termination"); + if (unlink(fname) < 0) { + wpa_printf(MSG_ERROR, + "Could not unlink existing ctrl_iface socket '%s': %s", + fname, strerror(errno)); + goto fail; + } + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < + 0) { + wpa_printf(MSG_ERROR, + "hostapd-ctrl-iface: bind(PF_UNIX): %s", + strerror(errno)); + goto fail; + } + wpa_printf(MSG_DEBUG, "Successfully replaced leftover " + "ctrl_iface socket '%s'", fname); + } else { + wpa_printf(MSG_INFO, "ctrl_iface exists and seems to " + "be in use - cannot override it"); + wpa_printf(MSG_INFO, "Delete '%s' manually if it is " + "not used anymore", fname); + os_free(fname); + fname = NULL; + goto fail; + } + } + + if (hapd->conf->ctrl_interface_gid_set && + lchown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface/ifname]: %s", + strerror(errno)); + goto fail; + } + + if (!hapd->conf->ctrl_interface_gid_set && + hapd->iface->interfaces->ctrl_iface_group && + lchown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface/ifname]: %s", + strerror(errno)); + goto fail; + } + + if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { + wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s", + strerror(errno)); + goto fail; + } + os_free(fname); + + hapd->ctrl_sock = s; + if (eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd, + NULL) < 0) { + hostapd_ctrl_iface_deinit(hapd); + return -1; + } + hapd->msg_ctx = hapd; + wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb); + + return 0; + +fail: + if (s >= 0) + close(s); + if (fname) { + unlink(fname); + os_free(fname); + } + return -1; +#endif /* CONFIG_CTRL_IFACE_UDP */ +} + + +void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) +{ + struct wpa_ctrl_dst *dst, *prev; + + if (hapd->ctrl_sock > -1) { +#ifndef CONFIG_CTRL_IFACE_UDP + char *fname; +#endif /* !CONFIG_CTRL_IFACE_UDP */ + + eloop_unregister_read_sock(hapd->ctrl_sock); + close(hapd->ctrl_sock); + hapd->ctrl_sock = -1; +#ifndef CONFIG_CTRL_IFACE_UDP + fname = hostapd_ctrl_iface_path(hapd); + if (fname) + unlink(fname); + os_free(fname); + + if (hapd->conf->ctrl_interface && + rmdir(hapd->conf->ctrl_interface) < 0) { + if (errno == ENOTEMPTY) { + wpa_printf(MSG_DEBUG, "Control interface " + "directory not empty - leaving it " + "behind"); + } else { + wpa_printf(MSG_ERROR, + "rmdir[ctrl_interface=%s]: %s", + hapd->conf->ctrl_interface, + strerror(errno)); + } + } +#endif /* !CONFIG_CTRL_IFACE_UDP */ + } + + dl_list_for_each_safe(dst, prev, &hapd->ctrl_dst, struct wpa_ctrl_dst, + list) + os_free(dst); + +#ifdef CONFIG_TESTING_OPTIONS + l2_packet_deinit(hapd->l2_test); + hapd->l2_test = NULL; +#endif /* CONFIG_TESTING_OPTIONS */ +} + + +static int hostapd_ctrl_iface_add(struct hapd_interfaces *interfaces, + char *buf) +{ + if (hostapd_add_iface(interfaces, buf) < 0) { + wpa_printf(MSG_ERROR, "Adding interface %s failed", buf); + return -1; + } + return 0; +} + + +static int hostapd_ctrl_iface_remove(struct hapd_interfaces *interfaces, + char *buf) +{ + if (hostapd_remove_iface(interfaces, buf) < 0) { + wpa_printf(MSG_ERROR, "Removing interface %s failed", buf); + return -1; + } + return 0; +} + + +static int hostapd_global_ctrl_iface_attach(struct hapd_interfaces *interfaces, + struct sockaddr_storage *from, + socklen_t fromlen, char *input) +{ + return ctrl_iface_attach(&interfaces->global_ctrl_dst, from, fromlen, + input); +} + + +static int hostapd_global_ctrl_iface_detach(struct hapd_interfaces *interfaces, + struct sockaddr_storage *from, + socklen_t fromlen) +{ + return ctrl_iface_detach(&interfaces->global_ctrl_dst, from, fromlen); +} + + +static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces) +{ +#ifdef CONFIG_WPS_TESTING + wps_version_number = 0x20; + wps_testing_stub_cred = 0; + wps_corrupt_pkhash = 0; +#endif /* CONFIG_WPS_TESTING */ + +#ifdef CONFIG_TESTING_OPTIONS +#ifdef CONFIG_DPP + dpp_test = DPP_TEST_DISABLED; +#ifdef CONFIG_DPP3 + dpp_version_override = 3; +#elif defined(CONFIG_DPP2) + dpp_version_override = 2; +#else /* CONFIG_DPP2 */ + dpp_version_override = 1; +#endif /* CONFIG_DPP2 */ +#endif /* CONFIG_DPP */ +#endif /* CONFIG_TESTING_OPTIONS */ + +#ifdef CONFIG_DPP + dpp_global_clear(interfaces->dpp); +#ifdef CONFIG_DPP3 + interfaces->dpp_pb_bi = NULL; + { + int i; + + for (i = 0; i < DPP_PB_INFO_COUNT; i++) { + struct dpp_pb_info *info; + + info = &interfaces->dpp_pb[i]; + info->rx_time.sec = 0; + info->rx_time.usec = 0; + } + } +#endif /* CONFIG_DPP3 */ +#endif /* CONFIG_DPP */ +} + + +#ifdef CONFIG_FST + +static int +hostapd_global_ctrl_iface_fst_attach(struct hapd_interfaces *interfaces, + const char *cmd) +{ + char ifname[IFNAMSIZ + 1]; + struct fst_iface_cfg cfg; + struct hostapd_data *hapd; + struct fst_wpa_obj iface_obj; + + if (!fst_parse_attach_command(cmd, ifname, sizeof(ifname), &cfg)) { + hapd = hostapd_get_iface(interfaces, ifname); + if (hapd) { + if (hapd->iface->fst) { + wpa_printf(MSG_INFO, "FST: Already attached"); + return -1; + } + fst_hostapd_fill_iface_obj(hapd, &iface_obj); + hapd->iface->fst = fst_attach(ifname, hapd->own_addr, + &iface_obj, &cfg); + if (hapd->iface->fst) + return 0; + } + } + + return -EINVAL; +} + + +static int +hostapd_global_ctrl_iface_fst_detach(struct hapd_interfaces *interfaces, + const char *cmd) +{ + char ifname[IFNAMSIZ + 1]; + struct hostapd_data * hapd; + + if (!fst_parse_detach_command(cmd, ifname, sizeof(ifname))) { + hapd = hostapd_get_iface(interfaces, ifname); + if (hapd) { + if (!fst_iface_detach(ifname)) { + hapd->iface->fst = NULL; + hapd->iface->fst_ies = NULL; + return 0; + } + } + } + + return -EINVAL; +} + +#endif /* CONFIG_FST */ + + +static struct hostapd_data * +hostapd_interfaces_get_hapd(struct hapd_interfaces *interfaces, + const char *ifname) +{ + size_t i, j; + + for (i = 0; i < interfaces->count; i++) { + struct hostapd_iface *iface = interfaces->iface[i]; + + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_data *hapd; + + hapd = iface->bss[j]; + if (os_strcmp(ifname, hapd->conf->iface) == 0) + return hapd; + } + } + + return NULL; +} + + +static int hostapd_ctrl_iface_dup_param(struct hostapd_data *src_hapd, + struct hostapd_data *dst_hapd, + const char *param) +{ + int res; + char *value; + + value = os_zalloc(HOSTAPD_CLI_DUP_VALUE_MAX_LEN); + if (!value) { + wpa_printf(MSG_ERROR, + "DUP: cannot allocate buffer to stringify %s", + param); + goto error_return; + } + + if (os_strcmp(param, "wpa") == 0) { + os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%d", + src_hapd->conf->wpa); + } else if (os_strcmp(param, "wpa_key_mgmt") == 0 && + src_hapd->conf->wpa_key_mgmt) { + res = hostapd_ctrl_iface_get_key_mgmt( + src_hapd, value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN); + if (os_snprintf_error(HOSTAPD_CLI_DUP_VALUE_MAX_LEN, res)) + goto error_stringify; + } else if (os_strcmp(param, "wpa_pairwise") == 0 && + src_hapd->conf->wpa_pairwise) { + res = wpa_write_ciphers(value, + value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN, + src_hapd->conf->wpa_pairwise, " "); + if (res < 0) + goto error_stringify; + } else if (os_strcmp(param, "rsn_pairwise") == 0 && + src_hapd->conf->rsn_pairwise) { + res = wpa_write_ciphers(value, + value + HOSTAPD_CLI_DUP_VALUE_MAX_LEN, + src_hapd->conf->rsn_pairwise, " "); + if (res < 0) + goto error_stringify; + } else if (os_strcmp(param, "wpa_passphrase") == 0 && + src_hapd->conf->ssid.wpa_passphrase) { + os_snprintf(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, "%s", + src_hapd->conf->ssid.wpa_passphrase); + } else if (os_strcmp(param, "wpa_psk") == 0 && + src_hapd->conf->ssid.wpa_psk_set) { + wpa_snprintf_hex(value, HOSTAPD_CLI_DUP_VALUE_MAX_LEN, + src_hapd->conf->ssid.wpa_psk->psk, PMK_LEN); + } else { + wpa_printf(MSG_WARNING, "DUP: %s cannot be duplicated", param); + goto error_return; + } + + res = hostapd_set_iface(dst_hapd->iconf, dst_hapd->conf, param, value); + os_free(value); + return res; + +error_stringify: + wpa_printf(MSG_ERROR, "DUP: cannot stringify %s", param); +error_return: + os_free(value); + return -1; +} + + +static int +hostapd_global_ctrl_iface_interfaces(struct hapd_interfaces *interfaces, + const char *input, + char *reply, int reply_size) +{ + size_t i, j; + int res; + char *pos, *end; + struct hostapd_iface *iface; + int show_ctrl = 0; + + if (input) + show_ctrl = !!os_strstr(input, "ctrl"); + + pos = reply; + end = reply + reply_size; + + for (i = 0; i < interfaces->count; i++) { + iface = interfaces->iface[i]; + + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_bss_config *conf; + + conf = iface->conf->bss[j]; + if (show_ctrl) + res = os_snprintf(pos, end - pos, + "%s ctrl_iface=%s\n", + conf->iface, + conf->ctrl_interface ? + conf->ctrl_interface : "N/A"); + else + res = os_snprintf(pos, end - pos, "%s\n", + conf->iface); + if (os_snprintf_error(end - pos, res)) { + *pos = '\0'; + return pos - reply; + } + pos += res; + } + } + + return pos - reply; +} + + +static int +hostapd_global_ctrl_iface_dup_network(struct hapd_interfaces *interfaces, + char *cmd) +{ + char *p_start = cmd, *p_end; + struct hostapd_data *src_hapd, *dst_hapd; + + /* cmd: " */ + + p_end = os_strchr(p_start, ' '); + if (!p_end) { + wpa_printf(MSG_ERROR, "DUP: no src ifname found in cmd: '%s'", + cmd); + return -1; + } + + *p_end = '\0'; + src_hapd = hostapd_interfaces_get_hapd(interfaces, p_start); + if (!src_hapd) { + wpa_printf(MSG_ERROR, "DUP: no src ifname found: '%s'", + p_start); + return -1; + } + + p_start = p_end + 1; + p_end = os_strchr(p_start, ' '); + if (!p_end) { + wpa_printf(MSG_ERROR, "DUP: no dst ifname found in cmd: '%s'", + cmd); + return -1; + } + + *p_end = '\0'; + dst_hapd = hostapd_interfaces_get_hapd(interfaces, p_start); + if (!dst_hapd) { + wpa_printf(MSG_ERROR, "DUP: no dst ifname found: '%s'", + p_start); + return -1; + } + + p_start = p_end + 1; + return hostapd_ctrl_iface_dup_param(src_hapd, dst_hapd, p_start); +} + + +static int hostapd_global_ctrl_iface_ifname(struct hapd_interfaces *interfaces, + const char *ifname, + char *buf, char *reply, + int reply_size, + struct sockaddr_storage *from, + socklen_t fromlen) +{ + struct hostapd_data *hapd; + + hapd = hostapd_interfaces_get_hapd(interfaces, ifname); + if (hapd == NULL) { + int res; + + res = os_snprintf(reply, reply_size, "FAIL-NO-IFNAME-MATCH\n"); + if (os_snprintf_error(reply_size, res)) + return -1; + return res; + } + + return hostapd_ctrl_iface_receive_process(hapd, buf, reply,reply_size, + from, fromlen); +} + + +static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct hapd_interfaces *interfaces = eloop_ctx; + char buffer[256], *buf = buffer; + int res; + struct sockaddr_storage from; + socklen_t fromlen = sizeof(from); + char *reply; + int reply_len; + const int reply_size = 4096; +#ifdef CONFIG_CTRL_IFACE_UDP + unsigned char lcookie[CTRL_IFACE_COOKIE_LEN]; +#endif /* CONFIG_CTRL_IFACE_UDP */ + + res = recvfrom(sock, buffer, sizeof(buffer) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s", + strerror(errno)); + return; + } + buf[res] = '\0'; + wpa_printf(MSG_DEBUG, "Global ctrl_iface command: %s", buf); + + reply = os_malloc(reply_size); + if (reply == NULL) { + if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", + strerror(errno)); + } + return; + } + + os_memcpy(reply, "OK\n", 3); + reply_len = 3; + +#ifdef CONFIG_CTRL_IFACE_UDP + if (os_strcmp(buf, "GET_COOKIE") == 0) { + os_memcpy(reply, "COOKIE=", 7); + 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, CTRL_IFACE_COOKIE_LEN) < 0) { + wpa_printf(MSG_DEBUG, + "CTRL: No cookie in the request - drop request"); + os_free(reply); + return; + } + + 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 * CTRL_IFACE_COOKIE_LEN; + while (*buf == ' ') + buf++; +#endif /* CONFIG_CTRL_IFACE_UDP */ + + if (os_strncmp(buf, "IFNAME=", 7) == 0) { + char *pos = os_strchr(buf + 7, ' '); + + if (pos) { + *pos++ = '\0'; + reply_len = hostapd_global_ctrl_iface_ifname( + interfaces, buf + 7, pos, reply, reply_size, + &from, fromlen); + goto send_reply; + } + } + + if (os_strcmp(buf, "PING") == 0) { + os_memcpy(reply, "PONG\n", 5); + reply_len = 5; + } else if (os_strncmp(buf, "RELOG", 5) == 0) { + if (wpa_debug_reopen_file() < 0) + reply_len = -1; + } else if (os_strcmp(buf, "FLUSH") == 0) { + hostapd_ctrl_iface_flush(interfaces); + } else if (os_strncmp(buf, "ADD ", 4) == 0) { + if (hostapd_ctrl_iface_add(interfaces, buf + 4) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "REMOVE ", 7) == 0) { + if (hostapd_ctrl_iface_remove(interfaces, buf + 7) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "ATTACH") == 0) { + if (hostapd_global_ctrl_iface_attach(interfaces, &from, + fromlen, NULL)) + reply_len = -1; + } else if (os_strncmp(buf, "ATTACH ", 7) == 0) { + if (hostapd_global_ctrl_iface_attach(interfaces, &from, + fromlen, buf + 7)) + reply_len = -1; + } else if (os_strcmp(buf, "DETACH") == 0) { + if (hostapd_global_ctrl_iface_detach(interfaces, &from, + fromlen)) + reply_len = -1; +#ifdef CONFIG_MODULE_TESTS + } else if (os_strcmp(buf, "MODULE_TESTS") == 0) { + if (hapd_module_tests() < 0) + reply_len = -1; +#endif /* CONFIG_MODULE_TESTS */ +#ifdef CONFIG_FST + } else if (os_strncmp(buf, "FST-ATTACH ", 11) == 0) { + if (!hostapd_global_ctrl_iface_fst_attach(interfaces, buf + 11)) + reply_len = os_snprintf(reply, reply_size, "OK\n"); + else + reply_len = -1; + } else if (os_strncmp(buf, "FST-DETACH ", 11) == 0) { + if (!hostapd_global_ctrl_iface_fst_detach(interfaces, buf + 11)) + reply_len = os_snprintf(reply, reply_size, "OK\n"); + else + reply_len = -1; + } else if (os_strncmp(buf, "FST-MANAGER ", 12) == 0) { + reply_len = fst_ctrl_iface_receive(buf + 12, reply, reply_size); +#endif /* CONFIG_FST */ + } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) { + if (!hostapd_global_ctrl_iface_dup_network(interfaces, + buf + 12)) + reply_len = os_snprintf(reply, reply_size, "OK\n"); + else + reply_len = -1; + } else if (os_strncmp(buf, "INTERFACES", 10) == 0) { + reply_len = hostapd_global_ctrl_iface_interfaces( + interfaces, buf + 10, reply, reply_size); + } else if (os_strcmp(buf, "TERMINATE") == 0) { + eloop_terminate(); + } else { + wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command " + "ignored"); + reply_len = -1; + } + +send_reply: + if (reply_len < 0) { + os_memcpy(reply, "FAIL\n", 5); + reply_len = 5; + } + + if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, + fromlen) < 0) { + wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s", + strerror(errno)); + } + os_free(reply); +} + + +#ifndef CONFIG_CTRL_IFACE_UDP +static char * hostapd_global_ctrl_iface_path(struct hapd_interfaces *interface) +{ + char *buf; + size_t len; + + if (interface->global_iface_path == NULL) + return NULL; + + len = os_strlen(interface->global_iface_path) + + os_strlen(interface->global_iface_name) + 2; + buf = os_malloc(len); + if (buf == NULL) + return NULL; + + os_snprintf(buf, len, "%s/%s", interface->global_iface_path, + interface->global_iface_name); + buf[len - 1] = '\0'; + return buf; +} +#endif /* CONFIG_CTRL_IFACE_UDP */ + + +int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) +{ +#ifdef CONFIG_CTRL_IFACE_UDP + int port = HOSTAPD_GLOBAL_CTRL_IFACE_PORT; + char p[32] = { 0 }; + char *pos; + struct addrinfo hints = { 0 }, *res, *saveres; + int n; + + if (interface->global_ctrl_sock > -1) { + wpa_printf(MSG_DEBUG, "ctrl_iface already exists!"); + return 0; + } + + if (interface->global_iface_path == NULL) + return 0; + + pos = os_strstr(interface->global_iface_path, "udp:"); + if (pos) { + pos += 4; + port = atoi(pos); + if (port <= 0) { + wpa_printf(MSG_ERROR, "Invalid global ctrl UDP port"); + goto fail; + } + } + + os_get_random(interface->ctrl_iface_cookie, CTRL_IFACE_COOKIE_LEN); + +#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE + hints.ai_flags = AI_PASSIVE; +#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */ + +#ifdef CONFIG_CTRL_IFACE_UDP_IPV6 + hints.ai_family = AF_INET6; +#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + hints.ai_family = AF_INET; +#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */ + hints.ai_socktype = SOCK_DGRAM; + +try_again: + os_snprintf(p, sizeof(p), "%d", port); + n = getaddrinfo(NULL, p, &hints, &res); + if (n) { + wpa_printf(MSG_ERROR, "getaddrinfo(): %s", gai_strerror(n)); + goto fail; + } + + saveres = res; + interface->global_ctrl_sock = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + if (interface->global_ctrl_sock < 0) { + wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno)); + goto fail; + } + + if (bind(interface->global_ctrl_sock, res->ai_addr, res->ai_addrlen) < + 0) { + port++; + if ((port - HOSTAPD_GLOBAL_CTRL_IFACE_PORT) < + HOSTAPD_GLOBAL_CTRL_IFACE_PORT_LIMIT && !pos) + goto try_again; + wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno)); + goto fail; + } + + freeaddrinfo(saveres); + + wpa_printf(MSG_DEBUG, "global ctrl_iface_init UDP port: %d", port); + + if (eloop_register_read_sock(interface->global_ctrl_sock, + hostapd_global_ctrl_iface_receive, + interface, NULL) < 0) { + hostapd_global_ctrl_iface_deinit(interface); + return -1; + } + + wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb); + + return 0; + +fail: + if (interface->global_ctrl_sock >= 0) + close(interface->global_ctrl_sock); + return -1; +#else /* CONFIG_CTRL_IFACE_UDP */ + struct sockaddr_un addr; + int s = -1; + char *fname = NULL; + + if (interface->global_iface_path == NULL) { + wpa_printf(MSG_DEBUG, "ctrl_iface not configured!"); + return 0; + } + + if (mkdir(interface->global_iface_path, S_IRWXU | S_IRWXG) < 0) { + if (errno == EEXIST) { + wpa_printf(MSG_DEBUG, "Using existing control " + "interface directory."); + } else { + wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s", + strerror(errno)); + goto fail; + } + } else if (interface->ctrl_iface_group && + lchown(interface->global_iface_path, -1, + interface->ctrl_iface_group) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", + strerror(errno)); + goto fail; + } + + if (os_strlen(interface->global_iface_path) + 1 + + os_strlen(interface->global_iface_name) >= sizeof(addr.sun_path)) + goto fail; + + s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (s < 0) { + wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno)); + goto fail; + } + + os_memset(&addr, 0, sizeof(addr)); +#ifdef __FreeBSD__ + addr.sun_len = sizeof(addr); +#endif /* __FreeBSD__ */ + addr.sun_family = AF_UNIX; + fname = hostapd_global_ctrl_iface_path(interface); + if (fname == NULL) + goto fail; + os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path)); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s", + strerror(errno)); + if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not" + " allow connections - assuming it was left" + "over from forced program termination"); + if (unlink(fname) < 0) { + wpa_printf(MSG_ERROR, + "Could not unlink existing ctrl_iface socket '%s': %s", + fname, strerror(errno)); + goto fail; + } + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < + 0) { + wpa_printf(MSG_ERROR, "bind(PF_UNIX): %s", + strerror(errno)); + goto fail; + } + wpa_printf(MSG_DEBUG, "Successfully replaced leftover " + "ctrl_iface socket '%s'", fname); + } else { + wpa_printf(MSG_INFO, "ctrl_iface exists and seems to " + "be in use - cannot override it"); + wpa_printf(MSG_INFO, "Delete '%s' manually if it is " + "not used anymore", fname); + os_free(fname); + fname = NULL; + goto fail; + } + } + + if (interface->ctrl_iface_group && + lchown(fname, -1, interface->ctrl_iface_group) < 0) { + wpa_printf(MSG_ERROR, "lchown[ctrl_interface]: %s", + strerror(errno)); + goto fail; + } + + if (chmod(fname, S_IRWXU | S_IRWXG) < 0) { + wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s", + strerror(errno)); + goto fail; + } + os_free(fname); + + interface->global_ctrl_sock = s; + eloop_register_read_sock(s, hostapd_global_ctrl_iface_receive, + interface, NULL); + + wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb); + + return 0; + +fail: + if (s >= 0) + close(s); + if (fname) { + unlink(fname); + os_free(fname); + } + return -1; +#endif /* CONFIG_CTRL_IFACE_UDP */ +} + + +void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces) +{ +#ifndef CONFIG_CTRL_IFACE_UDP + char *fname = NULL; +#endif /* CONFIG_CTRL_IFACE_UDP */ + struct wpa_ctrl_dst *dst, *prev; + + if (interfaces->global_ctrl_sock > -1) { + eloop_unregister_read_sock(interfaces->global_ctrl_sock); + close(interfaces->global_ctrl_sock); + interfaces->global_ctrl_sock = -1; +#ifndef CONFIG_CTRL_IFACE_UDP + fname = hostapd_global_ctrl_iface_path(interfaces); + if (fname) { + unlink(fname); + os_free(fname); + } + + if (interfaces->global_iface_path && + rmdir(interfaces->global_iface_path) < 0) { + if (errno == ENOTEMPTY) { + wpa_printf(MSG_DEBUG, "Control interface " + "directory not empty - leaving it " + "behind"); + } else { + wpa_printf(MSG_ERROR, + "rmdir[ctrl_interface=%s]: %s", + interfaces->global_iface_path, + strerror(errno)); + } + } +#endif /* CONFIG_CTRL_IFACE_UDP */ + } + + os_free(interfaces->global_iface_path); + interfaces->global_iface_path = NULL; + + dl_list_for_each_safe(dst, prev, &interfaces->global_ctrl_dst, + struct wpa_ctrl_dst, list) + os_free(dst); +} + + +static int hostapd_ctrl_check_event_enabled(struct wpa_ctrl_dst *dst, + const char *buf) +{ + /* Enable Probe Request events based on explicit request. + * Other events are enabled by default. + */ + if (str_starts(buf, RX_PROBE_REQUEST)) + return !!(dst->events & WPA_EVENT_RX_PROBE_REQUEST); + return 1; +} + + +static void hostapd_ctrl_iface_send_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 msghdr msg; + int idx, res; + struct iovec io[5]; + char levelstr[10]; + + if (sock < 0 || dl_list_empty(ctrl_dst)) + return; + + 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 = idx; + + idx = 0; + dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) { + if ((level >= dst->debug_level) && + hostapd_ctrl_check_event_enabled(dst, buf)) { + sockaddr_print(MSG_DEBUG, "CTRL_IFACE monitor send", + &dst->addr, dst->addrlen); + msg.msg_name = &dst->addr; + msg.msg_namelen = dst->addrlen; + 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) { + ctrl_iface_detach(ctrl_dst, + &dst->addr, + dst->addrlen); + } + } else + dst->errors = 0; + } + idx++; + } +} + + +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/hostapd/ctrl_iface.h b/hostapd/ctrl_iface.h new file mode 100644 index 000000000000..3341a66bdc6c --- /dev/null +++ b/hostapd/ctrl_iface.h @@ -0,0 +1,39 @@ +/* + * hostapd / UNIX domain socket -based control interface + * Copyright (c) 2004, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef CTRL_IFACE_H +#define CTRL_IFACE_H + +#ifndef CONFIG_NO_CTRL_IFACE +int hostapd_ctrl_iface_init(struct hostapd_data *hapd); +void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd); +int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface); +void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface); +#else /* CONFIG_NO_CTRL_IFACE */ +static inline int hostapd_ctrl_iface_init(struct hostapd_data *hapd) +{ + return 0; +} + +static inline void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd) +{ +} + +static inline int +hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface) +{ + return 0; +} + +static inline void +hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interface) +{ +} +#endif /* CONFIG_NO_CTRL_IFACE */ + +#endif /* CTRL_IFACE_H */ diff --git a/hostapd/defconfig b/hostapd/defconfig new file mode 100644 index 000000000000..550db697bc0a --- /dev/null +++ b/hostapd/defconfig @@ -0,0 +1,427 @@ +# 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 + +# QCA vendor extensions to nl80211 +#CONFIG_DRIVER_NL80211_QCA=y + +# driver_nl80211.c requires libnl. If you are compiling it yourself +# you may need to point hostapd to your version of libnl. +# +#CFLAGS += -I$ +#LIBS += -L$ + +# Use libnl v2.0 (or 3.0) libraries. +#CONFIG_LIBNL20=y + +# Use libnl 3.2 libraries (if this is selected, CONFIG_LIBNL20 is ignored) +CONFIG_LIBNL32=y + + +# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver) +#CONFIG_DRIVER_BSD=y +#CFLAGS += -I/usr/local/include +#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 Re-authentication Protocol (ERP) in integrated EAP server +CONFIG_ERP=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-pwd for the integrated EAP server (secure authentication with a password) +#CONFIG_EAP_PWD=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 +#CONFIG_EAP_FAST=y + +# EAP-TEAP for the integrated EAP server +# Note: The current EAP-TEAP implementation is experimental and should not be +# enabled for production use. The IETF RFC 7170 that defines EAP-TEAP has number +# of conflicting statements and missing details and the implementation has +# vendor specific workarounds for those and as such, may not interoperate with +# any other implementation. This should not be used for anything else than +# experimentation and interoperability testing until those issues has been +# resolved. +#CONFIG_EAP_TEAP=y + +# Wi-Fi Protected Setup (WPS) +#CONFIG_WPS=y +# Enable UPnP support for external WPS Registrars +#CONFIG_WPS_UPNP=y +# Enable WPS support with NFC config method +#CONFIG_WPS_NFC=y + +# EAP-IKEv2 +#CONFIG_EAP_IKEV2=y + +# Trusted Network Connect (EAP-TNC) +#CONFIG_EAP_TNC=y + +# EAP-EKE for the integrated EAP server +#CONFIG_EAP_EKE=y + +# PKCS#12 (PFX) support (used to read private key and certificate file from +# a file that usually has extension .p12 or .pfx) +CONFIG_PKCS12=y + +# 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 + +# Include support fo RADIUS/TLS into the RADIUS client +#CONFIG_RADIUS_TLS=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 + +# Wireless Network Management (IEEE Std 802.11v-2011) +# Note: This is experimental and not complete implementation. +#CONFIG_WNM=y + +# IEEE 802.11ac (Very High Throughput) support +#CONFIG_IEEE80211AC=y + +# IEEE 802.11ax HE support +#CONFIG_IEEE80211AX=y + +# IEEE 802.11be EHT support +# CONFIG_IEEE80211AX is mandatory for setting CONFIG_IEEE80211BE. +# Note: This is experimental and work in progress. The definitions are still +# subject to change and this should not be expected to interoperate with the +# final IEEE 802.11be version. +#CONFIG_IEEE80211BE=y + +# Simultaneous Authentication of Equals (SAE), WPA3-Personal +#CONFIG_SAE=y + +# SAE Public Key, WPA3-Personal +#CONFIG_SAE_PK=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 a file: -f /tmp/hostapd.log +# Disabled by default. +#CONFIG_DEBUG_FILE=y + +# Send debug messages to syslog instead of stdout +#CONFIG_DEBUG_SYSLOG=y + +# Add support for sending all debug messages (regardless of debug verbosity) +# to the Linux kernel tracing facility. This helps debug the entire stack by +# making it easy to record everything happening from the driver up into the +# same file, e.g., using trace-cmd. +#CONFIG_DEBUG_LINUX_TRACING=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 + +# Enable support for fully dynamic VLANs. This enables hostapd to +# automatically create bridge and VLAN interfaces if necessary. +#CONFIG_FULL_DYNAMIC_VLAN=y + +# Use netlink-based kernel API for VLAN operations instead of ioctl() +# Note: This requires libnl 3.1 or newer. +#CONFIG_VLAN_NETLINK=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 + +# 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 + +# hostapd depends on strong random number generation being available from the +# operating system. os_get_random() function is used to fetch random data when +# needed, e.g., for key generation. On Linux and BSD systems, this works by +# reading /dev/urandom. It should be noted that the OS entropy pool needs to be +# properly initialized before hostapd is started. This is important especially +# on embedded devices that do not have a hardware random number generator and +# may by default start up with minimal entropy available for random number +# generation. +# +# As a safety net, hostapd is by default trying to internally collect +# additional entropy for generating random data to mix in with the data +# fetched from the OS. This by itself is not considered to be very strong, but +# it may help in cases where the system pool is not initialized properly. +# However, it is very strongly recommended that the system pool is initialized +# with enough entropy either by using hardware assisted random number +# generator or by storing state over device reboots. +# +# hostapd can be configured to maintain its own entropy store over restarts to +# enhance random number generation. This is not perfect, but it is much more +# secure than using the same sequence of random numbers after every reboot. +# This can be enabled with -e command line option. The specified +# file needs to be readable and writable by hostapd. +# +# If the os_get_random() is known to provide strong random data (e.g., on +# Linux/BSD, the board in question is known to have reliable source of random +# data from /dev/urandom), the internal hostapd random pool can be disabled. +# This will save some in binary size and CPU use. However, this should only be +# considered for builds that are known to be used on devices that meet the +# requirements described above. +#CONFIG_NO_RANDOM_POOL=y + +# Should we attempt to use the getrandom(2) call that provides more reliable +# yet secure randomness source than /dev/random on Linux 3.17 and newer. +# Requires glibc 2.25 to build, falls back to /dev/random if unavailable. +#CONFIG_GETRANDOM=y + +# 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 + +# Should we use kqueue instead of select? Select is used by default. +#CONFIG_ELOOP_KQUEUE=y + +# Select TLS implementation +# openssl = OpenSSL (default) +# gnutls = GnuTLS +# internal = Internal TLSv1 implementation (experimental) +# linux = Linux kernel AF_ALG and internal TLSv1 implementation (experimental) +# none = Empty template +#CONFIG_TLS=openssl + +# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.1) +# can be enabled to get a stronger construction of messages when block ciphers +# are used. +#CONFIG_TLSV11=y + +# TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.2) +# can be enabled to enable use of stronger crypto algorithms. +#CONFIG_TLSV12=y + +# Select which ciphers to use by default with OpenSSL if the user does not +# specify them. +#CONFIG_TLS_DEFAULT_CIPHERS="DEFAULT:!EXP:!LOW" + +# If CONFIG_TLS=internal is used, additional library and include paths are +# needed for LibTomMath. Alternatively, an integrated, minimal version of +# LibTomMath can be used. See beginning of libtommath.c for details on benefits +# and drawbacks of this option. +#CONFIG_INTERNAL_LIBTOMMATH=y +#ifndef CONFIG_INTERNAL_LIBTOMMATH +#LTM_PATH=/usr/src/libtommath-0.39 +#CFLAGS += -I$(LTM_PATH) +#LIBS += -L$(LTM_PATH) +#LIBS_p += -L$(LTM_PATH) +#endif +# At the cost of about 4 kB of additional binary size, the internal LibTomMath +# can be configured to include faster routines for exptmod, sqr, and div to +# speed up DH and RSA calculation considerably +#CONFIG_INTERNAL_LIBTOMMATH_FAST=y + +# Interworking (IEEE 802.11u) +# This can be used to enable functionality to improve interworking with +# external networks. +#CONFIG_INTERWORKING=y + +# Hotspot 2.0 +#CONFIG_HS20=y + +# Enable SQLite database support in hlr_auc_gw, EAP-SIM DB, and eap_user_file +#CONFIG_SQLITE=y + +# Enable Fast Session Transfer (FST) +#CONFIG_FST=y + +# Enable CLI commands for FST testing +#CONFIG_FST_TEST=y + +# Testing options +# This can be used to enable some testing options (see also the example +# configuration file) that are really useful only for testing clients that +# connect to this hostapd. These options allow, for example, to drop a +# certain percentage of probe requests or auth/(re)assoc frames. +# +#CONFIG_TESTING_OPTIONS=y + +# Automatic Channel Selection +# This will allow hostapd to pick the channel automatically when channel is set +# to "acs_survey" or "0". Eventually, other ACS algorithms can be added in +# similar way. +# +# Automatic selection is currently only done through initialization, later on +# we hope to do background checks to keep us moving to more ideal channels as +# time goes by. ACS is currently only supported through the nl80211 driver and +# your driver must have survey dump capability that is filled by the driver +# during scanning. +# +# You can customize the ACS survey algorithm with the hostapd.conf variable +# acs_num_scans. +# +# Supported ACS drivers: +# * ath9k +# * ath5k +# * ath10k +# +# For more details refer to: +# https://wireless.wiki.kernel.org/en/users/documentation/acs +# +#CONFIG_ACS=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 + +# Client Taxonomy +# Has the AP retain the Probe Request and (Re)Association Request frames from +# a client, from which a signature can be produced which can identify the model +# of client device like "Nexus 6P" or "iPhone 5s". +#CONFIG_TAXONOMY=y + +# Fast Initial Link Setup (FILS) (IEEE 802.11ai) +#CONFIG_FILS=y +# FILS shared key authentication with PFS +#CONFIG_FILS_SK_PFS=y + +# Include internal line edit mode in hostapd_cli. This can be used to provide +# limited command line editing and history support. +#CONFIG_WPA_CLI_EDIT=y + +# Opportunistic Wireless Encryption (OWE) +# Experimental implementation of draft-harkins-owe-07.txt +#CONFIG_OWE=y + +# Airtime policy support +#CONFIG_AIRTIME_POLICY=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. +#CONFIG_PASN=y + +# Device Provisioning Protocol (DPP) (also known as Wi-Fi Easy Connect) +CONFIG_DPP=y +# DPP version 2 support +CONFIG_DPP2=y +# DPP version 3 support (experimental and still changing; do not enable for +# production use) +#CONFIG_DPP3=y + +# Wi-Fi Aware unsynchronized service discovery (NAN USD) +#CONFIG_NAN_USD=y diff --git a/hostapd/eap_register.c b/hostapd/eap_register.c new file mode 100644 index 000000000000..3e870c7f0670 --- /dev/null +++ b/hostapd/eap_register.c @@ -0,0 +1,155 @@ +/* + * EAP method registration + * Copyright (c) 2004-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_server/eap_methods.h" +#include "eap_register.h" + + +/** + * eap_server_register_methods - Register statically linked EAP server methods + * Returns: 0 on success, -1 or -2 on failure + * + * This function is called at program initialization to register all EAP + * methods that were linked in statically. + */ +int eap_server_register_methods(void) +{ + int ret = 0; + +#ifdef EAP_SERVER_IDENTITY + if (ret == 0) + ret = eap_server_identity_register(); +#endif /* EAP_SERVER_IDENTITY */ + +#ifdef EAP_SERVER_MD5 + if (ret == 0) + ret = eap_server_md5_register(); +#endif /* EAP_SERVER_MD5 */ + +#ifdef EAP_SERVER_TLS + if (ret == 0) + ret = eap_server_tls_register(); +#endif /* EAP_SERVER_TLS */ + +#ifdef EAP_SERVER_UNAUTH_TLS + if (ret == 0) + ret = eap_server_unauth_tls_register(); +#endif /* EAP_SERVER_TLS */ + +#ifdef EAP_SERVER_TLS +#ifdef CONFIG_HS20 + if (ret == 0) + ret = eap_server_wfa_unauth_tls_register(); +#endif /* CONFIG_HS20 */ +#endif /* EAP_SERVER_TLS */ + +#ifdef EAP_SERVER_MSCHAPV2 + if (ret == 0) + ret = eap_server_mschapv2_register(); +#endif /* EAP_SERVER_MSCHAPV2 */ + +#ifdef EAP_SERVER_PEAP + if (ret == 0) + ret = eap_server_peap_register(); +#endif /* EAP_SERVER_PEAP */ + +#ifdef EAP_SERVER_TLV + if (ret == 0) + ret = eap_server_tlv_register(); +#endif /* EAP_SERVER_TLV */ + +#ifdef EAP_SERVER_GTC + if (ret == 0) + ret = eap_server_gtc_register(); +#endif /* EAP_SERVER_GTC */ + +#ifdef EAP_SERVER_TTLS + if (ret == 0) + ret = eap_server_ttls_register(); +#endif /* EAP_SERVER_TTLS */ + +#ifdef EAP_SERVER_SIM + if (ret == 0) + ret = eap_server_sim_register(); +#endif /* EAP_SERVER_SIM */ + +#ifdef EAP_SERVER_AKA + if (ret == 0) + ret = eap_server_aka_register(); +#endif /* EAP_SERVER_AKA */ + +#ifdef EAP_SERVER_AKA_PRIME + if (ret == 0) + ret = eap_server_aka_prime_register(); +#endif /* EAP_SERVER_AKA_PRIME */ + +#ifdef EAP_SERVER_PAX + if (ret == 0) + ret = eap_server_pax_register(); +#endif /* EAP_SERVER_PAX */ + +#ifdef EAP_SERVER_PSK + if (ret == 0) + ret = eap_server_psk_register(); +#endif /* EAP_SERVER_PSK */ + +#ifdef EAP_SERVER_SAKE + if (ret == 0) + ret = eap_server_sake_register(); +#endif /* EAP_SERVER_SAKE */ + +#ifdef EAP_SERVER_GPSK + if (ret == 0) + ret = eap_server_gpsk_register(); +#endif /* EAP_SERVER_GPSK */ + +#ifdef EAP_SERVER_VENDOR_TEST + if (ret == 0) + ret = eap_server_vendor_test_register(); +#endif /* EAP_SERVER_VENDOR_TEST */ + +#ifdef EAP_SERVER_FAST + if (ret == 0) + ret = eap_server_fast_register(); +#endif /* EAP_SERVER_FAST */ + +#ifdef EAP_SERVER_TEAP + if (ret == 0) + ret = eap_server_teap_register(); +#endif /* EAP_SERVER_TEAP */ + +#ifdef EAP_SERVER_WSC + if (ret == 0) + ret = eap_server_wsc_register(); +#endif /* EAP_SERVER_WSC */ + +#ifdef EAP_SERVER_IKEV2 + if (ret == 0) + ret = eap_server_ikev2_register(); +#endif /* EAP_SERVER_IKEV2 */ + +#ifdef EAP_SERVER_TNC + if (ret == 0) + ret = eap_server_tnc_register(); +#endif /* EAP_SERVER_TNC */ + +#ifdef EAP_SERVER_PWD + if (ret == 0) + ret = eap_server_pwd_register(); +#endif /* EAP_SERVER_PWD */ + +#ifdef EAP_SERVER_EKE + if (ret == 0) + ret = eap_server_eke_register(); +#endif /* EAP_SERVER_EKE */ + + return ret; +} diff --git a/hostapd/eap_register.h b/hostapd/eap_register.h new file mode 100644 index 000000000000..c342351a1a0d --- /dev/null +++ b/hostapd/eap_register.h @@ -0,0 +1,14 @@ +/* + * EAP method registration + * Copyright (c) 2004-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef EAP_REGISTER_H +#define EAP_REGISTER_H + +int eap_server_register_methods(void); + +#endif /* EAP_REGISTER_H */ diff --git a/hostapd/eap_testing.txt b/hostapd/eap_testing.txt new file mode 100644 index 000000000000..04468c39f01e --- /dev/null +++ b/hostapd/eap_testing.txt @@ -0,0 +1,77 @@ +Interoperability testing of hostapd's IEEE 802.1X/EAPOL authentication + +Test matrix + ++) tested successfully +F) failed +-) peer did not support +?) not tested + +XSupplicant --------------------------------. +Intel PROSet ---------------------------. | +Windows XP -------------------------. | | +Mac OS X 10.4 ------------------. | | | +Nokia S60 ------------------. | | | | +wpa_supplicant ---------. | | | | | + | | | | | | + +EAP-MD5 + - ? ? - +EAP-GTC + - ? - - +EAP-MSCHAPv2 + - ? - - +EAP-TLS + + +1 + + +EAP-PEAPv0/MSCHAPv2 + + + + + + +EAP-PEAPv0/GTC + + + - + +EAP-PEAPv0/MD5 + - + - - +EAP-PEAPv0/TLS + F - + + +EAP-PEAPv0/SIM + + - - - +EAP-PEAPv0/AKA + + - - - +EAP-PEAPv0/PSK + - - - - +EAP-PEAPv0/PAX + - - - - +EAP-PEAPv0/SAKE + - - - - +EAP-PEAPv0/GPSK + - - - - +EAP-PEAPv1/MSCHAPv2 + + + - + + +EAP-PEAPv1/GTC + + + - + +EAP-PEAPv1/MD5 + - + - - +EAP-PEAPv1/TLS + F - - + +EAP-PEAPv1/SIM + + - - - +EAP-PEAPv1/AKA + + - - - +EAP-PEAPv1/PSK + - - - - +EAP-PEAPv1/PAX + - - - - +EAP-PEAPv1/SAKE + - - - - +EAP-PEAPv1/GPSK + - - - - +EAP-TTLS/CHAP + - + - + + +EAP-TTLS/MSCHAP + - + - + + +EAP-TTLS/MSCHAPv2 + + + - + + +EAP-TTLS/PAP + - + - + + +EAP-TTLS/EAP-MD5 + - - - - + +EAP-TTLS/EAP-GTC + + - - - +EAP-TTLS/EAP-MSCHAPv2 + + - - - +EAP-TTLS/EAP-TLS + F - - - +EAP-TTLS/EAP-SIM + + - - - +EAP-TTLS/EAP-AKA + + - - - +EAP-TTLS + TNC + - - - - +EAP-SIM + + - - + +EAP-AKA + + - - - +EAP-PAX + - - - - +EAP-SAKE + - - - - +EAP-GPSK + - - - - +EAP-FAST/MSCHAPv2(prov) + - F - F +EAP-FAST/GTC(auth) + - + - + +EAP-FAST/MSCHAPv2(aprov)+ - F - F +EAP-FAST/GTC(aprov) + - F - F +EAP-FAST/MD5(aprov) + - - - - +EAP-FAST/TLS(aprov) + - - - - +EAP-FAST/SIM(aprov) + - - - - +EAP-FAST/AKA(aprov) + - - - - +EAP-FAST/MSCHAPv2(auth) + - + - + +EAP-FAST/MD5(auth) + - + - - +EAP-FAST/TLS(auth) + - - - - +EAP-FAST/SIM(auth) + - - - - +EAP-FAST/AKA(auth) + - - - - +EAP-FAST + TNC + - - - - +EAP-IKEv2 + - - - - +EAP-TNC + - - - - + +1) EAP-TLS itself worked, but peer certificate validation failed at + least when using the internal TLS server (peer included incorrect + certificates in the chain?) diff --git a/hostapd/hapd_module_tests.c b/hostapd/hapd_module_tests.c new file mode 100644 index 000000000000..a5016f22bf09 --- /dev/null +++ b/hostapd/hapd_module_tests.c @@ -0,0 +1,18 @@ +/* + * hostapd module tests + * Copyright (c) 2014, Jouni Malinen + * + * 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/module_tests.h" + +int hapd_module_tests(void) +{ + wpa_printf(MSG_INFO, "hostapd module tests"); + return 0; +} diff --git a/hostapd/hlr_auc_gw.c b/hostapd/hlr_auc_gw.c new file mode 100644 index 000000000000..5caa779dd648 --- /dev/null +++ b/hostapd/hlr_auc_gw.c @@ -0,0 +1,1109 @@ +/* + * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator + * Copyright (c) 2005-2007, 2012-2017, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + * + * This is an example implementation of the EAP-SIM/AKA database/authentication + * gateway interface to HLR/AuC. It is expected to be replaced with an + * implementation of SS7 gateway to GSM/UMTS authentication center (HLR/AuC) or + * a local implementation of SIM triplet and AKA authentication data generator. + * + * hostapd will send SIM/AKA authentication queries over a UNIX domain socket + * to and external program, e.g., this hlr_auc_gw. This interface uses simple + * text-based format: + * + * EAP-SIM / GSM triplet query/response: + * SIM-REQ-AUTH + * SIM-RESP-AUTH Kc1:SRES1:RAND1 Kc2:SRES2:RAND2 [Kc3:SRES3:RAND3] + * SIM-RESP-AUTH FAILURE + * GSM-AUTH-REQ RAND1:RAND2[:RAND3] + * GSM-AUTH-RESP Kc1:SRES1:Kc2:SRES2[:Kc3:SRES3] + * GSM-AUTH-RESP FAILURE + * + * EAP-AKA / UMTS query/response: + * AKA-REQ-AUTH + * AKA-RESP-AUTH + * AKA-RESP-AUTH FAILURE + * + * EAP-AKA / UMTS AUTS (re-synchronization): + * AKA-AUTS + * + * IMSI and max_chal are sent as an ASCII string, + * Kc/SRES/RAND/AUTN/IK/CK/RES/AUTS as hex strings. + * + * An example implementation here reads GSM authentication triplets from a + * text file in IMSI:Kc:SRES:RAND format, IMSI in ASCII, other fields as hex + * strings. This is used to simulate an HLR/AuC. As such, it is not very useful + * for real life authentication, but it is useful both as an example + * implementation and for EAP-SIM/AKA/AKA' testing. + * + * For a stronger example design, Milenage and GSM-Milenage algorithms can be + * used to dynamically generate authenticatipn information for EAP-AKA/AKA' and + * EAP-SIM, respectively, if Ki is known. + * + * SQN generation follows the not time-based Profile 2 described in + * 3GPP TS 33.102 Annex C.3.2. The length of IND is 5 bits by default, but this + * can be changed with a command line options if needed. + */ + +#include "includes.h" +#include +#ifdef CONFIG_SQLITE +#include +#endif /* CONFIG_SQLITE */ + +#include "common.h" +#include "crypto/milenage.h" +#include "crypto/random.h" + +static const char *default_socket_path = "/tmp/hlr_auc_gw.sock"; +static const char *socket_path; +static int serv_sock = -1; +static char *milenage_file = NULL; +static int update_milenage = 0; +static int sqn_changes = 0; +static int ind_len = 5; +static int stdout_debug = 1; + +/* GSM triplets */ +struct gsm_triplet { + struct gsm_triplet *next; + char imsi[20]; + u8 kc[8]; + u8 sres[4]; + u8 _rand[16]; +}; + +static struct gsm_triplet *gsm_db = NULL, *gsm_db_pos = NULL; + +/* OPc and AMF parameters for Milenage (Example algorithms for AKA). */ +struct milenage_parameters { + struct milenage_parameters *next; + char imsi[20]; + u8 ki[16]; + u8 opc[16]; + u8 amf[2]; + u8 sqn[6]; + int set; + size_t res_len; +}; + +static struct milenage_parameters *milenage_db = NULL; + +#define EAP_SIM_MAX_CHAL 3 + +#define EAP_AKA_RAND_LEN 16 +#define EAP_AKA_AUTN_LEN 16 +#define EAP_AKA_AUTS_LEN 14 +#define EAP_AKA_RES_MIN_LEN 4 +#define EAP_AKA_RES_MAX_LEN 16 +#define EAP_AKA_IK_LEN 16 +#define EAP_AKA_CK_LEN 16 + + +#ifdef CONFIG_SQLITE + +static sqlite3 *sqlite_db = NULL; +static struct milenage_parameters db_tmp_milenage; + + +static int db_table_exists(sqlite3 *db, const char *name) +{ + char cmd[128]; + os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name); + return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK; +} + + +static int db_table_create_milenage(sqlite3 *db) +{ + char *err = NULL; + const char *sql = + "CREATE TABLE milenage(" + " imsi INTEGER PRIMARY KEY NOT NULL," + " ki CHAR(32) NOT NULL," + " opc CHAR(32) NOT NULL," + " amf CHAR(4) NOT NULL," + " sqn CHAR(12) NOT NULL," + " res_len INTEGER" + ");"; + + printf("Adding database table for milenage information\n"); + if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) { + printf("SQLite error: %s\n", err); + sqlite3_free(err); + return -1; + } + + return 0; +} + + +static sqlite3 * db_open(const char *db_file) +{ + sqlite3 *db; + + if (sqlite3_open(db_file, &db)) { + printf("Failed to open database %s: %s\n", + db_file, sqlite3_errmsg(db)); + sqlite3_close(db); + return NULL; + } + + if (!db_table_exists(db, "milenage") && + db_table_create_milenage(db) < 0) { + sqlite3_close(db); + return NULL; + } + + return db; +} + + +static int get_milenage_cb(void *ctx, int argc, char *argv[], char *col[]) +{ + struct milenage_parameters *m = ctx; + int i; + + m->set = 1; + + for (i = 0; i < argc; i++) { + if (os_strcmp(col[i], "ki") == 0 && argv[i] && + hexstr2bin(argv[i], m->ki, sizeof(m->ki))) { + printf("Invalid ki value in database\n"); + return -1; + } + + if (os_strcmp(col[i], "opc") == 0 && argv[i] && + hexstr2bin(argv[i], m->opc, sizeof(m->opc))) { + printf("Invalid opcvalue in database\n"); + return -1; + } + + if (os_strcmp(col[i], "amf") == 0 && argv[i] && + hexstr2bin(argv[i], m->amf, sizeof(m->amf))) { + printf("Invalid amf value in database\n"); + return -1; + } + + if (os_strcmp(col[i], "sqn") == 0 && argv[i] && + hexstr2bin(argv[i], m->sqn, sizeof(m->sqn))) { + printf("Invalid sqn value in database\n"); + return -1; + } + + if (os_strcmp(col[i], "res_len") == 0 && argv[i]) { + m->res_len = atoi(argv[i]); + } + } + + return 0; +} + + +static struct milenage_parameters * db_get_milenage(const char *imsi_txt) +{ + char cmd[128]; + unsigned long long imsi; + + os_memset(&db_tmp_milenage, 0, sizeof(db_tmp_milenage)); + imsi = atoll(imsi_txt); + os_snprintf(db_tmp_milenage.imsi, sizeof(db_tmp_milenage.imsi), + "%llu", imsi); + os_snprintf(cmd, sizeof(cmd), + "SELECT * FROM milenage WHERE imsi=%llu;", imsi); + if (sqlite3_exec(sqlite_db, cmd, get_milenage_cb, &db_tmp_milenage, + NULL) != SQLITE_OK) + return NULL; + + if (!db_tmp_milenage.set) + return NULL; + return &db_tmp_milenage; +} + + +static int db_update_milenage_sqn(struct milenage_parameters *m) +{ + char cmd[128], val[13], *pos; + + if (sqlite_db == NULL) + return 0; + + pos = val; + pos += wpa_snprintf_hex(pos, sizeof(val), m->sqn, 6); + *pos = '\0'; + os_snprintf(cmd, sizeof(cmd), + "UPDATE milenage SET sqn='%s' WHERE imsi=%s;", + val, m->imsi); + if (sqlite3_exec(sqlite_db, cmd, NULL, NULL, NULL) != SQLITE_OK) { + printf("Failed to update SQN in database for IMSI %s\n", + m->imsi); + return -1; + } + return 0; +} + +#endif /* CONFIG_SQLITE */ + + +static int open_socket(const char *path) +{ + struct sockaddr_un addr; + int s; + + s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket(PF_UNIX)"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, path, sizeof(addr.sun_path)); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("hlr-auc-gw: bind(PF_UNIX)"); + close(s); + return -1; + } + + return s; +} + + +static int read_gsm_triplets(const char *fname) +{ + FILE *f; + char buf[200], *pos, *pos2; + struct gsm_triplet *g = NULL; + int line, ret = 0; + + if (fname == NULL) + return -1; + + f = fopen(fname, "r"); + if (f == NULL) { + printf("Could not open GSM triplet data file '%s'\n", fname); + return -1; + } + + line = 0; + while (fgets(buf, sizeof(buf), f)) { + line++; + + /* Parse IMSI:Kc:SRES:RAND */ + buf[sizeof(buf) - 1] = '\0'; + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0' && *pos != '\n') + pos++; + if (*pos == '\n') + *pos = '\0'; + pos = buf; + if (*pos == '\0') + continue; + + g = os_zalloc(sizeof(*g)); + if (g == NULL) { + ret = -1; + break; + } + + /* IMSI */ + pos2 = NULL; + pos = str_token(buf, ":", &pos2); + if (!pos || os_strlen(pos) >= sizeof(g->imsi)) { + printf("%s:%d - Invalid IMSI\n", fname, line); + ret = -1; + break; + } + os_strlcpy(g->imsi, pos, sizeof(g->imsi)); + + /* Kc */ + pos = str_token(buf, ":", &pos2); + if (!pos || os_strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) { + printf("%s:%d - Invalid Kc\n", fname, line); + ret = -1; + break; + } + + /* SRES */ + pos = str_token(buf, ":", &pos2); + if (!pos || os_strlen(pos) != 8 || + hexstr2bin(pos, g->sres, 4)) { + printf("%s:%d - Invalid SRES\n", fname, line); + ret = -1; + break; + } + + /* RAND */ + pos = str_token(buf, ":", &pos2); + if (!pos || os_strlen(pos) != 32 || + hexstr2bin(pos, g->_rand, 16)) { + printf("%s:%d - Invalid RAND\n", fname, line); + ret = -1; + break; + } + + g->next = gsm_db; + gsm_db = g; + g = NULL; + } + os_free(g); + + fclose(f); + + return ret; +} + + +static struct gsm_triplet * get_gsm_triplet(const char *imsi) +{ + struct gsm_triplet *g = gsm_db_pos; + + while (g) { + if (strcmp(g->imsi, imsi) == 0) { + gsm_db_pos = g->next; + return g; + } + g = g->next; + } + + g = gsm_db; + while (g && g != gsm_db_pos) { + if (strcmp(g->imsi, imsi) == 0) { + gsm_db_pos = g->next; + return g; + } + g = g->next; + } + + return NULL; +} + + +static int read_milenage(const char *fname) +{ + FILE *f; + char buf[200], *pos, *pos2; + struct milenage_parameters *m = NULL; + int line, ret = 0; + + if (fname == NULL) + return -1; + + f = fopen(fname, "r"); + if (f == NULL) { + printf("Could not open Milenage data file '%s'\n", fname); + return -1; + } + + line = 0; + while (fgets(buf, sizeof(buf), f)) { + line++; + + /* Parse IMSI Ki OPc AMF SQN [RES_len] */ + buf[sizeof(buf) - 1] = '\0'; + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0' && *pos != '\n') + pos++; + if (*pos == '\n') + *pos = '\0'; + pos = buf; + if (*pos == '\0') + continue; + + m = os_zalloc(sizeof(*m)); + if (m == NULL) { + ret = -1; + break; + } + + /* IMSI */ + pos2 = NULL; + pos = str_token(buf, " ", &pos2); + if (!pos || os_strlen(pos) >= sizeof(m->imsi)) { + printf("%s:%d - Invalid IMSI\n", fname, line); + ret = -1; + break; + } + os_strlcpy(m->imsi, pos, sizeof(m->imsi)); + + /* Ki */ + pos = str_token(buf, " ", &pos2); + if (!pos || os_strlen(pos) != 32 || + hexstr2bin(pos, m->ki, 16)) { + printf("%s:%d - Invalid Ki\n", fname, line); + ret = -1; + break; + } + + /* OPc */ + pos = str_token(buf, " ", &pos2); + if (!pos || os_strlen(pos) != 32 || + hexstr2bin(pos, m->opc, 16)) { + printf("%s:%d - Invalid OPc\n", fname, line); + ret = -1; + break; + } + + /* AMF */ + pos = str_token(buf, " ", &pos2); + if (!pos || os_strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) { + printf("%s:%d - Invalid AMF\n", fname, line); + ret = -1; + break; + } + + /* SQN */ + pos = str_token(buf, " ", &pos2); + if (!pos || os_strlen(pos) != 12 || + hexstr2bin(pos, m->sqn, 6)) { + printf("%s:%d - Invalid SEQ\n", fname, line); + ret = -1; + break; + } + + pos = str_token(buf, " ", &pos2); + if (pos) { + m->res_len = atoi(pos); + if (m->res_len && + (m->res_len < EAP_AKA_RES_MIN_LEN || + m->res_len > EAP_AKA_RES_MAX_LEN)) { + printf("%s:%d - Invalid RES_len\n", + fname, line); + ret = -1; + break; + } + } + + m->next = milenage_db; + milenage_db = m; + m = NULL; + } + os_free(m); + + fclose(f); + + return ret; +} + + +static void update_milenage_file(const char *fname) +{ + FILE *f, *f2; + char name[500], buf[500], *pos; + char *end = buf + sizeof(buf); + struct milenage_parameters *m; + size_t imsi_len; + + f = fopen(fname, "r"); + if (f == NULL) { + printf("Could not open Milenage data file '%s'\n", fname); + return; + } + + snprintf(name, sizeof(name), "%s.new", fname); + f2 = fopen(name, "w"); + if (f2 == NULL) { + printf("Could not write Milenage data file '%s'\n", name); + fclose(f); + return; + } + + while (fgets(buf, sizeof(buf), f)) { + /* IMSI Ki OPc AMF SQN */ + buf[sizeof(buf) - 1] = '\0'; + + pos = strchr(buf, ' '); + if (buf[0] == '#' || pos == NULL || pos - buf >= 20) + goto no_update; + + imsi_len = pos - buf; + + for (m = milenage_db; m; m = m->next) { + if (strncmp(buf, m->imsi, imsi_len) == 0 && + m->imsi[imsi_len] == '\0') + break; + } + + if (!m) + goto no_update; + + pos = buf; + pos += snprintf(pos, end - pos, "%s ", m->imsi); + pos += wpa_snprintf_hex(pos, end - pos, m->ki, 16); + *pos++ = ' '; + pos += wpa_snprintf_hex(pos, end - pos, m->opc, 16); + *pos++ = ' '; + pos += wpa_snprintf_hex(pos, end - pos, m->amf, 2); + *pos++ = ' '; + pos += wpa_snprintf_hex(pos, end - pos, m->sqn, 6); + *pos++ = '\n'; + + no_update: + fprintf(f2, "%s", buf); + } + + fclose(f2); + fclose(f); + + snprintf(name, sizeof(name), "%s.bak", fname); + if (rename(fname, name) < 0) { + perror("rename"); + return; + } + + snprintf(name, sizeof(name), "%s.new", fname); + if (rename(name, fname) < 0) { + perror("rename"); + return; + } + +} + + +static struct milenage_parameters * get_milenage(const char *imsi) +{ + struct milenage_parameters *m = milenage_db; + + while (m) { + if (strcmp(m->imsi, imsi) == 0) + break; + m = m->next; + } + +#ifdef CONFIG_SQLITE + if (!m) + m = db_get_milenage(imsi); +#endif /* CONFIG_SQLITE */ + + return m; +} + + +static int sim_req_auth(char *imsi, char *resp, size_t resp_len) +{ + int count, max_chal, ret; + char *pos; + char *rpos, *rend; + struct milenage_parameters *m; + struct gsm_triplet *g; + + resp[0] = '\0'; + + pos = strchr(imsi, ' '); + if (pos) { + *pos++ = '\0'; + max_chal = atoi(pos); + if (max_chal < 1 || max_chal > EAP_SIM_MAX_CHAL) + max_chal = EAP_SIM_MAX_CHAL; + } else + max_chal = EAP_SIM_MAX_CHAL; + + rend = resp + resp_len; + rpos = resp; + ret = snprintf(rpos, rend - rpos, "SIM-RESP-AUTH %s", imsi); + if (ret < 0 || ret >= rend - rpos) + return -1; + rpos += ret; + + m = get_milenage(imsi); + if (m) { + u8 _rand[16], sres[4], kc[8]; + for (count = 0; count < max_chal; count++) { + if (random_get_bytes(_rand, 16) < 0) + return -1; + gsm_milenage(m->opc, m->ki, _rand, sres, kc); + *rpos++ = ' '; + rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8); + *rpos++ = ':'; + rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4); + *rpos++ = ':'; + rpos += wpa_snprintf_hex(rpos, rend - rpos, _rand, 16); + } + *rpos = '\0'; + return 0; + } + + count = 0; + while (count < max_chal && (g = get_gsm_triplet(imsi))) { + if (strcmp(g->imsi, imsi) != 0) + continue; + + if (rpos < rend) + *rpos++ = ' '; + rpos += wpa_snprintf_hex(rpos, rend - rpos, g->kc, 8); + if (rpos < rend) + *rpos++ = ':'; + rpos += wpa_snprintf_hex(rpos, rend - rpos, g->sres, 4); + if (rpos < rend) + *rpos++ = ':'; + rpos += wpa_snprintf_hex(rpos, rend - rpos, g->_rand, 16); + count++; + } + + if (count == 0) { + printf("No GSM triplets found for %s\n", imsi); + ret = snprintf(rpos, rend - rpos, " FAILURE"); + if (ret < 0 || ret >= rend - rpos) + return -1; + rpos += ret; + } + + return 0; +} + + +static int gsm_auth_req(char *imsi, char *resp, size_t resp_len) +{ + int count, ret; + char *pos, *rpos, *rend; + struct milenage_parameters *m; + + resp[0] = '\0'; + + pos = os_strchr(imsi, ' '); + if (!pos) + return -1; + *pos++ = '\0'; + + rend = resp + resp_len; + rpos = resp; + ret = os_snprintf(rpos, rend - rpos, "GSM-AUTH-RESP %s", imsi); + if (os_snprintf_error(rend - rpos, ret)) + return -1; + rpos += ret; + + m = get_milenage(imsi); + if (m) { + u8 _rand[16], sres[4], kc[8]; + for (count = 0; count < EAP_SIM_MAX_CHAL; count++) { + if (hexstr2bin(pos, _rand, 16) != 0) + return -1; + gsm_milenage(m->opc, m->ki, _rand, sres, kc); + *rpos++ = count == 0 ? ' ' : ':'; + rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8); + *rpos++ = ':'; + rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4); + pos += 16 * 2; + if (*pos != ':') + break; + pos++; + } + *rpos = '\0'; + return 0; + } + + printf("No GSM triplets found for %s\n", imsi); + ret = os_snprintf(rpos, rend - rpos, " FAILURE"); + if (os_snprintf_error(rend - rpos, ret)) + return -1; + rpos += ret; + + return 0; +} + + +static void inc_sqn(u8 *sqn) +{ + u64 val, seq, ind; + + /* + * SQN = SEQ | IND = SEQ1 | SEQ2 | IND + * + * The mechanism used here is not time-based, so SEQ2 is void and + * SQN = SEQ1 | IND. The length of IND is ind_len bits and the length + * of SEQ1 is 48 - ind_len bits. + */ + + /* Increment both SEQ and IND by one */ + val = ((u64) WPA_GET_BE32(sqn) << 16) | ((u64) WPA_GET_BE16(sqn + 4)); + seq = (val >> ind_len) + 1; + ind = (val + 1) & ((1 << ind_len) - 1); + val = (seq << ind_len) | ind; + WPA_PUT_BE32(sqn, val >> 16); + WPA_PUT_BE16(sqn + 4, val & 0xffff); +} + + +static int aka_req_auth(char *imsi, char *resp, size_t resp_len) +{ + /* AKA-RESP-AUTH */ + char *pos, *end; + u8 _rand[EAP_AKA_RAND_LEN]; + u8 autn[EAP_AKA_AUTN_LEN]; + u8 ik[EAP_AKA_IK_LEN]; + u8 ck[EAP_AKA_CK_LEN]; + u8 res[EAP_AKA_RES_MAX_LEN]; + size_t res_len; + int ret; + struct milenage_parameters *m; + int failed = 0; + + m = get_milenage(imsi); + if (m) { + if (random_get_bytes(_rand, EAP_AKA_RAND_LEN) < 0) + return -1; + res_len = EAP_AKA_RES_MAX_LEN; + inc_sqn(m->sqn); +#ifdef CONFIG_SQLITE + db_update_milenage_sqn(m); +#endif /* CONFIG_SQLITE */ + sqn_changes = 1; + if (stdout_debug) { + printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n", + m->sqn[0], m->sqn[1], m->sqn[2], + m->sqn[3], m->sqn[4], m->sqn[5]); + } + milenage_generate(m->opc, m->amf, m->ki, m->sqn, _rand, + autn, ik, ck, res, &res_len); + if (m->res_len >= EAP_AKA_RES_MIN_LEN && + m->res_len <= EAP_AKA_RES_MAX_LEN && + m->res_len < res_len) + res_len = m->res_len; + } else { + printf("Unknown IMSI: %s\n", imsi); +#ifdef AKA_USE_FIXED_TEST_VALUES + printf("Using fixed test values for AKA\n"); + memset(_rand, '0', EAP_AKA_RAND_LEN); + memset(autn, '1', EAP_AKA_AUTN_LEN); + memset(ik, '3', EAP_AKA_IK_LEN); + memset(ck, '4', EAP_AKA_CK_LEN); + memset(res, '2', EAP_AKA_RES_MAX_LEN); + res_len = EAP_AKA_RES_MAX_LEN; +#else /* AKA_USE_FIXED_TEST_VALUES */ + failed = 1; +#endif /* AKA_USE_FIXED_TEST_VALUES */ + } + + pos = resp; + end = resp + resp_len; + ret = snprintf(pos, end - pos, "AKA-RESP-AUTH %s ", imsi); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + if (failed) { + ret = snprintf(pos, end - pos, "FAILURE"); + if (ret < 0 || ret >= end - pos) + return -1; + pos += ret; + return 0; + } + pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN); + *pos++ = ' '; + pos += wpa_snprintf_hex(pos, end - pos, autn, EAP_AKA_AUTN_LEN); + *pos++ = ' '; + pos += wpa_snprintf_hex(pos, end - pos, ik, EAP_AKA_IK_LEN); + *pos++ = ' '; + pos += wpa_snprintf_hex(pos, end - pos, ck, EAP_AKA_CK_LEN); + *pos++ = ' '; + pos += wpa_snprintf_hex(pos, end - pos, res, res_len); + + return 0; +} + + +static int aka_auts(char *imsi, char *resp, size_t resp_len) +{ + char *auts, *__rand; + u8 _auts[EAP_AKA_AUTS_LEN], _rand[EAP_AKA_RAND_LEN], sqn[6]; + struct milenage_parameters *m; + + resp[0] = '\0'; + + /* AKA-AUTS */ + + auts = strchr(imsi, ' '); + if (auts == NULL) + return -1; + *auts++ = '\0'; + + __rand = strchr(auts, ' '); + if (__rand == NULL) + return -1; + *__rand++ = '\0'; + + if (stdout_debug) { + printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n", + imsi, auts, __rand); + } + if (hexstr2bin(auts, _auts, EAP_AKA_AUTS_LEN) || + hexstr2bin(__rand, _rand, EAP_AKA_RAND_LEN)) { + printf("Could not parse AUTS/RAND\n"); + return -1; + } + + m = get_milenage(imsi); + if (m == NULL) { + printf("Unknown IMSI: %s\n", imsi); + return -1; + } + + if (milenage_auts(m->opc, m->ki, _rand, _auts, sqn)) { + printf("AKA-AUTS: Incorrect MAC-S\n"); + } else { + memcpy(m->sqn, sqn, 6); + if (stdout_debug) { + printf("AKA-AUTS: Re-synchronized: " + "SQN=%02x%02x%02x%02x%02x%02x\n", + sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]); + } +#ifdef CONFIG_SQLITE + db_update_milenage_sqn(m); +#endif /* CONFIG_SQLITE */ + sqn_changes = 1; + } + + return 0; +} + + +static int process_cmd(char *cmd, char *resp, size_t resp_len) +{ + if (os_strncmp(cmd, "SIM-REQ-AUTH ", 13) == 0) + return sim_req_auth(cmd + 13, resp, resp_len); + + if (os_strncmp(cmd, "GSM-AUTH-REQ ", 13) == 0) + return gsm_auth_req(cmd + 13, resp, resp_len); + + if (os_strncmp(cmd, "AKA-REQ-AUTH ", 13) == 0) + return aka_req_auth(cmd + 13, resp, resp_len); + + if (os_strncmp(cmd, "AKA-AUTS ", 9) == 0) + return aka_auts(cmd + 9, resp, resp_len); + + printf("Unknown request: %s\n", cmd); + return -1; +} + + +static int process(int s) +{ + char buf[1000], resp[1000]; + struct sockaddr_un from; + socklen_t fromlen; + ssize_t res; + + fromlen = sizeof(from); + res = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *) &from, + &fromlen); + if (res < 0) { + perror("recvfrom"); + return -1; + } + + if (res == 0) + return 0; + + if ((size_t) res >= sizeof(buf)) + res = sizeof(buf) - 1; + buf[res] = '\0'; + + printf("Received: %s\n", buf); + + if (process_cmd(buf, resp, sizeof(resp)) < 0) { + printf("Failed to process request\n"); + return -1; + } + + if (resp[0] == '\0') { + printf("No response\n"); + return 0; + } + + printf("Send: %s\n", resp); + + if (sendto(s, resp, os_strlen(resp), 0, (struct sockaddr *) &from, + fromlen) < 0) + perror("send"); + + return 0; +} + + +static void cleanup(void) +{ + struct gsm_triplet *g, *gprev; + struct milenage_parameters *m, *prev; + + if (update_milenage && milenage_file && sqn_changes) + update_milenage_file(milenage_file); + + g = gsm_db; + while (g) { + gprev = g; + g = g->next; + os_free(gprev); + } + + m = milenage_db; + while (m) { + prev = m; + m = m->next; + os_free(prev); + } + + if (serv_sock >= 0) + close(serv_sock); + if (socket_path) + unlink(socket_path); + +#ifdef CONFIG_SQLITE + if (sqlite_db) { + sqlite3_close(sqlite_db); + sqlite_db = NULL; + } +#endif /* CONFIG_SQLITE */ +} + + +static void handle_term(int sig) +{ + printf("Signal %d - terminate\n", sig); + exit(0); +} + + +static void usage(void) +{ + printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA " + "database/authenticator\n" + "Copyright (c) 2005-2017, Jouni Malinen \n" + "\n" + "usage:\n" + "hlr_auc_gw [-hu] [-s] [-g] " + "[-m] \\\n" + " [-D] [-i] [command]\n" + "\n" + "options:\n" + " -h = show this usage help\n" + " -u = update SQN in Milenage file on exit\n" + " -s = path for UNIX domain socket\n" + " (default: %s)\n" + " -g = path for GSM authentication triplets\n" + " -m = path for Milenage keys\n" + " -D = path to SQLite database\n" + " -i = IND length for SQN (default: 5)\n" + "\n" + "If the optional command argument, like " + "\"AKA-REQ-AUTH \" is used, a single\n" + "command is processed with response sent to stdout. Otherwise, " + "hlr_auc_gw opens\n" + "a control interface and processes commands sent through it " + "(e.g., by EAP server\n" + "in hostapd).\n", + default_socket_path); +} + + +int main(int argc, char *argv[]) +{ + int c; + char *gsm_triplet_file = NULL; + char *sqlite_db_file = NULL; + int ret = 0; + + if (os_program_init()) + return -1; + + socket_path = default_socket_path; + + for (;;) { + c = getopt(argc, argv, "D:g:hi:m:s:u"); + if (c < 0) + break; + switch (c) { + case 'D': +#ifdef CONFIG_SQLITE + sqlite_db_file = optarg; + break; +#else /* CONFIG_SQLITE */ + printf("No SQLite support included in the build\n"); + return -1; +#endif /* CONFIG_SQLITE */ + case 'g': + gsm_triplet_file = optarg; + break; + case 'h': + usage(); + return 0; + case 'i': + ind_len = atoi(optarg); + if (ind_len < 0 || ind_len > 32) { + printf("Invalid IND length\n"); + return -1; + } + break; + case 'm': + milenage_file = optarg; + break; + case 's': + socket_path = optarg; + break; + case 'u': + update_milenage = 1; + break; + default: + usage(); + return -1; + } + } + + if (!gsm_triplet_file && !milenage_file && !sqlite_db_file) { + usage(); + return -1; + } + +#ifdef CONFIG_SQLITE + if (sqlite_db_file && (sqlite_db = db_open(sqlite_db_file)) == NULL) + return -1; +#endif /* CONFIG_SQLITE */ + + if (gsm_triplet_file && read_gsm_triplets(gsm_triplet_file) < 0) + return -1; + + if (milenage_file && read_milenage(milenage_file) < 0) + return -1; + + if (optind == argc) { + serv_sock = open_socket(socket_path); + if (serv_sock < 0) + return -1; + + printf("Listening for requests on %s\n", socket_path); + + atexit(cleanup); + signal(SIGTERM, handle_term); + signal(SIGINT, handle_term); + + for (;;) + process(serv_sock); + } else { + char buf[1000]; + socket_path = NULL; + stdout_debug = 0; + if (process_cmd(argv[optind], buf, sizeof(buf)) < 0) { + printf("FAIL\n"); + ret = -1; + } else { + printf("%s\n", buf); + } + cleanup(); + } + +#ifdef CONFIG_SQLITE + if (sqlite_db) { + sqlite3_close(sqlite_db); + sqlite_db = NULL; + } +#endif /* CONFIG_SQLITE */ + + os_program_deinit(); + + return ret; +} diff --git a/hostapd/hlr_auc_gw.milenage_db b/hostapd/hlr_auc_gw.milenage_db new file mode 100644 index 000000000000..a250653108b3 --- /dev/null +++ b/hostapd/hlr_auc_gw.milenage_db @@ -0,0 +1,15 @@ +# Parameters for Milenage (Example algorithms for AKA). +# The example Ki, OPc, and AMF values here are from 3GPP TS 35.208 v6.0.0 +# 4.3.20 Test Set 20. SQN is the last used SQN value. +# These values can be used for both UMTS (EAP-AKA) and GSM (EAP-SIM) +# authentication. In case of GSM/EAP-SIM, AMF and SQN values are not used, but +# stub values will need to be included in this file. + +# IMSI Ki OPc AMF SQN [RES_len] +232010000000000 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000 +# Example using truncated 32-bit RES instead of 64-bit default +232010000000001 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000 4 + +# These values are from Test Set 19 which has the AMF separation bit set to 1 +# and as such, is suitable for EAP-AKA' test. +555444333222111 5122250214c33e723a5dd523fc145fc0 981d464c7c52eb6e5036234984ad0bcf c3ab 16f3b3f70fc1 diff --git a/hostapd/hlr_auc_gw.txt b/hostapd/hlr_auc_gw.txt new file mode 100644 index 000000000000..097bbce36297 --- /dev/null +++ b/hostapd/hlr_auc_gw.txt @@ -0,0 +1,104 @@ +HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator + +hlr_auc_gw is an example implementation of the EAP-SIM/AKA/AKA' +database/authentication gateway interface to HLR/AuC. It could be +replaced with an implementation of SS7 gateway to GSM/UMTS +authentication center (HLR/AuC). hostapd will send SIM/AKA +authentication queries over a UNIX domain socket to and external +program, e.g., hlr_auc_gw. + +hlr_auc_gw can be configured with GSM and UMTS authentication data with +text files: GSM triplet file (see hostapd.sim_db) and Milenage file (see +hlr_auc_gw.milenage_db). Milenage parameters can be used to generate +dynamic authentication data for EAP-SIM, EAP-AKA, and EAP-AKA' while the +GSM triplet data is used for a more static configuration (e.g., triplets +extracted from a SIM card). + +Alternatively, hlr_auc_gw can be built with support for an SQLite +database for more dynamic operations. This is enabled by adding +"CONFIG_SQLITE=y" into hostapd/.config before building hlr_auc_gw ("make +clean; make hlr_auc_gw" in this directory). + +hostapd is configured to use hlr_auc_gw with the eap_sim_db parameter in +hostapd.conf (e.g., "eap_sim_db=unix:/tmp/hlr_auc_gw.sock"). hlr_auc_gw +is configured with command line parameters: + +hlr_auc_gw [-hu] [-s] [-g] [-m] \ + [-D] [-i] + +options: + -h = show this usage help + -u = update SQN in Milenage file on exit + -s = path for UNIX domain socket + (default: /tmp/hlr_auc_gw.sock) + -g = path for GSM authentication triplets + -m = path for Milenage keys + -D = path to SQLite database + -i = IND length for SQN (default: 5) + + +The SQLite database can be initialized with sqlite, e.g., by running +following commands in "sqlite3 /path/to/hlr_auc_gw.db": + +CREATE TABLE milenage( + imsi INTEGER PRIMARY KEY NOT NULL, + ki CHAR(32) NOT NULL, + opc CHAR(32) NOT NULL, + amf CHAR(4) NOT NULL, + sqn CHAR(12) NOT NULL +); +INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES( + 232010000000000, + '90dca4eda45b53cf0f12d7c9c3bc6a89', + 'cb9cccc4b9258e6dca4760379fb82581', + '61df', + '000000000000' +); +INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES( + 555444333222111, + '5122250214c33e723a5dd523fc145fc0', + '981d464c7c52eb6e5036234984ad0bcf', + 'c3ab', + '16f3b3f70fc1' +); + + +hostapd (EAP server) can also be configured to store the EAP-SIM/AKA +pseudonyms and reauth information into a SQLite database. This is +configured with the db parameter within the eap_sim_db configuration +option. + + +"hlr_auc_gw -D /path/to/hlr_auc_gw.db" can then be used to fetch +Milenage parameters based on IMSI from the database. The database can be +updated dynamically while hlr_auc_gw is running to add/remove/modify +entries. + + +Example configuration files for hostapd to operate as a RADIUS +authentication server for EAP-SIM/AKA/AKA': + +hostapd.conf: + +driver=none +radius_server_clients=hostapd.radius_clients +eap_server=1 +eap_user_file=hostapd.eap_user +eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/eap_sim.db +eap_sim_aka_result_ind=1 + +hostapd.radius_clients: + +0.0.0.0/0 radius + +hostapd.eap_user: + +"0"* AKA +"1"* SIM +"2"* AKA +"3"* SIM +"4"* AKA +"5"* SIM +"6"* AKA' +"7"* AKA' +"8"* AKA' diff --git a/hostapd/hostapd.8 b/hostapd/hostapd.8 new file mode 100644 index 000000000000..d19d862c0a75 --- /dev/null +++ b/hostapd/hostapd.8 @@ -0,0 +1,59 @@ +.TH HOSTAPD 8 "April 7, 2005" hostapd hostapd +.SH NAME +hostapd \- IEEE 802.11 AP, IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator +.SH SYNOPSIS +.B hostapd +[\-hdBKtv] [\-P ] +.SH DESCRIPTION +This manual page documents briefly the +.B hostapd +daemon. +.PP +.B hostapd +is a user space daemon for access point and authentication servers. +It implements IEEE 802.11 access point management, IEEE 802.1X/WPA/WPA2/EAP Authenticators and RADIUS authentication server. +The current version supports Linux (Host AP, mac80211-based drivers) and FreeBSD (net80211). + +.B hostapd +is designed to be a "daemon" program that runs in the background and acts as the backend component controlling authentication. +.B hostapd +supports separate frontend programs and an example text-based frontend, +.BR hostapd_cli , +is included with +.BR hostapd . +.SH OPTIONS +A summary of options is included below. +For a complete description, run +.BR hostapd +from the command line. +.TP +.B \-h +Show usage. +.TP +.B \-d +Show more debug messages. +.TP +.B \-dd +Show even more debug messages. +.TP +.B \-B +Run daemon in the background. +.TP +.B \-P +Path to PID file. +.TP +.B \-K +Include key data in debug messages. +.TP +.B \-t +Include timestamps in some debug messages. +.TP +.B \-v +Show hostapd version. +.SH SEE ALSO +.BR hostapd_cli (1). +.SH AUTHOR +hostapd was written by Jouni Malinen . +.PP +This manual page was written by Faidon Liambotis , +for the Debian project (but may be used by others). diff --git a/hostapd/hostapd.accept b/hostapd/hostapd.accept new file mode 100644 index 000000000000..2d2a0a27efdc --- /dev/null +++ b/hostapd/hostapd.accept @@ -0,0 +1,6 @@ +# List of MAC addresses that are allowed to authenticate (IEEE 802.11) +# with the AP. Optional VLAN ID can be assigned for clients based on the +# MAC address if dynamic VLANs (hostapd.conf dynamic_vlan option) are used. +00:11:22:33:44:55 +00:66:77:88:99:aa +00:00:22:33:44:55 1 diff --git a/hostapd/hostapd.android.rc b/hostapd/hostapd.android.rc new file mode 100644 index 000000000000..26a87b808914 --- /dev/null +++ b/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/hostapd/hostapd.conf b/hostapd/hostapd.conf new file mode 100644 index 000000000000..d875d5fc63b0 --- /dev/null +++ b/hostapd/hostapd.conf @@ -0,0 +1,3383 @@ +##### hostapd configuration file ############################################## +# Empty lines and lines starting with # are ignored + +# AP netdevice name (without 'ap' postfix, i.e., wlan0 uses wlan0ap for +# management frames with the Host AP driver); wlan0 with many nl80211 drivers +# Note: This attribute can be overridden by the values supplied with the '-i' +# command line parameter. +interface=wlan0 + +# In case of atheros and nl80211 driver interfaces, an additional +# configuration parameter, bridge, may be used to notify hostapd if the +# interface is included in a bridge. This parameter is not used with Host AP +# driver. If the bridge parameter is not set, the drivers will automatically +# figure out the bridge interface (assuming sysfs is enabled and mounted to +# /sys) and this parameter may not be needed. +# +# For nl80211, this parameter can be used to request the AP interface to be +# added to the bridge automatically (brctl may refuse to do this before hostapd +# has been started to change the interface mode). If needed, the bridge +# interface is also created. +#bridge=br0 + +# Driver interface type (hostap/wired/none/nl80211/bsd); +# default: hostap). nl80211 is used with all Linux mac80211 drivers. +# Use driver=none if building hostapd as a standalone RADIUS server that does +# not control any wireless/wired driver. +# driver=hostap + +# Driver interface parameters (mainly for development testing use) +# driver_params= + +# hostapd event logger configuration +# +# Two output method: syslog and stdout (only usable if not forking to +# background). +# +# Module bitfield (ORed bitfield of modules that will be logged; -1 = all +# modules): +# bit 0 (1) = IEEE 802.11 +# bit 1 (2) = IEEE 802.1X +# bit 2 (4) = RADIUS +# bit 3 (8) = WPA +# bit 4 (16) = driver interface +# bit 6 (64) = MLME +# +# Levels (minimum value for logged events): +# 0 = verbose debugging +# 1 = debugging +# 2 = informational messages +# 3 = notification +# 4 = warning +# +logger_syslog=-1 +logger_syslog_level=2 +logger_stdout=-1 +logger_stdout_level=2 + +# Interface for separate control program. If this is specified, hostapd +# will create this directory and a UNIX domain socket for listening to requests +# from external programs (CLI/GUI, etc.) for status information and +# configuration. The socket file will be named based on the interface name, so +# multiple hostapd processes/interfaces can be run at the same time if more +# than one interface is used. +# /var/run/hostapd is the recommended directory for sockets and by default, +# hostapd_cli will use it when trying to connect with hostapd. +ctrl_interface=/var/run/hostapd + +# Access control for the control interface can be configured by setting the +# directory to allow only members of a group to use sockets. This way, it is +# possible to run hostapd as root (since it needs to change network +# configuration and open raw sockets) and still allow GUI/CLI components to be +# 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 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. +# +# This variable can be a group name or gid. +#ctrl_interface_group=wheel +ctrl_interface_group=0 + + +##### IEEE 802.11 related configuration ####################################### + +# SSID to be used in IEEE 802.11 management frames +ssid=test +# Alternative formats for configuring SSID +# (double quoted string, hexdump, printf-escaped string) +#ssid2="test" +#ssid2=74657374 +#ssid2=P"hello\nthere" + +# UTF-8 SSID: Whether the SSID is to be interpreted using UTF-8 encoding +#utf8_ssid=1 + +# Country code (ISO/IEC 3166-1). Used to set regulatory domain. +# Set as needed to indicate country in which device is operating. +# This can limit available channels and transmit power. +# These two octets are used as the first two octets of the Country String +# (dot11CountryString) +#country_code=US + +# The third octet of the Country String (dot11CountryString) +# This parameter is used to set the third octet of the country string. +# +# All environments of the current frequency band and country (default) +#country3=0x20 +# Outdoor environment only +#country3=0x4f +# Indoor environment only +#country3=0x49 +# Noncountry entity (country_code=XX) +#country3=0x58 +# IEEE 802.11 standard Annex E table indication: 0x01 .. 0x1f +# Annex E, Table E-4 (Global operating classes) +#country3=0x04 + +# Enable IEEE 802.11d. This advertises the country_code and the set of allowed +# channels and transmit power levels based on the regulatory limits. The +# country_code setting must be configured with the correct country for +# IEEE 802.11d functions. +# (default: 0 = disabled) +#ieee80211d=1 + +# Enable IEEE 802.11h. This enables radar detection and DFS support if +# available. DFS support is required on outdoor 5 GHz channels in most countries +# of the world. This can be used only with ieee80211d=1. +# (default: 0 = disabled) +#ieee80211h=1 + +# Add Power Constraint element to Beacon and Probe Response frames +# This config option adds Power Constraint element when applicable and Country +# element is added. Power Constraint element is required by Transmit Power +# Control. This can be used only with ieee80211d=1. +# Valid values are 0..255. +#local_pwr_constraint=3 + +# Set Spectrum Management subfield in the Capability Information field. +# This config option forces the Spectrum Management bit to be set. When this +# option is not set, the value of the Spectrum Management bit depends on whether +# DFS or TPC is required by regulatory authorities. This can be used only with +# ieee80211d=1 and local_pwr_constraint configured. +#spectrum_mgmt_required=1 + +# 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. 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. +# Default: IEEE 802.11b +hw_mode=g + +# Channel number (IEEE 802.11) +# (default: 0, i.e., not set) +# Please note that some drivers do not use this value from hostapd and the +# channel will need to be configured separately with iwconfig. +# +# If CONFIG_ACS build option is enabled, the channel can be selected +# automatically at run time by setting channel=acs_survey or channel=0, both of +# 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: https://wireless.wiki.kernel.org/en/users/documentation/acs +# +# You can customize the ACS survey algorithm with following variables: +# +# acs_num_scans requirement is 1..100 - number of scans to be performed that +# are used to trigger survey data gathering of an underlying device driver. +# Scans are passive and typically take a little over 100ms (depending on the +# driver) on each available channel for given hw_mode. Increasing this value +# means sacrificing startup time and gathering more data wrt channel +# interference that may help choosing a better channel. This can also help fine +# tune the ACS scan time in case a driver has different scan dwell times. +# +# acs_chan_bias is a space-separated list of : pairs. It can be +# used to increase (or decrease) the likelihood of a specific channel to be +# selected by the ACS algorithm. The total interference factor for each channel +# gets multiplied by the specified bias value before finding the channel with +# the lowest value. In other words, values between 0.0 and 1.0 can be used to +# make a channel more likely to be picked while values larger than 1.0 make the +# specified channel less likely to be picked. This can be used, e.g., to prefer +# the commonly used 2.4 GHz band channels 1, 6, and 11 (which is the default +# behavior on 2.4 GHz band if no acs_chan_bias parameter is specified). +# +# Defaults: +#acs_num_scans=5 +#acs_chan_bias=1:0.8 6:0.8 11:0.8 + +# Channel list restriction. This option allows hostapd to select one of the +# provided channels when a channel should be automatically selected. +# Channel list can be provided as range using hyphen ('-') or individual +# channels can be specified by space (' ') separated values +# Default: all channels allowed in selected hw_mode +#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 + +# Enable background radar feature +# This feature allows CAC to be run on dedicated radio RF chains while the +# radio(s) are otherwise running normal AP activities on other channels. +# This requires that the driver and the radio support it before feature will +# actually be enabled, i.e., this parameter value is ignored with drivers that +# do not advertise support for the capability. +# 0: Leave disabled (default) +# 1: Enable it. +#enable_background_radar=1 + +# Set minimum permitted max TX power (in dBm) for ACS and DFS channel selection. +# (default 0, i.e., not constraint) +#min_tx_power=20 + +# Beacon interval in kus (1.024 ms) (default: 100; range 15..65535) +beacon_int=100 + +# DTIM (delivery traffic information message) period (range 1..255): +# number of beacons between DTIMs (1 = every beacon includes DTIM element) +# (default: 2) +dtim_period=2 + +# Maximum number of stations allowed in station table. New stations will be +# rejected after the station table is full. IEEE 802.11 has a limit of 2007 +# different association IDs, so this number should not be larger than that. +# (default: 2007) +max_num_sta=255 + +# RTS/CTS threshold; -1 = disabled (default); range -1..65535 +# If this field is not included in hostapd.conf, hostapd will not control +# RTS threshold and 'iwconfig wlan# rts ' can be used to set it. +rts_threshold=-1 + +# Fragmentation threshold; -1 = disabled (default); range -1, 256..2346 +# If this field is not included in hostapd.conf, hostapd will not control +# fragmentation threshold and 'iwconfig wlan# frag ' can be used to set +# it. +fragm_threshold=-1 + +# Rate configuration +# Default is to enable all rates supported by the hardware. This configuration +# item allows this list be filtered so that only the listed rates will be left +# in the list. If the list is empty, all rates are used. This list can have +# entries that are not in the list of rates the hardware supports (such entries +# are ignored). The entries in this list are in 100 kbps, i.e., 11 Mbps = 110. +# If this item is present, at least one rate have to be matching with the rates +# hardware supports. +# default: use the most common supported rate setting for the selected +# hw_mode (i.e., this line can be removed from configuration file in most +# cases) +#supported_rates=10 20 55 110 60 90 120 180 240 360 480 540 + +# Basic rate set configuration +# List of rates (in 100 kbps) that are included in the basic rate set. +# If this item is not included, usually reasonable default set is used. +#basic_rates=10 20 +#basic_rates=10 20 55 110 +#basic_rates=60 120 240 + +# Beacon frame TX rate configuration +# This sets the TX rate that is used to transmit Beacon frames. If this item is +# not included, the driver default rate (likely lowest rate) is used. +# Legacy (CCK/OFDM rates): +# beacon_rate= +# HT: +# 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 + +# Short Preamble +# This parameter can be used to enable optional use of short preamble for +# frames sent at 2 Mbps, 5.5 Mbps, and 11 Mbps to improve network performance. +# This applies only to IEEE 802.11b-compatible networks and this should only be +# enabled if the local hardware supports use of short preamble. If any of the +# associated STAs do not support short preamble, use of short preamble will be +# disabled (and enabled when such STAs disassociate) dynamically. +# 0 = do not allow use of short preamble (default) +# 1 = allow use of short preamble +#preamble=1 + +# Station MAC address -based authentication +# Please note that this kind of access control requires a driver that uses +# hostapd to take care of management frame processing and as such, this can be +# used with driver=hostap or driver=nl80211, but not with driver=atheros. +# 0 = accept unless in deny list +# 1 = deny unless in accept list +# 2 = use external RADIUS server (accept/deny lists are searched first) +macaddr_acl=0 + +# Accept/deny lists are read from separate files (containing list of +# MAC addresses, one per line). Use absolute path name to make sure that the +# files can be read on SIGHUP configuration reloads. +#accept_mac_file=/etc/hostapd.accept +#deny_mac_file=/etc/hostapd.deny + +# IEEE 802.11 specifies two authentication algorithms. hostapd can be +# configured to allow both of these or only one. Open system authentication +# should be used with IEEE 802.1X. +# Bit fields of allowed authentication algorithms: +# bit 0 = Open System Authentication +# bit 1 = Shared Key Authentication (requires WEP) +auth_algs=3 + +# Send empty SSID in beacons and ignore probe request frames that do not +# specify full SSID, i.e., require stations to know SSID. +# default: disabled (0) +# 1 = send empty (length=0) SSID in beacon and ignore probe request for +# broadcast SSID +# 2 = clear SSID (ASCII 0), but keep the original length (this may be required +# with some clients that do not support empty SSID) and ignore probe +# requests for broadcast SSID +ignore_broadcast_ssid=0 + +# Do not reply to broadcast Probe Request frames from unassociated STA if there +# is no room for additional stations (max_num_sta). This can be used to +# discourage a STA from trying to associate with this AP if the association +# would be rejected due to maximum STA limit. +# Default: 0 (disabled) +#no_probe_resp_if_max_sta=0 + +# Additional vendor specific elements for Beacon and Probe Response frames +# This parameter can be used to add additional vendor specific element(s) into +# the end of the Beacon and Probe Response frames. The format for these +# element(s) is a hexdump of the raw information elements (id+len+payload for +# one or more elements) +#vendor_elements=dd0411223301 + +# Additional vendor specific elements for (Re)Association Response frames +# This parameter can be used to add additional vendor specific element(s) into +# the end of the (Re)Association Response frames. The format for these +# element(s) is a hexdump of the raw information elements (id+len+payload for +# one or more elements) +#assocresp_elements=dd0411223301 + +# TX queue parameters (EDCF / bursting) +# tx_queue__ +# queues: data0, data1, data2, data3 +# (data0 is the highest priority queue) +# parameters: +# aifs: AIFS (default 2) +# cwmin: cwMin (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, +# 16383, 32767) +# cwmax: cwMax (same values as cwMin, cwMax >= cwMin) +# burst: maximum length (in milliseconds with precision of up to 0.1 ms) for +# bursting +# +# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e): +# These parameters are used by the access point when transmitting frames +# to the clients. +# +# Low priority / AC_BK = background +#tx_queue_data3_aifs=7 +#tx_queue_data3_cwmin=15 +#tx_queue_data3_cwmax=1023 +#tx_queue_data3_burst=0 +# Note: for IEEE 802.11b mode: cWmin=31 cWmax=1023 burst=0 +# +# Normal priority / AC_BE = best effort +#tx_queue_data2_aifs=3 +#tx_queue_data2_cwmin=15 +#tx_queue_data2_cwmax=63 +#tx_queue_data2_burst=0 +# Note: for IEEE 802.11b mode: cWmin=31 cWmax=127 burst=0 +# +# High priority / AC_VI = video +#tx_queue_data1_aifs=1 +#tx_queue_data1_cwmin=7 +#tx_queue_data1_cwmax=15 +#tx_queue_data1_burst=3.0 +# Note: for IEEE 802.11b mode: cWmin=15 cWmax=31 burst=6.0 +# +# Highest priority / AC_VO = voice +#tx_queue_data0_aifs=1 +#tx_queue_data0_cwmin=3 +#tx_queue_data0_cwmax=7 +#tx_queue_data0_burst=1.5 +# Note: for IEEE 802.11b mode: cWmin=7 cWmax=15 burst=3.3 + +# 802.1D Tag (= UP) to AC mappings +# WMM specifies following mapping of data frames to different ACs. This mapping +# can be configured using Linux QoS/tc and sch_pktpri.o module. +# 802.1D Tag 802.1D Designation Access Category WMM Designation +# 1 BK AC_BK Background +# 2 - AC_BK Background +# 0 BE AC_BE Best Effort +# 3 EE AC_BE Best Effort +# 4 CL AC_VI Video +# 5 VI AC_VI Video +# 6 VO AC_VO Voice +# 7 NC AC_VO Voice +# Data frames with no priority information: AC_BE +# Management frames: AC_VO +# PS-Poll frames: AC_BE + +# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e): +# for 802.11a or 802.11g networks +# These parameters are sent to WMM clients when they associate. +# The parameters will be used by WMM clients for frames transmitted to the +# access point. +# +# note - txop_limit is in units of 32microseconds +# note - acm is admission control mandatory flag. 0 = admission control not +# required, 1 = mandatory +# note - Here cwMin and cmMax are in exponent form. The actual cw value used +# will be (2^n)-1 where n is the value given here. The allowed range for these +# wmm_ac_??_{cwmin,cwmax} is 0..15 with cwmax >= cwmin. +# +wmm_enabled=1 +# +# WMM-PS Unscheduled Automatic Power Save Delivery [U-APSD] +# Enable this flag if U-APSD supported outside hostapd (eg., Firmware/driver) +#uapsd_advertisement_enabled=1 +# +# Low priority / AC_BK = background +wmm_ac_bk_cwmin=4 +wmm_ac_bk_cwmax=10 +wmm_ac_bk_aifs=7 +wmm_ac_bk_txop_limit=0 +wmm_ac_bk_acm=0 +# Note: for IEEE 802.11b mode: cWmin=5 cWmax=10 +# +# Normal priority / AC_BE = best effort +wmm_ac_be_aifs=3 +wmm_ac_be_cwmin=4 +wmm_ac_be_cwmax=10 +wmm_ac_be_txop_limit=0 +wmm_ac_be_acm=0 +# Note: for IEEE 802.11b mode: cWmin=5 cWmax=7 +# +# High priority / AC_VI = video +wmm_ac_vi_aifs=2 +wmm_ac_vi_cwmin=3 +wmm_ac_vi_cwmax=4 +wmm_ac_vi_txop_limit=94 +wmm_ac_vi_acm=0 +# Note: for IEEE 802.11b mode: cWmin=4 cWmax=5 txop_limit=188 +# +# Highest priority / AC_VO = voice +wmm_ac_vo_aifs=2 +wmm_ac_vo_cwmin=2 +wmm_ac_vo_cwmax=3 +wmm_ac_vo_txop_limit=47 +wmm_ac_vo_acm=0 +# Note: for IEEE 802.11b mode: cWmin=3 cWmax=4 burst=102 + +# Enable Multi-AP functionality +# 0 = disabled (default) +# 1 = AP support backhaul BSS +# 2 = AP support fronthaul BSS +# 3 = AP supports both backhaul BSS and fronthaul BSS +#multi_ap=0 + +# Static WEP key configuration +# +# The key number to use when transmitting. +# It must be between 0 and 3, and the corresponding key must be set. +# default: not set +#wep_default_key=0 +# The WEP keys to use. +# A key may be a quoted string or unquoted hexadecimal digits. +# The key length should be 5, 13, or 16 characters, or 10, 26, or 32 +# digits, depending on whether 40-bit (64-bit), 104-bit (128-bit), or +# 128-bit (152-bit) WEP is used. +# Only the default key must be supplied; the others are optional. +# default: not set +#wep_key0=123456789a +#wep_key1="vwxyz" +#wep_key2=0102030405060708090a0b0c0d +#wep_key3=".2.4.6.8.0.23" + +# Station inactivity limit +# +# If a station does not send anything in ap_max_inactivity seconds, an +# empty data frame is sent to it in order to verify whether it is +# still in range. If this frame is not ACKed, the station will be +# disassociated and then deauthenticated. This feature is used to +# clear station table of old entries when the STAs move out of the +# range. +# +# The station can associate again with the AP if it is still in range; +# this inactivity poll is just used as a nicer way of verifying +# inactivity; i.e., client will not report broken connection because +# disassociation frame is not sent immediately without first polling +# the STA with a data frame. +# default: 300 (i.e., 5 minutes) +#ap_max_inactivity=300 +# +# The inactivity polling can be disabled to disconnect stations based on +# inactivity timeout so that idle stations are more likely to be disconnected +# even if they are still in range of the AP. This can be done by setting +# skip_inactivity_poll to 1 (default 0). +#skip_inactivity_poll=0 +# +# BSS max idle period management +# 0 = disabled (do not advertise and manage BSS max idle period) +# 1 = enabled (advertise and manage BSS max idle period; default) +# 2 = enabled requiring protected frames (advertise and manage BSS max idle +# period and require STAs to use protected keep-alive frames) +#bss_max_idle=1 +# +# Maximum acceptable BSS maximum idle period +# If this is set to a nonzero value, the AP allows STAs to request different +# maximum idle period values. This is in the units to 1000 TUs (1.024 s) +#max_acceptable_idle_period=600 +# +# Allow STA to skip group key handshake without getting disconnection when +# BSS max idle period management is enabled. +# 0 = disconnect STA if it does not reply to group key handshake (default) +# 1 = do not disconnect STA if it does not reply to group key handshake and +# if BSS max idle period management is enabled +#no_disconnect_on_group_keyerror=0 + +# Disassociate stations based on excessive transmission failures or other +# indications of connection loss. This depends on the driver capabilities and +# may not be available with all drivers. +#disassoc_low_ack=1 + +# Maximum allowed Listen Interval (how many Beacon periods STAs are allowed to +# remain asleep). Default: 65535 (no limit apart from field size) +#max_listen_interval=100 + +# WDS (4-address frame) mode with per-station virtual interfaces +# (only supported with driver=nl80211) +# This mode allows associated stations to use 4-address frames to allow layer 2 +# bridging to be used. +#wds_sta=1 + +# If bridge parameter is set, the WDS STA interface will be added to the same +# bridge by default. This can be overridden with the wds_bridge parameter to +# use a separate bridge. +#wds_bridge=wds-br0 + +# Start the AP with beaconing disabled by default. +#start_disabled=0 + +# Client isolation can be used to prevent low-level bridging of frames between +# associated stations in the BSS. By default, this bridging is allowed. +#ap_isolate=1 + +# BSS Load update period (in BUs) +# This field is used to enable and configure adding a BSS Load element into +# Beacon and Probe Response frames. +#bss_load_update_period=50 + +# Channel utilization averaging period (in BUs) +# This field is used to enable and configure channel utilization average +# calculation with bss_load_update_period. This should be in multiples of +# bss_load_update_period for more accurate calculation. +#chan_util_avg_period=600 + +# Fixed BSS Load value for testing purposes +# This field can be used to configure hostapd to add a fixed BSS Load element +# into Beacon and Probe Response frames for testing purposes. The format is +# :: +#bss_load_test=12:80:20000 + +# Multicast to unicast conversion +# Request that the AP will do multicast-to-unicast conversion for ARP, IPv4, and +# IPv6 frames (possibly within 802.1Q). If enabled, such frames are to be sent +# to each station separately, with the DA replaced by their own MAC address +# rather than the group address. +# +# Note that this may break certain expectations of the receiver, such as the +# ability to drop unicast IP packets received within multicast L2 frames, or the +# ability to not send ICMP destination unreachable messages for packets received +# in L2 multicast (which is required, but the receiver can't tell the difference +# if this new option is enabled). +# +# This also doesn't implement the 802.11 DMS (directed multicast service). +# +#multicast_to_unicast=0 + +# Send broadcast Deauthentication frame on AP start/stop +# Default: 1 (enabled) +#broadcast_deauth=1 + +# 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 +# 0 = disabled (default) +# 1 = enabled +# Note: You will also need to enable WMM for full HT functionality. +# 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 +# channel below the primary channel; [HT40+] = both 20 MHz and 40 MHz +# with secondary channel above the primary channel +# (20 MHz only if neither is set) +# Note: There are limits on which channels can be used with HT40- and +# HT40+. Following table shows the channels that may be available for +# HT40- and HT40+ use per IEEE 802.11n Annex J: +# freq HT40- HT40+ +# 2.4 GHz 5-13 1-7 (1-9 in Europe/Japan) +# 5 GHz 40,48,56,64 36,44,52,60 +# (depending on the location, not all of these channels may be available +# for use) +# Please note that 40 MHz channels may switch their primary and secondary +# 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. +# 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) +# Tx STBC: [TX-STBC] (disabled if not set) +# Rx STBC: [RX-STBC1] (one spatial stream), [RX-STBC12] (one or two spatial +# streams), or [RX-STBC123] (one, two, or three spatial streams); Rx STBC +# disabled if none of these set +# HT-delayed Block Ack: [DELAYED-BA] (disabled if not set) +# Maximum A-MSDU length: [MAX-AMSDU-7935] for 7935 octets (3839 octets if not +# set) +# DSSS/CCK Mode in 40 MHz: [DSSS_CCK-40] = allowed (not allowed if not set) +# 40 MHz intolerant [40-INTOLERANT] (not advertised if not set) +# L-SIG TXOP protection support: [LSIG-TXOP-PROT] (disabled if not set) +#ht_capab=[HT40-][SHORT-GI-20][SHORT-GI-40] + +# Require stations to support HT PHY (reject association if they do not) +#require_ht=1 + +# If set non-zero, require stations to perform scans of overlapping +# channels to test for stations which would be affected by 40 MHz traffic. +# This parameter sets the interval in seconds between these scans. Setting this +# to non-zero allows 2.4 GHz band AP to move dynamically to a 40 MHz channel if +# no co-existence issues with neighboring devices are found. +#obss_interval=0 + +# ht_vht_twt_responder: Whether TWT responder is enabled in HT and VHT modes +# 0 = disable; Disable TWT responder support in HT and VHT modes (default). +# 1 = enable; Enable TWT responder support in HT and VHT modes if supported by +# the driver. +#ht_vht_twt_responder=0 + +##### IEEE 802.11ac related configuration ##################################### + +# ieee80211ac: Whether IEEE 802.11ac (VHT) is enabled +# 0 = disabled (default) +# 1 = enabled +# Note: You will also need to enable WMM for full VHT functionality. +# 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] +# Indicates maximum MPDU length +# 0 = 3895 octets (default) +# 1 = 7991 octets +# 2 = 11454 octets +# 3 = reserved +# +# supported_chan_width: [VHT160] [VHT160-80PLUS80] +# Indicates supported Channel widths +# 0 = 160 MHz & 80+80 channel widths are not supported (default) +# 1 = 160 MHz channel width is supported +# 2 = 160 MHz & 80+80 channel widths are supported +# 3 = reserved +# +# Rx LDPC coding capability: [RXLDPC] +# Indicates support for receiving LDPC coded pkts +# 0 = Not supported (default) +# 1 = Supported +# +# Short GI for 80 MHz: [SHORT-GI-80] +# Indicates short GI support for reception of packets transmitted with TXVECTOR +# params format equal to VHT and CBW = 80Mhz +# 0 = Not supported (default) +# 1 = Supported +# +# Short GI for 160 MHz: [SHORT-GI-160] +# Indicates short GI support for reception of packets transmitted with TXVECTOR +# params format equal to VHT and CBW = 160Mhz +# 0 = Not supported (default) +# 1 = Supported +# +# Tx STBC: [TX-STBC-2BY1] +# Indicates support for the transmission of at least 2x1 STBC +# 0 = Not supported (default) +# 1 = Supported +# +# Rx STBC: [RX-STBC-1] [RX-STBC-12] [RX-STBC-123] [RX-STBC-1234] +# Indicates support for the reception of PPDUs using STBC +# 0 = Not supported (default) +# 1 = support of one spatial stream +# 2 = support of one and two spatial streams +# 3 = support of one, two and three spatial streams +# 4 = support of one, two, three and four spatial streams +# 5,6,7 = reserved +# +# SU Beamformer Capable: [SU-BEAMFORMER] +# Indicates support for operation as a single user beamformer +# 0 = Not supported (default) +# 1 = Supported +# +# SU Beamformee Capable: [SU-BEAMFORMEE] +# Indicates support for operation as a single user beamformee +# 0 = Not supported (default) +# 1 = Supported +# +# Compressed Steering Number of Beamformer Antennas Supported: +# [BF-ANTENNA-2] [BF-ANTENNA-3] [BF-ANTENNA-4] +# Beamformee's capability indicating the maximum number of beamformer +# antennas the beamformee can support when sending compressed beamforming +# feedback +# If SU beamformer capable, set to maximum value minus 1 +# else reserved (default) +# +# Number of Sounding Dimensions: +# [SOUNDING-DIMENSION-2] [SOUNDING-DIMENSION-3] [SOUNDING-DIMENSION-4] +# Beamformer's capability indicating the maximum value of the NUM_STS parameter +# in the TXVECTOR of a VHT NDP +# If SU beamformer capable, set to maximum value minus 1 +# else reserved (default) +# +# MU Beamformer Capable: [MU-BEAMFORMER] +# Indicates support for operation as an MU beamformer +# 0 = Not supported or sent by Non-AP STA (default) +# 1 = Supported +# +# VHT TXOP PS: [VHT-TXOP-PS] +# Indicates whether or not the AP supports VHT TXOP Power Save Mode +# or whether or not the STA is in VHT TXOP Power Save mode +# 0 = VHT AP doesn't support VHT TXOP PS mode (OR) VHT STA not in VHT TXOP PS +# mode +# 1 = VHT AP supports VHT TXOP PS mode (OR) VHT STA is in VHT TXOP power save +# mode +# +# +HTC-VHT Capable: [HTC-VHT] +# Indicates whether or not the STA supports receiving a VHT variant HT Control +# field. +# 0 = Not supported (default) +# 1 = supported +# +# Maximum A-MPDU Length Exponent: [MAX-A-MPDU-LEN-EXP0]..[MAX-A-MPDU-LEN-EXP7] +# Indicates the maximum length of A-MPDU pre-EOF padding that the STA can recv +# 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 +# +# VHT Link Adaptation Capable: [VHT-LINK-ADAPT2] [VHT-LINK-ADAPT3] +# Indicates whether or not the STA supports link adaptation using VHT variant +# HT Control field +# If +HTC-VHTcapable is 1 +# 0 = (no feedback) if the STA does not provide VHT MFB (default) +# 1 = reserved +# 2 = (Unsolicited) if the STA provides only unsolicited VHT MFB +# 3 = (Both) if the STA can provide VHT MFB in response to VHT MRQ and if the +# STA provides unsolicited VHT MFB +# Reserved if +HTC-VHTcapable is 0 +# +# Rx Antenna Pattern Consistency: [RX-ANTENNA-PATTERN] +# 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 +# +# Tx Antenna Pattern Consistency: [TX-ANTENNA-PATTERN] +# 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 +#vht_capab=[SHORT-GI-80][HTC-VHT] +# +# Require stations to support VHT PHY (reject association if they do not) +#require_vht=1 + +# 0 = 20 or 40 MHz operating Channel width +# 1 = 80 MHz channel width +# 2 = 160 MHz channel width +# 3 = 80+80 MHz channel width +#vht_oper_chwidth=1 +# +# center freq = 5 GHz + (5 * index) +# So index 42 gives center freq 5.210 GHz +# which is channel 42 in 5G band +# +#vht_oper_centr_freq_seg0_idx=42 +# +# center freq = 5 GHz + (5 * index) +# So index 159 gives center freq 5.795 GHz +# which is channel 159 in 5G band +# +#vht_oper_centr_freq_seg1_idx=159 + +# Workaround to use station's nsts capability in (Re)Association Response frame +# This may be needed with some deployed devices as an interoperability +# workaround for beamforming if the AP's capability is greater than the +# station's capability. This is disabled by default and can be enabled by +# setting use_sta_nsts=1. +#use_sta_nsts=0 + +##### IEEE 802.11ax related configuration ##################################### + +#ieee80211ax: Whether IEEE 802.11ax (HE) is enabled +# 0 = disabled (default) +# 1 = enabled +#ieee80211ax=1 + +# Require stations to support HE PHY (reject association if they do not) +#require_he=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 +#he_su_beamformer=1 + +#he_su_beamformee: HE single user beamformee support +# 0 = not supported (default) +# 1 = supported +#he_su_beamformee=1 + +#he_mu_beamformer: HE multiple user beamformer support +# 0 = not supported (default) +# 1 = supported +#he_mu_beamformer=1 + +# he_bss_color: BSS color (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 + +#he_twt_required: Whether TWT is required +# 0 = not required (default) +# 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_er_su_disable: Disable 242-tone HE ER SU PPDU reception by the AP +# 0 = enable reception (default) +# 1 = disable reception +#he_er_su_disable=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 (see vht_oper_chwidth) +#he_oper_centr_freq_seg0_idx +#he_oper_centr_freq_seg1_idx + +#he_basic_mcs_nss_set: Basic NSS/MCS set +# 16-bit combination of 2-bit values of Max HE-MCS For 1..8 SS; each 2-bit +# value having following meaning: +# 0 = HE-MCS 0-7, 1 = HE-MCS 0-9, 2 = HE-MCS 0-11, 3 = not supported +#he_basic_mcs_nss_set + +#he_mu_edca_qos_info_param_count +#he_mu_edca_qos_info_q_ack +#he_mu_edca_qos_info_queue_request=1 +#he_mu_edca_qos_info_txop_request +#he_mu_edca_ac_be_aifsn=0 +#he_mu_edca_ac_be_ecwmin=15 +#he_mu_edca_ac_be_ecwmax=15 +#he_mu_edca_ac_be_timer=255 +#he_mu_edca_ac_bk_aifsn=0 +#he_mu_edca_ac_bk_aci=1 +#he_mu_edca_ac_bk_ecwmin=15 +#he_mu_edca_ac_bk_ecwmax=15 +#he_mu_edca_ac_bk_timer=255 +#he_mu_edca_ac_vi_ecwmin=15 +#he_mu_edca_ac_vi_ecwmax=15 +#he_mu_edca_ac_vi_aifsn=0 +#he_mu_edca_ac_vi_aci=2 +#he_mu_edca_ac_vi_timer=255 +#he_mu_edca_ac_vo_aifsn=0 +#he_mu_edca_ac_vo_aci=3 +#he_mu_edca_ac_vo_ecwmin=15 +#he_mu_edca_ac_vo_ecwmax=15 +#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 + +# 6 GHz Access Point type +# This config is to set the 6 GHz Access Point type. Possible options are: +# 0 = Indoor AP +# 1 = Standard power AP +# 2 = Very low power AP (default) +# 3 = Indoor enabled AP +# 4 = Indoor standard power AP +# This has no impact for operation on other bands. +# See IEEE P802.11-REVme/D4.0, Table E-12 (Regulatory Info subfield encoding) +# for more details. +#he_6ghz_reg_pwr_type=0 +# +# 6 GHz Maximum Tx Power used in Transmit Power Envelope elements, where the +# "Transmit Power Interpretation" is set to "Regulatory client EIRP PSD". +# For Maximum Transmit Power Category subfield encoding set to default (0): +#reg_def_cli_eirp_psd=-1 +# For Maximum Transmit Power Category subfield encoding set to subordinate (1): +#reg_sub_cli_eirp_psd=-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.11be related configuration ##################################### + +#ieee80211be: Whether IEEE 802.11be (EHT) is enabled +# 0 = disabled (default) +# 1 = enabled +#ieee80211be=1 + +#disable_11be: Boolean (0/1) to disable EHT for a specific BSS +#disable_11be=0 + +#eht_su_beamformer: EHT single user beamformer support +# 0 = not supported (default) +# 1 = supported +#eht_su_beamformer=1 + +#eht_su_beamformee: EHT single user beamformee support +# 0 = not supported (default) +# 1 = supported +#eht_su_beamformee=1 + +#eht_mu_beamformer: EHT multiple user beamformer support +# 0 = not supported (default) +# 1 = supported +#eht_mu_beamformer=1 + +# EHT operating channel information; see matching he_* parameters for details. +# The field eht_oper_centr_freq_seg0_idx field is used to indicate center +# frequency of 40, 80, and 160 MHz bandwidth operation. +# In the 6 GHz band, eht_oper_chwidth is ignored and the channel width is +# derived from the configured operating class (IEEE P802.11be/D1.5, +# Annex E.1 - Country information and operating classes). +#eht_oper_chwidth (see vht_oper_chwidth) +#eht_oper_centr_freq_seg0_idx + +#eht_default_pe_duration: The duration of PE field in EHT TB PPDU +# 0 = PE field duration is the same as he_default_pe_duration (default) +# 1 = PE field duration is 20 us +#eht_default_pe_duration=0 + +#eht_bw320_offset: For automatic channel selection (ACS) to indicate a preferred +# 320 MHz channelization in EHT mode. +# If the channel is decided or the bandwidth is not 320 MHz, this option is +# meaningless. +# 0 = auto-detect by hostapd +# 1 = 320 MHz-1 (channel center frequency 31, 95, 159) +# 2 = 320 MHz-2 (channel center frequency 63, 127, 191) +#eht_bw320_offset=0 + +# Disabled subchannel bitmap (16 bits) as per IEEE P802.11be/3.0, +# Figure 9-1002c (EHT Operation Information field format). Each bit corresponds +# to a 20 MHz channel, the lowest bit corresponds to the lowest frequency. A +# bit set to 1 indicates that the channel is punctured (disabled). The default +# value is 0 indicating that all channels are active. +#punct_bitmap=0 + +# Preamble puncturing threshold in automatic channel selection (ACS). +# The value indicates the percentage of ideal channel average interference +# factor above which a channel should be punctured. +# Default is 0, indicates that ACS algorithm should not puncture any channel. +#punct_acs_threshold=75 + +# AP MLD - Whether this AP is a part of an AP MLD +# 0 = no (no MLO) +# 1 = yes (MLO) +#mld_ap=0 + +# AP MLD MAC address +# The configured address will be set as the interface hardware address and used +# as the AP MLD MAC address. If not set, the current interface hardware address +# will be used as the AP MLD MAC address. +#mld_addr=02:03:04:05:06:07 + +##### IEEE 802.1X-2004 related configuration ################################## + +# Require IEEE 802.1X authorization +#ieee8021x=1 + +# IEEE 802.1X/EAPOL version +# hostapd is implemented based on IEEE Std 802.1X-2004 which defines EAPOL +# version 2. However, there are many client implementations that do not handle +# the new version number correctly (they seem to drop the frames completely). +# In order to make hostapd interoperate with these clients, the version number +# can be set to the older version (1) with this configuration value. +# Note: When using MACsec, eapol_version shall be set to 3, which is +# defined in IEEE Std 802.1X-2010. +#eapol_version=2 + +# Optional displayable message sent with EAP Request-Identity. The first \0 +# in this string will be converted to ASCII-0 (nul). This can be used to +# separate network info (comma separated list of attribute=value pairs); see, +# e.g., RFC 4284. +#eap_message=hello +#eap_message=hello\0networkid=netw,nasid=foo,portid=0,NAIRealms=example.com + +# WEP rekeying (disabled if key lengths are not set or are set to 0) +# Key lengths for default/broadcast and individual/unicast keys: +# 5 = 40-bit WEP (also known as 64-bit WEP with 40 secret bits) +# 13 = 104-bit WEP (also known as 128-bit WEP with 104 secret bits) +#wep_key_len_broadcast=5 +#wep_key_len_unicast=5 +# Rekeying period in seconds. 0 = do not rekey (i.e., set keys only once) +#wep_rekey_period=300 + +# EAPOL-Key index workaround (set bit7) for WinXP Supplicant (needed only if +# only broadcast keys are used) +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 +# address when sending EAPOL frames with driver=wired. This is the most common +# mechanism used in wired authentication, but it also requires that the port +# is only used by one station. +#use_pae_group_addr=1 + +# EAP Re-authentication Protocol (ERP) authenticator (RFC 6696) +# +# Whether to initiate EAP authentication with EAP-Initiate/Re-auth-Start before +# EAP-Identity/Request +#erp_send_reauth_start=1 +# +# Domain name for EAP-Initiate/Re-auth-Start. Omitted from the message if not +# set (no local ER server). This is also used by the integrated EAP server if +# ERP is enabled (eap_server_erp=1). +#erp_domain=example.com + +##### MACsec ################################################################## + +# macsec_policy: IEEE 802.1X/MACsec options +# This determines how sessions are secured with MACsec (only for MACsec +# drivers). +# 0: MACsec not in use (default) +# 1: MACsec enabled - Should secure, accept key server's advice to +# determine whether to use a secure session or not. +# +# macsec_integ_only: IEEE 802.1X/MACsec transmit mode +# This setting applies only when MACsec is in use, i.e., +# - macsec_policy is enabled +# - the key server has decided to enable MACsec +# 0: Encrypt traffic (default) +# 1: Integrity only +# +# macsec_replay_protect: IEEE 802.1X/MACsec replay protection +# This setting applies only when MACsec is in use, i.e., +# - macsec_policy is enabled +# - the key server has decided to enable MACsec +# 0: Replay protection disabled (default) +# 1: Replay protection enabled +# +# macsec_replay_window: IEEE 802.1X/MACsec replay protection window +# This determines a window in which replay is tolerated, to allow receipt +# of frames that have been misordered by the network. +# This setting applies only when MACsec replay protection active, i.e., +# - macsec_replay_protect is enabled +# - the key server has decided to enable MACsec +# 0: No replay window, strict check (default) +# 1..2^32-1: number of packets that could be misordered +# +# macsec_offload: IEEE 802.1X/MACsec hardware offload +# This setting applies only when MACsec is in use, i.e., +# - macsec_policy is enabled +# - the key server has decided to enable MACsec +# 0 = MACSEC_OFFLOAD_OFF (default) +# 1 = MACSEC_OFFLOAD_PHY +# 2 = MACSEC_OFFLOAD_MAC +# +# macsec_port: IEEE 802.1X/MACsec port +# Port component of the SCI +# Range: 1-65534 (default: 1) +# +# mka_priority (Priority of MKA Actor) +# Range: 0..255 (default: 255) +# +# macsec_csindex: IEEE 802.1X/MACsec cipher suite +# 0 = GCM-AES-128 (default) +# 1 = GCM-AES-256 (default) +# +# mka_cak, mka_ckn, and mka_priority: IEEE 802.1X/MACsec pre-shared key mode +# This allows to configure MACsec with a pre-shared key using a (CAK,CKN) pair. +# In this mode, instances of hostapd can act as MACsec peers. The peer +# with lower priority will become the key server and start distributing SAKs. +# mka_cak (CAK = Secure Connectivity Association Key) takes a 16-byte (128-bit) +# hex-string (32 hex-digits) or a 32-byte (256-bit) hex-string (64 hex-digits) +# mka_ckn (CKN = CAK Name) takes a 1..32-bytes (8..256 bit) hex-string +# (2..64 hex-digits) + +##### Integrated EAP server ################################################### + +# Optionally, hostapd can be configured to use an integrated EAP server +# to process EAP authentication locally without need for an external RADIUS +# server. This functionality can be used both as a local authentication server +# for IEEE 802.1X/EAPOL and as a RADIUS server for other devices. + +# Use integrated EAP server instead of external RADIUS authentication +# server. This is also needed if hostapd is configured to act as a RADIUS +# authentication server. +eap_server=0 + +# Path for EAP server user database +# If SQLite support is included, this can be set to "sqlite:/path/to/sqlite.db" +# to use SQLite database instead of a text file. +#eap_user_file=/etc/hostapd.eap_user + +# CA certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS +#ca_cert=/etc/hostapd.ca.pem + +# Server certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS +#server_cert=/etc/hostapd.server.pem + +# Private key matching with the server certificate for EAP-TLS/PEAP/TTLS +# This may point to the same file as server_cert if both certificate and key +# are included in a single file. PKCS#12 (PFX) file (.p12/.pfx) can also be +# used by commenting out server_cert and specifying the PFX file as the +# private_key. +#private_key=/etc/hostapd.server.prv + +# Passphrase for private key +#private_key_passwd=secret passphrase + +# An alternative server certificate and private key can be configured with the +# following parameters (with values just like the parameters above without the +# '2' suffix). The ca_cert file (in PEM encoding) is used to add the trust roots +# for both server certificates and/or client certificates). +# +# The main use case for this alternative server certificate configuration is to +# enable both RSA and ECC public keys. The server will pick which one to use +# based on the client preferences for the cipher suite (in the TLS ClientHello +# message). It should be noted that number of deployed EAP peer implementations +# do not filter out the cipher suite list based on their local configuration and +# as such, configuration of alternative types of certificates on the server may +# result in interoperability issues. +#server_cert2=/etc/hostapd.server-ecc.pem +#private_key2=/etc/hostapd.server-ecc.prv +#private_key_passwd2=secret passphrase + + +# Server identity +# EAP methods that provide mechanism for authenticated server identity delivery +# use this value. If not set, "hostapd" is used as a default. +#server_id=server.example.com + +# Enable CRL verification. +# Note: hostapd does not yet support CRL downloading based on CDP. Thus, a +# valid CRL signed by the CA is required to be included in the ca_cert file. +# This can be done by using PEM format for CA certificate and CRL and +# concatenating these into one file. Whenever CRL changes, hostapd needs to be +# restarted to take the new CRL into use. Alternatively, crl_reload_interval can +# be used to configure periodic updating of the loaded CRL information. +# 0 = do not verify CRLs (default) +# 1 = check the CRL of the user certificate +# 2 = check all CRLs in the certificate path +#check_crl=1 + +# Specify whether to ignore certificate CRL validity time mismatches with +# errors X509_V_ERR_CRL_HAS_EXPIRED and X509_V_ERR_CRL_NOT_YET_VALID. +# +# 0 = ignore errors +# 1 = do not ignore errors (default) +#check_crl_strict=1 + +# CRL reload interval in seconds +# This can be used to reload ca_cert file and the included CRL on every new TLS +# session if difference between last reload and the current reload time in +# seconds is greater than crl_reload_interval. +# Note: If interval time is very short, CPU overhead may be negatively affected +# and it is advised to not go below 300 seconds. +# This is applicable only with check_crl values 1 and 2. +# 0 = do not reload CRLs (default) +# crl_reload_interval = 300 + +# If check_cert_subject is set, the value of every field will be checked +# against the DN of the subject in the client certificate. If the values do +# not match, the certificate verification will fail, rejecting the user. +# This option allows hostapd to match every individual field in the right order +# against the DN of the subject in the client certificate. +# +# For example, check_cert_subject=C=US/O=XX/OU=ABC/OU=XYZ/CN=1234 will check +# every individual DN field of the subject in the client certificate. If OU=XYZ +# comes first in terms of the order in the client certificate (DN field of +# client certificate C=US/O=XX/OU=XYZ/OU=ABC/CN=1234), hostapd will reject the +# client because the order of 'OU' is not matching the specified string in +# check_cert_subject. +# +# This option also allows '*' as a wildcard. This option has some limitation. +# It can only be used as per the following example. +# +# For example, check_cert_subject=C=US/O=XX/OU=Production* and we have two +# clients and DN of the subject in the first client certificate is +# (C=US/O=XX/OU=Production Unit) and DN of the subject in the second client is +# (C=US/O=XX/OU=Production Factory). In this case, hostapd will allow both +# clients because the value of 'OU' field in both client certificates matches +# 'OU' value in 'check_cert_subject' up to 'wildcard'. +# +# * (Allow all clients, e.g., check_cert_subject=*) +#check_cert_subject=string + +# TLS Session Lifetime in seconds +# This can be used to allow TLS sessions to be cached and resumed with an +# abbreviated handshake when using EAP-TLS/TTLS/PEAP. +# (default: 0 = session caching and resumption disabled) +#tls_session_lifetime=3600 + +# TLS flags +# [ALLOW-SIGN-RSA-MD5] = allow MD5-based certificate signatures (depending on +# the TLS library, these may be disabled by default to enforce stronger +# security) +# [DISABLE-TIME-CHECKS] = ignore certificate validity time (this requests +# the TLS library to accept certificates even if they are not currently +# valid, i.e., have expired or have not yet become valid; this should be +# used only for testing purposes) +# [DISABLE-TLSv1.0] = disable use of TLSv1.0 +# [ENABLE-TLSv1.0] = explicitly enable use of TLSv1.0 (this allows +# systemwide TLS policies to be overridden) +# [DISABLE-TLSv1.1] = disable use of TLSv1.1 +# [ENABLE-TLSv1.1] = explicitly enable use of TLSv1.1 (this allows +# systemwide TLS policies to be overridden) +# [DISABLE-TLSv1.2] = disable use of TLSv1.2 +# [ENABLE-TLSv1.2] = explicitly enable use of TLSv1.2 (this allows +# systemwide TLS policies to be overridden) +# [DISABLE-TLSv1.3] = disable use of TLSv1.3 +# [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. +# This cache file can be updated, e.g., by running following command +# periodically to get an update from the OCSP responder: +# openssl ocsp \ +# -no_nonce \ +# -CAfile /etc/hostapd.ca.pem \ +# -issuer /etc/hostapd.ca.pem \ +# -cert /etc/hostapd.server.pem \ +# -url http://ocsp.example.com:8888/ \ +# -respout /tmp/ocsp-cache.der +#ocsp_stapling_response=/tmp/ocsp-cache.der + +# Cached OCSP stapling response list (DER encoded OCSPResponseList) +# This is similar to ocsp_stapling_response, but the extended version defined in +# RFC 6961 to allow multiple OCSP responses to be provided. +#ocsp_stapling_response_multi=/tmp/ocsp-multi-cache.der + +# dh_file: File path to DH/DSA parameters file (in PEM format) +# This is an optional configuration file for setting parameters for an +# ephemeral DH key exchange. If the file is in DSA parameters format, it will +# be automatically converted into DH params. If the used TLS library supports +# automatic DH parameter selection, that functionality will be used if this +# parameter is not set. DH parameters are required if anonymous EAP-FAST is +# used. +# You can generate DH parameters file with OpenSSL, e.g., +# "openssl dhparam -out /etc/hostapd.dh.pem 2048" +#dh_file=/etc/hostapd.dh.pem + +# OpenSSL cipher string +# +# This is an OpenSSL specific configuration option for configuring the default +# ciphers. If not set, the value configured at build time ("DEFAULT:!EXP:!LOW" +# by default) is used. +# See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation +# on cipher suite configuration. This is applicable only if hostapd is built to +# use OpenSSL. +#openssl_ciphers=DEFAULT:!EXP:!LOW + +# OpenSSL ECDH curves +# +# This is an OpenSSL specific configuration option for configuring the ECDH +# curves for EAP-TLS/TTLS/PEAP/FAST server. If not set, automatic curve +# selection is enabled. If set to an empty string, ECDH curve configuration is +# not done (the exact library behavior depends on the library version). +# Otherwise, this is a colon separated list of the supported curves (e.g., +# P-521:P-384:P-256). This is applicable only if hostapd is built to use +# OpenSSL. This must not be used for Suite B cases since the same OpenSSL +# parameter is set differently in those cases and this might conflict with that +# design. +#openssl_ecdh_curves=P-521:P-384:P-256 + +# Fragment size for EAP methods +#fragment_size=1400 + +# Finite cyclic group for EAP-pwd. Number maps to group of domain parameters +# using the IANA repository for IKE (RFC 2409). +#pwd_group=19 + +# Configuration data for EAP-SIM database/authentication gateway interface. +# This is a text string in implementation specific format. The example +# implementation in eap_sim_db.c uses this as the UNIX domain socket name for +# the HLR/AuC gateway (e.g., hlr_auc_gw). In this case, the path uses "unix:" +# prefix. If hostapd is built with SQLite support (CONFIG_SQLITE=y in .config), +# database file can be described with an optional db= parameter. +#eap_sim_db=unix:/tmp/hlr_auc_gw.sock +#eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=/tmp/hostapd.db + +# EAP-SIM DB request timeout +# This parameter sets the maximum time to wait for a database request response. +# The parameter value is in seconds. +#eap_sim_db_timeout=1 + +# Encryption key for EAP-FAST PAC-Opaque values. This key must be a secret, +# random value. It is configured as a 16-octet value in hex format. It can be +# generated, e.g., with the following command: +# od -tx1 -v -N16 /dev/random | colrm 1 8 | tr -d ' ' +#pac_opaque_encr_key=000102030405060708090a0b0c0d0e0f + +# EAP-FAST authority identity (A-ID) +# A-ID indicates the identity of the authority that issues PACs. The A-ID +# 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 provide interoperability with deployed peer implementations. This +# field is configured in hex format. +#eap_fast_a_id=101112131415161718191a1b1c1d1e1f + +# EAP-FAST authority identifier information (A-ID-Info) +# This is a user-friendly name for the A-ID. For example, the enterprise name +# and server name in a human-readable format. This field is encoded as UTF-8. +#eap_fast_a_id_info=test server + +# Enable/disable different EAP-FAST provisioning modes: +#0 = provisioning disabled +#1 = only anonymous provisioning allowed +#2 = only authenticated provisioning allowed +#3 = both provisioning modes allowed (default) +#eap_fast_prov=3 + +# EAP-FAST PAC-Key lifetime in seconds (hard limit) +#pac_key_lifetime=604800 + +# EAP-FAST PAC-Key refresh time in seconds (soft limit on remaining hard +# limit). The server will generate a new PAC-Key when this number of seconds +# (or fewer) of the lifetime remains. +#pac_key_refresh_time=86400 + +# 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 +# 0 = perform inner authentication (default) +# 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-TEAP tunneled EAP method behavior +# 0 = minimize roundtrips by merging start of the next EAP method with the +# crypto-binding of the previous one. +# 1 = complete crypto-binding before starting the next EAP method +#eap_teap_method_sequence=0 + +# EAP-SIM and EAP-AKA protected success/failure indication using AT_RESULT_IND +# (default: 0 = disabled). +#eap_sim_aka_result_ind=1 + +# EAP-SIM and EAP-AKA identity options +# 0 = do not use pseudonyms or fast reauthentication +# 1 = use pseudonyms, but not fast reauthentication +# 2 = do not use pseudonyms, but use fast reauthentication +# 3 = use pseudonyms and use fast reauthentication (default) +# 4 = do not use pseudonyms or fast reauthentication and allow +# EAP-Response/Identity to be used without method specific identity exchange +# 5 = use pseudonyms, but not fast reauthentication and allow +# EAP-Response/Identity to be used without method specific identity exchange +# 6 = do not use pseudonyms, but use fast reauthentication and allow +# EAP-Response/Identity to be used without method specific identity exchange +# 7 = use pseudonyms and use fast reauthentication and allow +# EAP-Response/Identity to be used without method specific identity exchange +#eap_sim_id=3 + +# IMSI privacy key (PEM encoded RSA 2048-bit private key) for decrypting +# permanent identity when using EAP-SIM/AKA/AKA'. +#imsi_privacy_key=imsi-privacy-key.pem + +# EAP-SIM and EAP-AKA fast re-authentication limit +# Maximum number of fast re-authentications allowed after each full +# authentication. +#eap_sim_aka_fast_reauth_limit=1000 + +# Trusted Network Connect (TNC) +# If enabled, TNC validation will be required before the peer is allowed to +# connect. Note: This is only used with EAP-TTLS and EAP-FAST. If any other +# EAP method is enabled, the peer will be allowed to connect without TNC. +#tnc=1 + +# EAP Re-authentication Protocol (ERP) - RFC 6696 +# +# Whether to enable ERP on the EAP server. +#eap_server_erp=1 + + +##### RADIUS client configuration ############################################# +# for IEEE 802.1X with external Authentication Server, IEEE 802.11 +# authentication with external ACL for MAC addresses, and accounting + +# The own IP address of the access point (used as NAS-IP-Address) +own_ip_addr=127.0.0.1 + +# NAS-Identifier string for RADIUS messages. When used, this should be unique +# to the NAS within the scope of the RADIUS server. Please note that hostapd +# uses a separate RADIUS client for each BSS and as such, a unique +# nas_identifier value should be configured separately for each BSS. This is +# particularly important for cases where RADIUS accounting is used +# (Accounting-On/Off messages are interpreted as clearing all ongoing sessions +# and that may get interpreted as applying to all BSSes if the same +# NAS-Identifier value is used.) For example, a fully qualified domain name +# prefixed with a unique identifier of the BSS (e.g., BSSID) can be used here. +# +# When using IEEE 802.11r, nas_identifier must be set and must be between 1 and +# 48 octets long. +# +# It is mandatory to configure either own_ip_addr or nas_identifier to be +# compliant with the RADIUS protocol. When using RADIUS accounting, it is +# strongly recommended that nas_identifier is set to a unique value for each +# BSS. +#nas_identifier=ap.example.com + +# RADIUS client forced local IP address for the access point +# Normally the local IP address is determined automatically based on configured +# IP addresses, but this field can be used to force a specific address to be +# 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 +#auth_server_shared_secret=secret + +# RADIUS accounting server +#acct_server_addr=127.0.0.1 +#acct_server_port=1813 +#acct_server_shared_secret=secret + +# Secondary RADIUS servers; to be used if primary one does not reply to +# RADIUS packets. These are optional and there can be more than one secondary +# server listed. +#auth_server_addr=127.0.0.2 +#auth_server_port=1812 +#auth_server_shared_secret=secret2 +# +#acct_server_addr=127.0.0.2 +#acct_server_port=1813 +#acct_server_shared_secret=secret2 + +# RADIUS/TLS instead of RADIUS/UDP +#auth_server_addr=127.0.0.1 +#auth_server_port=2083 +#auth_server_type=TLS +#auth_server_shared_secret=radsec +#auth_server_ca_cert= +#auth_server_client_cert= +#auth_server_private_key= +#auth_server_private_key_passwd= + +# Retry interval for trying to return to the primary RADIUS server (in +# seconds). RADIUS client code will automatically try to use the next server +# when the current server is not replying to requests. If this interval is set, +# primary server will be retried after configured amount of time even if the +# currently used secondary server is still working. +#radius_retry_primary_interval=600 + +# Message-Authenticator attribute requirement for non-EAP cases +# hostapd requires Message-Authenticator attribute to be included in all cases +# where RADIUS is used for EAP authentication. This is also required for cases +# where RADIUS is used for MAC ACL (macaddr_acl=2) by default, but that case +# can be configured to not require this for compatibility with RADIUS servers +# that do not include the attribute. This is not recommended due to potential +# security concerns, but can be used as a temporary workaround in networks where +# the connection to the RADIUS server is secure. +# 0 = Do not require Message-Authenticator in MAC ACL response +# 1 = Require Message-Authenticator in all authentication cases (default) +#radius_require_message_authenticator=1 + +# Interim accounting update interval +# If this is set (larger than 0) and acct_server is configured, hostapd will +# send interim accounting updates every N seconds. Note: if set, this overrides +# possible Acct-Interim-Interval attribute in Access-Accept message. Thus, this +# value should not be configured in hostapd.conf, if RADIUS server is used to +# control the interim interval. +# This value should not be less 600 (10 minutes) and must not be less than +# 60 (1 minute). +#radius_acct_interim_interval=600 + +# Request Chargeable-User-Identity (RFC 4372) +# This parameter can be used to configure hostapd to request CUI from the +# RADIUS server by including Chargeable-User-Identity attribute into +# Access-Request packets. +#radius_request_cui=1 + +# Dynamic VLAN mode; allow RADIUS authentication server to decide which VLAN +# is used for the stations. This information is parsed from following RADIUS +# attributes based on RFC 3580 and RFC 2868: Tunnel-Type (value 13 = VLAN), +# Tunnel-Medium-Type (value 6 = IEEE 802), Tunnel-Private-Group-ID (value +# VLANID as a string). Optionally, the local MAC ACL list (accept_mac_file) can +# be used to set static client MAC address to VLAN ID mapping. +# Dynamic VLAN mode is also used with VLAN ID assignment based on WPA/WPA2 +# passphrase from wpa_psk_file or vlan_id parameter from sae_password. +# 0 = disabled (default); only VLAN IDs from accept_mac_file will be used +# 1 = optional; use default interface if RADIUS server does not include VLAN ID +# 2 = required; reject authentication if RADIUS server does not include VLAN ID +#dynamic_vlan=0 + +# Per-Station AP_VLAN interface mode +# If enabled, each station is assigned its own AP_VLAN interface. +# This implies per-station group keying and ebtables filtering of inter-STA +# traffic (when passed through the AP). +# If the sta is not assigned to any VLAN, then its AP_VLAN interface will be +# added to the bridge given by the "bridge" configuration option (see above). +# Otherwise, it will be added to the per-VLAN bridge. +# 0 = disabled (default) +# 1 = enabled +#per_sta_vif=0 + +# VLAN interface list for dynamic VLAN mode is read from a separate text file. +# This list is used to map VLAN ID from the RADIUS server to a network +# interface. Each station is bound to one interface in the same way as with +# multiple BSSIDs or SSIDs. Each line in this text file is defining a new +# interface and the line must include VLAN ID and interface name separated by +# white space (space or tab). +# If no entries are provided by this file, the station is statically mapped +# to . interfaces. +# Each line can optionally also contain the name of a bridge to add the VLAN to +#vlan_file=/etc/hostapd.vlan + +# Interface where 802.1q tagged packets should appear when a RADIUS server is +# used to determine which VLAN a station is on. hostapd creates a bridge for +# each VLAN. Then hostapd adds a VLAN interface (associated with the interface +# indicated by 'vlan_tagged_interface') and the appropriate wireless interface +# to the bridge. +#vlan_tagged_interface=eth0 + +# Bridge (prefix) to add the wifi and the tagged interface to. This gets the +# VLAN ID appended. It defaults to brvlan%d if no tagged interface is given +# and br%s.%d if a tagged interface is given, provided %s = tagged interface +# and %d = VLAN ID. +#vlan_bridge=brvlan + +# When hostapd creates a VLAN interface on vlan_tagged_interfaces, it needs +# to know how to name it. +# 0 = vlan, e.g., vlan1 +# 1 = ., e.g. eth0.1 +#vlan_naming=0 + +# Arbitrary RADIUS attributes can be added into Access-Request and +# Accounting-Request packets by specifying the contents of the attributes with +# the following configuration parameters. There can be multiple of these to +# add multiple attributes. These parameters can also be used to override some +# of the attributes added automatically by hostapd. +# Format: [:] +# attr_id: RADIUS attribute type (e.g., 26 = Vendor-Specific) +# syntax: s = string (UTF-8), d = integer, x = octet string +# value: attribute value in format indicated by the syntax +# If syntax and value parts are omitted, a null value (single 0x00 octet) is +# used. +# +# Additional Access-Request attributes +# radius_auth_req_attr=[:] +# Examples: +# Operator-Name = "Operator" +#radius_auth_req_attr=126:s:Operator +# Service-Type = Framed (2) +#radius_auth_req_attr=6:d:2 +# Connect-Info = "testing" (this overrides the automatically generated value) +#radius_auth_req_attr=77:s:testing +# Same Connect-Info value set as a hexdump +#radius_auth_req_attr=77:x:74657374696e67 + +# +# Additional Accounting-Request attributes +# radius_acct_req_attr=[:] +# Examples: +# Operator-Name = "Operator" +#radius_acct_req_attr=126:s:Operator + +# If SQLite support is included, path to a database from which additional +# RADIUS request attributes are extracted based on the station MAC address. +# +# The schema for the radius_attributes table is: +# id | sta | reqtype | attr : multi-key (sta, reqtype) +# id = autonumber +# sta = station MAC address in `11:22:33:44:55:66` format. +# type = `auth` | `acct` | NULL (match any) +# attr = existing config file format, e.g. `126:s:Test Operator` +#radius_req_attr_sqlite=radius_attr.sqlite + +# Dynamic Authorization Extensions (RFC 5176) +# This mechanism can be used to allow dynamic changes to user session based on +# commands from a RADIUS server (or some other disconnect client that has the +# needed session information). For example, Disconnect message can be used to +# request an associated station to be disconnected. +# +# This is disabled by default. Set radius_das_port to non-zero UDP port +# number to enable. +#radius_das_port=3799 +# +# DAS client (the host that can send Disconnect/CoA requests) and shared secret +# Format: +# IP address 0.0.0.0 can be used to allow requests from any address. +#radius_das_client=192.168.1.123 shared secret here +# +# DAS Event-Timestamp time window in seconds +#radius_das_time_window=300 +# +# DAS require Event-Timestamp +#radius_das_require_event_timestamp=1 +# +# DAS require Message-Authenticator +#radius_das_require_message_authenticator=1 + +##### RADIUS authentication server configuration ############################## + +# hostapd can be used as a RADIUS authentication server for other hosts. This +# requires that the integrated EAP server is also enabled and both +# authentication services are sharing the same configuration. + +# File name of the RADIUS clients configuration for the RADIUS server. If this +# commented out, RADIUS server is disabled. +#radius_server_clients=/etc/hostapd.radius_clients + +# The UDP port number for the RADIUS authentication server +#radius_server_auth_port=1812 + +# The UDP port number for the RADIUS accounting server +# Commenting this out or setting this to 0 can be used to disable RADIUS +# accounting while still enabling RADIUS authentication. +#radius_server_acct_port=1813 + +# Use IPv6 with RADIUS server (IPv4 will also be supported using IPv6 API) +#radius_server_ipv6=1 + + +##### WPA/IEEE 802.11i configuration ########################################## + +# Enable WPA. Setting this variable configures the AP to require WPA (either +# WPA-PSK or WPA-RADIUS/EAP based on other configuration). For WPA-PSK, either +# wpa_psk or wpa_passphrase must be set and wpa_key_mgmt must include WPA-PSK. +# Instead of wpa_psk / wpa_passphrase, wpa_psk_radius might suffice. +# For WPA-RADIUS/EAP, ieee8021x must be set (but without dynamic WEP keys), +# RADIUS authentication server must be configured, and WPA-EAP must be included +# in wpa_key_mgmt. +# This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0) +# and/or WPA2 (full IEEE 802.11i/RSN): +# bit0 = WPA +# bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled) +# Note that WPA3 is also configured with bit1 since it uses RSN just like WPA2. +# In other words, for WPA3, wpa=2 is used the configuration (and +# wpa_key_mgmt=SAE for WPA3-Personal instead of wpa_key_mgmt=WPA-PSK). +#wpa=2 + +# 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 +# so the PSK changes when ASCII passphrase is used and the SSID is changed. +# wpa_psk (dot11RSNAConfigPSKValue) +# wpa_passphrase (dot11RSNAConfigPSKPassPhrase) +#wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef +#wpa_passphrase=secret passphrase + +# Optionally, WPA PSKs can be read from a separate text file (containing list +# of (PSK,MAC address) pairs. This allows more than one PSK to be configured. +# Use absolute path name to make sure that the files can be read on SIGHUP +# configuration reloads. +#wpa_psk_file=/etc/hostapd.wpa_psk + +# Optionally, WPA passphrase can be received from RADIUS authentication server +# This requires macaddr_acl to be set to 2 (RADIUS) for wpa_psk_radius values +# 1 and 2. +# 0 = disabled (default) +# 1 = optional; use default passphrase/psk if RADIUS server does not include +# Tunnel-Password +# 2 = required; reject authentication if RADIUS server does not include +# Tunnel-Password +# 3 = ask RADIUS server during 4-way handshake if there is no locally +# configured PSK/passphrase for the STA +# +# The Tunnel-Password attribute in Access-Accept can contain either the +# 8..63 character ASCII passphrase or a 64 hex character encoding of the PSK. +# +#wpa_psk_radius=0 + +# Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The +# entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be +# added to enable SHA256-based stronger algorithms. +# WPA-PSK = WPA-Personal / WPA2-Personal +# WPA-PSK-SHA256 = WPA2-Personal using SHA256 +# WPA-EAP = WPA-Enterprise / WPA2-Enterprise +# WPA-EAP-SHA256 = WPA2-Enterprise using SHA256 +# SAE = SAE (WPA3-Personal) +# WPA-EAP-SUITE-B-192 = WPA3-Enterprise with 192-bit security/CNSA suite +# FT-PSK = FT with passphrase/PSK +# FT-EAP = FT with EAP +# FT-EAP-SHA384 = FT with EAP using SHA384 +# FT-SAE = FT with SAE +# FILS-SHA256 = Fast Initial Link Setup with SHA256 +# FILS-SHA384 = Fast Initial Link Setup with SHA384 +# FT-FILS-SHA256 = FT and Fast Initial Link Setup with SHA256 +# FT-FILS-SHA384 = FT and Fast Initial Link Setup with SHA384 +# OWE = Opportunistic Wireless Encryption (a.k.a. Enhanced Open) +# DPP = Device Provisioning Protocol +# OSEN = Hotspot 2.0 online signup with encryption +# (dot11RSNAConfigAuthenticationSuitesTable) +#wpa_key_mgmt=WPA-PSK WPA-EAP + +# Set of accepted cipher suites (encryption algorithms) for pairwise keys +# (unicast packets). This is a space separated list of algorithms: +# CCMP = AES in Counter mode with CBC-MAC (CCMP-128) +# TKIP = Temporal Key Integrity Protocol +# CCMP-256 = AES in Counter mode with CBC-MAC with 256-bit key +# GCMP = Galois/counter mode protocol (GCMP-128) +# GCMP-256 = Galois/counter mode protocol with 256-bit key +# Group cipher suite (encryption algorithm for broadcast and multicast frames) +# is automatically selected based on this configuration. If only CCMP is +# allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise, +# TKIP will be used as the group cipher. The optional group_cipher parameter can +# be used to override this automatic selection. +# +# (dot11RSNAConfigPairwiseCiphersTable) +# Pairwise cipher for WPA (v1) (default: TKIP) +#wpa_pairwise=TKIP CCMP +# Pairwise cipher for RSN/WPA2 (default: use wpa_pairwise value) +#rsn_pairwise=CCMP + +# Optional override for automatic group cipher selection +# This can be used to select a specific group cipher regardless of which +# pairwise ciphers were enabled for WPA and RSN. It should be noted that +# overriding the group cipher with an unexpected value can result in +# interoperability issues and in general, this parameter is mainly used for +# testing purposes. +#group_cipher=CCMP + +# Time interval for rekeying GTK (broadcast/multicast encryption keys) in +# seconds. (dot11RSNAConfigGroupRekeyTime) +# This defaults to 86400 seconds (once per day) when using CCMP/GCMP as the +# group cipher and 600 seconds (once per 10 minutes) when using TKIP as the +# group cipher. +#wpa_group_rekey=86400 + +# Rekey GTK when any STA that possesses the current GTK is leaving the BSS. +# (dot11RSNAConfigGroupRekeyStrict) +#wpa_strict_rekey=1 + +# The number of times EAPOL-Key Message 1/2 in the RSN Group Key Handshake is +#retried per GTK Handshake attempt. (dot11RSNAConfigGroupUpdateCount) +# This value should only be increased when stations are constantly +# deauthenticated during GTK rekeying with the log message +# "group key handshake failed...". +# You should consider to also increase wpa_pairwise_update_count then. +# Range 1..4294967295; default: 4 +#wpa_group_update_count=4 + +# Time interval for rekeying GMK (master key used internally to generate GTKs +# (in seconds). +#wpa_gmk_rekey=86400 + +# 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) +# Range 1..4294967295; default: 4 +#wpa_pairwise_update_count=4 + +# Workaround for key reinstallation attacks +# +# This parameter can be used to disable retransmission of EAPOL-Key frames that +# are used to install keys (EAPOL-Key message 3/4 and group message 1/2). This +# is similar to setting wpa_group_update_count=1 and +# wpa_pairwise_update_count=1, but with no impact to message 1/4 and with +# extended timeout on the response to avoid causing issues with stations that +# may use aggressive power saving have very long time in replying to the +# EAPOL-Key messages. +# +# This option can be used to work around key reinstallation attacks on the +# station (supplicant) side in cases those station devices cannot be updated +# for some reason. By removing the retransmissions the attacker cannot cause +# key reinstallation with a delayed frame transmission. This is related to the +# station side vulnerabilities CVE-2017-13077, CVE-2017-13078, CVE-2017-13079, +# CVE-2017-13080, and CVE-2017-13081. +# +# This workaround might cause interoperability issues and reduced robustness of +# key negotiation especially in environments with heavy traffic load due to the +# number of attempts to perform the key exchange is reduced significantly. As +# such, this workaround is disabled by default (unless overridden in build +# configuration). To enable this, set the parameter to 1. +#wpa_disable_eapol_key_retries=1 + +# Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up +# roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN +# authentication and key handshake before actually associating with a new AP. +# (dot11RSNAPreauthenticationEnabled) +#rsn_preauth=1 +# +# Space separated list of interfaces from which pre-authentication frames are +# accepted (e.g., 'eth0' or 'eth0 wlan0wds0'. This list should include all +# interface that are used for connections to other APs. This could include +# wired interfaces and WDS links. The normal wireless data interface towards +# associated stations (e.g., wlan0) should not be added, since +# pre-authentication is only used with APs other than the currently associated +# one. +#rsn_preauth_interfaces=eth0 + +# ieee80211w: Whether management frame protection (MFP) is enabled +# 0 = disabled (default) +# 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) +# Other options (depending on driver support): +# BIP-GMAC-128 +# BIP-GMAC-256 +# BIP-CMAC-256 +# Note: All the stations connecting to the BSS will also need to support the +# selected cipher. The default AES-128-CMAC is the only option that is commonly +# 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 +#assoc_sa_query_max_timeout=1000 + +# Association SA Query retry timeout (in TU = 1.024 ms; for MFP) +# (time between two subsequent SA Query requests) +# dot11AssociationSAQueryRetryTimeout, 1...4294967295 +#assoc_sa_query_retry_timeout=201 + +# ocv: Operating Channel Validation +# This is a countermeasure against multi-channel on-path 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 +# This parameter can be used to disable caching of PMKSA created through EAP +# authentication. RSN preauthentication may still end up using PMKSA caching if +# it is enabled (rsn_preauth=1). +# 0 = PMKSA caching enabled (default) +# 1 = PMKSA caching disabled +#disable_pmksa_caching=0 + +# okc: Opportunistic Key Caching (aka Proactive Key Caching) +# Allow PMK cache to be shared opportunistically among configured interfaces +# and BSSes (i.e., all configurations within a single hostapd process). +# 0 = disabled (default) +# 1 = enabled +#okc=1 + +# SAE password +# This parameter can be used to set passwords for SAE. By default, the +# wpa_passphrase value is used if this separate parameter is not used, but +# wpa_passphrase follows the WPA-PSK constraints (8..63 characters) even though +# SAE passwords do not have such constraints. If the BSS enabled both SAE and +# WPA-PSK and both values are set, SAE uses the sae_password values and WPA-PSK +# uses the wpa_passphrase value. +# +# Each sae_password entry is added to a list of available passwords. This +# corresponds to the dot11RSNAConfigPasswordValueEntry. sae_password value +# starts with the password (dot11RSNAConfigPasswordCredential). That value can +# be followed by optional peer MAC address (dot11RSNAConfigPasswordPeerMac) and +# by optional password identifier (dot11RSNAConfigPasswordIdentifier). In +# addition, an optional VLAN ID specification can be used to bind the station +# 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 +# specific peer MAC address is included, only a station with that MAC address +# is allowed to use the entry. +# +# If the password identifier (with non-zero length) is included, the entry is +# limited to be used only with that specified identifier. + +# The last matching (based on peer MAC address and identifier) entry is used to +# select which password to use. Setting sae_password to an empty string has a +# special meaning of removing all previously added entries. +# +# sae_password uses the following encoding: +#[|mac=][|vlanid=] +#[|pk=][|id=] +# Examples: +#sae_password=secret +#sae_password=really secret|mac=ff:ff:ff:ff:ff:ff +#sae_password=example secret|mac=02:03:04:05:06:07|id=pw identifier +#sae_password=example secret|vlanid=3|id=pw identifier +# +# SAE passwords can also be read from a separate file in which each line +# contains and entry in the same format as sae_password uses. +#sae_password_file=/tc/hostapd.sae_passwords + +# 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 (deprecated) +#anti_clogging_threshold=5 + +# Maximum number of SAE synchronization errors (dot11RSNASAESync) +# The offending SAE peer will be disconnected if more than this many +# synchronization errors happen. +#sae_sync=3 + +# Enabled SAE finite cyclic groups +# SAE implementation are required to support group 19 (ECC group defined over a +# 256-bit prime order field). This configuration parameter can be used to +# specify a set of allowed groups. If not included, only the mandatory group 19 +# is enabled. +# The group values are listed in the IANA registry: +# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9 +# Note that groups 1, 2, 5, 22, 23, and 24 should not be used in production +# purposes due limited security (see RFC 8247). Groups that are not as strong as +# group 19 (ECC, NIST P-256) are unlikely to be useful for production use cases +# since all implementations are required to support group 19. +#sae_groups=19 20 21 + +# Require MFP for all associations using SAE +# This parameter can be used to enforce negotiation of MFP for all associations +# that negotiate use of SAE. This is used in cases where SAE-capable devices are +# known to be MFP-capable and the BSS is configured with optional MFP +# (ieee80211w=1) for legacy support. The non-SAE stations can connect without +# MFP while SAE stations are required to negotiate MFP if sae_require_mfp=1. +#sae_require_mfp=0 + +# 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 + +# FILS Realm Information +# One or more FILS realms need to be configured when FILS is enabled. This list +# of realms is used to define which realms (used in keyName-NAI by the client) +# can be used with FILS shared key authentication for ERP. +#fils_realm=example.com +#fils_realm=example.org + +# FILS DH Group for PFS +# 0 = PFS disabled with FILS shared key authentication (default) +# 1-65535 DH Group to use for FILS PFS +#fils_dh_group=0 + +# OWE DH groups +# OWE implementations are required to support group 19 (NIST P-256). All groups +# that are supported by the implementation (e.g., groups 19, 20, and 21 when +# using OpenSSL) are enabled by default. This configuration parameter can be +# used to specify a limited set of allowed groups. The group values are listed +# in the IANA registry: +# http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-10 +#owe_groups=19 20 21 + +# OWE 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= +# SSID in same format as ssid2 described above. +#owe_transition_ssid= +# Alternatively, OWE transition mode BSSID/SSID can be configured with a +# reference to a BSS operated by this hostapd process. +#owe_transition_ifname= + +# DHCP server for FILS HLP +# If configured, hostapd will act as a DHCP relay for all FILS HLP requests +# that include a DHCPDISCOVER message and send them to the specific DHCP +# server for processing. hostapd will then wait for a response from that server +# before replying with (Re)Association Response frame that encapsulates this +# DHCP response. own_ip_addr is used as the local address for the communication +# with the DHCP server. +#dhcp_server=127.0.0.1 + +# DHCP server UDP port +# Default: 67 +#dhcp_server_port=67 + +# DHCP relay UDP port on the local device +# Default: 67; 0 means not to bind any specific port +#dhcp_relay_port=67 + +# DHCP rapid commit proxy +# If set to 1, this enables hostapd to act as a DHCP rapid commit proxy to +# allow the rapid commit options (two message DHCP exchange) to be used with a +# server that supports only the four message DHCP exchange. This is disabled by +# default (= 0) and can be enabled by setting this to 1. +#dhcp_rapid_commit_proxy=0 + +# Wait time for FILS HLP (dot11HLPWaitTime) in TUs +# default: 30 TUs (= 30.72 milliseconds) +#fils_hlp_wait_time=30 + +# 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. +# This feature is currently supported only when ieee80211ax is enabled for +# the radio and disable_11ax is not set for the BSS. +#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 + +# Unauthenticated PASN activated (dot11NoAuthPASNActivated) +# This indicates whether PASN without mutual authentication is allowed. +# (default: 1 = activated) +#pasn_noauth=1 + +# SSID protection in 4-way handshake +# The IEEE 802.11i-2004 RSN design did not provide means for protecting the +# SSID in the general case. IEEE P802.11REVme/D6.0 added support for this in +# 4-way handshake. This capability allows a STA to confirm that the AP has the +# same understanding on which SSID is being used for an association in a +# protected manner in cases where both the AP and the STA has this capability. +# This can be used to mitigate CVE-2023-52424 (a.k.a. the SSID Confusion +# Attack). +# +# Ideally, this capability would be enabled by default on the AP, but since this +# is new functionality with limited testing, the default is to disable this for +# now and require explicitly configuration to enable. The default behavior is +# like to change once this capability has received more testing. +# +# 0 = SSID protection in 4-way handshake disabled (default) +# 1 = SSID protection in 4-way handshake enabled +# +#ssid_protection=0 + +##### IEEE 802.11r configuration ############################################## + +# Mobility Domain identifier (dot11FTMobilityDomainID, MDID) +# MDID is used to indicate a group of APs (within an ESS, i.e., sharing the +# same SSID) between which a STA can use Fast BSS Transition. +# 2-octet identifier as a hex string. +#mobility_domain=a1b2 + +# PMK-R0 Key Holder identifier (dot11FTR0KeyHolderID) +# 1 to 48 octet identifier. +# This is configured with nas_identifier (see RADIUS client section above). + +# Default lifetime of the PMK-R0 in seconds; range 60..4294967295 +# (default: 14 days / 1209600 seconds; 0 = disable timeout) +# (dot11FTR0KeyLifetime) +#ft_r0_key_lifetime=1209600 + +# Maximum lifetime for PMK-R1; applied only if not zero +# PMK-R1 is removed at latest after this limit. +# Removing any PMK-R1 for expiry can be disabled by setting this to -1. +# (default: 0) +#r1_max_key_lifetime=0 + +# PMK-R1 Key Holder identifier (dot11FTR1KeyHolderID) +# 6-octet identifier as a hex string. +# Defaults to BSSID. +#r1_key_holder=000102030405 + +# Reassociation deadline in time units (TUs / 1.024 ms; range 1000..65535) +# (dot11FTReassociationDeadline) +#reassociation_deadline=1000 + +# List of R0KHs in the same Mobility Domain +# format: <256-bit key as hex string> +# This list is used to map R0KH-ID (NAS Identifier) to a destination MAC +# address when requesting PMK-R1 key from the R0KH that the STA used during the +# Initial Mobility Domain Association. +#r0kh=02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f +#r0kh=02:01:02:03:04:06 r0kh-2.example.com 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff +# And so on.. One line per R0KH. +# Wildcard entry: +# Upon receiving a response from R0KH, it will be added to this list, so +# subsequent requests won't be broadcast. If R0KH does not reply, it will be +# temporarily blocked (see rkh_neg_timeout). +#r0kh=ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff + +# List of R1KHs in the same Mobility Domain +# format: <256-bit key as hex string> +# This list is used to map R1KH-ID to a destination MAC address when sending +# PMK-R1 key from the R0KH. This is also the list of authorized R1KHs in the MD +# that can request PMK-R1 keys. +#r1kh=02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f +#r1kh=02:01:02:03:04:06 02:11:22:33:44:66 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff +# And so on.. One line per R1KH. +# Wildcard entry: +# Upon receiving a request from an R1KH not yet known, it will be added to this +# list and thus will receive push notifications. +#r1kh=00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff + +# Optionally, the list of RxKHs can be read from a text file. Format is the same +# as specified above. File shall contain both r0kh and r1kh. Once this variable +# is set, RxKHs can be reloaded at runtime without bringing down an interface +# using the RELOAD_RXKHS command. +#rxkh_file= + +# Timeout (seconds) for newly discovered R0KH/R1KH (see wildcard entries above) +# Special values: 0 -> do not expire +# Warning: do not cache implies no sequence number validation with wildcards +#rkh_pos_timeout=86400 (default = 1 day) + +# Timeout (milliseconds) for requesting PMK-R1 from R0KH using PULL request +# and number of retries. +#rkh_pull_timeout=1000 (default = 1 second) +#rkh_pull_retries=4 (default) + +# Timeout (seconds) for non replying R0KH (see wildcard entries above) +# Special values: 0 -> do not cache +# default: 60 seconds +#rkh_neg_timeout=60 + +# Note: The R0KH/R1KH keys used to be 128-bit in length before the message +# format was changed. That shorter key length is still supported for backwards +# compatibility of the configuration files. If such a shorter key is used, a +# 256-bit key is derived from it. For new deployments, configuring the 256-bit +# key is recommended. + +# Whether PMK-R1 push is enabled at R0KH +# 0 = do not push PMK-R1 to all configured R1KHs (default) +# 1 = push PMK-R1 to all configured R1KHs whenever a new PMK-R0 is derived +#pmk_r1_push=1 + +# Whether to enable FT-over-DS +# 0 = FT-over-DS disabled +# 1 = FT-over-DS enabled (default) +#ft_over_ds=1 + +# Whether to generate FT response locally for PSK networks +# This avoids use of PMK-R1 push/pull from other APs with FT-PSK networks as +# the required information (PSK and other session data) is already locally +# available. +# 0 = disabled (default) +# 1 = enabled +#ft_psk_generate_local=0 + +##### Neighbor table ########################################################## +# Maximum number of entries kept in AP table (either for 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 +# enabled, so this field should not be set to 0 when using IEEE 802.11g. +# default: 255 +#ap_table_max_size=255 + +# Number of seconds of no frames received after which entries may be deleted +# from the AP table. Since passive scanning is not usually performed frequently +# this should not be set to very small value. In addition, there is no +# guarantee that every scan cycle will receive beacon frames from the +# neighboring APs. +# default: 60 +#ap_table_expiration_time=3600 + +# Maximum number of stations to track on the operating channel +# This can be used to detect dualband capable stations before they have +# associated, e.g., to provide guidance on which colocated BSS to use. +# Default: 0 (disabled) +#track_sta_max_num=100 + +# Maximum age of a station tracking entry in seconds +# Default: 180 +#track_sta_max_age=180 + +# Do not reply to group-addressed Probe Request from a station that was seen on +# another radio. +# Default: Disabled +# +# This can be used with enabled track_sta_max_num configuration on another +# interface controlled by the same hostapd process to restrict Probe Request +# frame handling from replying to group-addressed Probe Request frames from a +# station that has been detected to be capable of operating on another band, +# e.g., to try to reduce likelihood of the station selecting a 2.4 GHz BSS when +# the AP operates both a 2.4 GHz and 5 GHz BSS concurrently. +# +# Note: Enabling this can cause connectivity issues and increase latency for +# discovering the AP. +#no_probe_resp_if_seen_on=wlan1 + +# Reject authentication from a station that was seen on another radio. +# Default: Disabled +# +# This can be used with enabled track_sta_max_num configuration on another +# interface controlled by the same hostapd process to reject authentication +# attempts from a station that has been detected to be capable of operating on +# another band, e.g., to try to reduce likelihood of the station selecting a +# 2.4 GHz BSS when the AP operates both a 2.4 GHz and 5 GHz BSS concurrently. +# +# Note: Enabling this can cause connectivity issues and increase latency for +# connecting with the AP. +#no_auth_if_seen_on=wlan1 + +##### Wi-Fi Protected Setup (WPS) ############################################# + +# WPS state +# 0 = WPS disabled (default) +# 1 = WPS enabled, not configured +# 2 = WPS enabled, configured +#wps_state=2 + +# Whether to manage this interface independently from other WPS interfaces +# By default, a single hostapd process applies WPS operations to all configured +# interfaces. This parameter can be used to disable that behavior for a subset +# of interfaces. If this is set to non-zero for an interface, WPS commands +# issued on that interface do not apply to other interfaces and WPS operations +# performed on other interfaces do not affect this interface. +#wps_independent=0 + +# AP can be configured into a locked state where new WPS Registrar are not +# accepted, but previously authorized Registrars (including the internal one) +# can continue to add new Enrollees. +#ap_setup_locked=1 + +# Universally Unique IDentifier (UUID; see RFC 4122) of the device +# This value is used as the UUID for the internal WPS Registrar. If the AP +# is also using UPnP, this value should be set to the device's UPnP UUID. +# If not configured, UUID will be generated based on the local MAC address. +#uuid=12345678-9abc-def0-1234-56789abcdef0 + +# Note: If wpa_psk_file is set, WPS is used to generate random, per-device PSKs +# that will be appended to the wpa_psk_file. If wpa_psk_file is not set, the +# default PSK (wpa_psk/wpa_passphrase) will be delivered to Enrollees. Use of +# per-device PSKs is recommended as the more secure option (i.e., make sure to +# set wpa_psk_file when using WPS with WPA-PSK). + +# When an Enrollee requests access to the network with PIN method, the Enrollee +# PIN will need to be entered for the Registrar. PIN request notifications are +# sent to hostapd ctrl_iface monitor. In addition, they can be written to a +# text file that could be used, e.g., to populate the AP administration UI with +# pending PIN requests. If the following variable is set, the PIN requests will +# be written to the configured file. +#wps_pin_requests=/var/run/hostapd_wps_pin_requests + +# Device Name +# User-friendly description of device; up to 32 octets encoded in UTF-8 +#device_name=Wireless AP + +# Manufacturer +# The manufacturer of the device (up to 64 ASCII characters) +#manufacturer=Company + +# Model Name +# Model of the device (up to 32 ASCII characters) +#model_name=WAP + +# Model Number +# Additional device description (up to 32 ASCII characters) +#model_number=123 + +# Serial Number +# Serial number of the device (up to 32 characters) +#serial_number=12345 + +# Primary Device Type +# Used format: -- +# categ = Category as an integer value +# OUI = OUI and type octet as a 4-octet hex-encoded value; 0050F204 for +# default WPS OUI +# subcateg = OUI-specific Sub Category as an integer value +# Examples: +# 1-0050F204-1 (Computer / PC) +# 1-0050F204-2 (Computer / Server) +# 5-0050F204-1 (Storage / NAS) +# 6-0050F204-1 (Network Infrastructure / AP) +#device_type=6-0050F204-1 + +# OS Version +# 4-octet operating system version number (hex string) +#os_version=01020300 + +# Config Methods +# List of the supported configuration methods +# Available methods: usba ethernet label display ext_nfc_token int_nfc_token +# nfc_interface push_button keypad virtual_display physical_display +# virtual_push_button physical_push_button +#config_methods=label virtual_display virtual_push_button keypad + +# WPS capability discovery workaround for PBC with Windows 7 +# Windows 7 uses incorrect way of figuring out AP's WPS capabilities by acting +# as a Registrar and using M1 from the AP. The config methods attribute in that +# message is supposed to indicate only the configuration method supported by +# the AP in Enrollee role, i.e., to add an external Registrar. For that case, +# PBC shall not be used and as such, the PushButton config method is removed +# from M1 by default. If pbc_in_m1=1 is included in the configuration file, +# the PushButton config method is left in M1 (if included in config_methods +# parameter) to allow Windows 7 to use PBC instead of PIN (e.g., from a label +# in the AP). +#pbc_in_m1=1 + +# Static access point PIN for initial configuration and adding Registrars +# If not set, hostapd will not allow external WPS Registrars to control the +# access point. The AP PIN can also be set at runtime with hostapd_cli +# wps_ap_pin command. Use of temporary (enabled by user action) and random +# AP PIN is much more secure than configuring a static AP PIN here. As such, +# use of the ap_pin parameter is not recommended if the AP device has means for +# displaying a random PIN. +#ap_pin=12345670 + +# Skip building of automatic WPS credential +# This can be used to allow the automatically generated Credential attribute to +# be replaced with pre-configured Credential(s). +#skip_cred_build=1 + +# Additional Credential attribute(s) +# This option can be used to add pre-configured Credential attributes into M8 +# message when acting as a Registrar. If skip_cred_build=1, this data will also +# be able to override the Credential attribute that would have otherwise been +# automatically generated based on network configuration. This configuration +# option points to an external file that much contain the WPS Credential +# attribute(s) as binary data. +#extra_cred=hostapd.cred + +# Credential processing +# 0 = process received credentials internally (default) +# 1 = do not process received credentials; just pass them over ctrl_iface to +# external program(s) +# 2 = process received credentials internally and pass them over ctrl_iface +# to external program(s) +# Note: With wps_cred_processing=1, skip_cred_build should be set to 1 and +# extra_cred be used to provide the Credential data for Enrollees. +# +# wps_cred_processing=1 will disabled automatic updates of hostapd.conf file +# both for Credential processing and for marking AP Setup Locked based on +# validation failures of AP PIN. An external program is responsible on updating +# the configuration appropriately in this case. +#wps_cred_processing=0 + +# Whether to enable SAE (WPA3-Personal transition mode) automatically for +# WPA2-PSK credentials received using WPS. +# 0 = only add the explicitly listed WPA2-PSK configuration (default) +# 1 = add both the WPA2-PSK and SAE configuration and enable PMF so that the +# AP gets configured in WPA3-Personal transition mode (supports both +# WPA2-Personal (PSK) and WPA3-Personal (SAE) clients). +#wps_cred_add_sae=0 + +# AP Settings Attributes for M7 +# By default, hostapd generates the AP Settings Attributes for M7 based on the +# current configuration. It is possible to override this by providing a file +# with pre-configured attributes. This is similar to extra_cred file format, +# but the AP Settings attributes are not encapsulated in a Credential +# attribute. +#ap_settings=hostapd.ap_settings + +# Multi-AP backhaul BSS config +# Used in WPS when multi_ap=2 or 3. Defines "backhaul BSS" credentials. +# These are passed in WPS M8 instead of the normal (fronthaul) credentials +# if the Enrollee has the Multi-AP subelement set. Backhaul SSID is formatted +# like ssid2. The key is set like wpa_psk or wpa_passphrase. +#multi_ap_backhaul_ssid="backhaul" +#multi_ap_backhaul_wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef +#multi_ap_backhaul_wpa_passphrase=secret passphrase + +# Multi-AP Profile +# Indicate the supported Multi-AP profile (default: 2) +# 1 = Supports Multi-AP profile 1 as defined in Wi-Fi EasyMesh specification +# 2 = Supports Multi-AP profile 2 as defined in Wi-Fi EasyMesh specification +#multi_ap_profile=2 + +# Multi-AP client disallow +# Used to disallow profile specific backhaul STA association +# Bitmap of the disallowed Profile-X profiles +# 1 = Profile-1 Backhaul STA association disallowed +# 2 = Profile-2 Backhaul STA association disallowed +#multi_ap_client_disallow=0 + +# Multi-AP VLAN ID +# A valid non-zero VLAN ID will be used to update Default IEEE 802.1Q Setting +#multi_ap_vlanid=0 + +# WPS UPnP interface +# If set, support for external Registrars is enabled. +#upnp_iface=br0 + +# Friendly Name (required for UPnP) +# Short description for end use. Should be less than 64 characters. +#friendly_name=WPS Access Point + +# Manufacturer URL (optional for UPnP) +#manufacturer_url=http://www.example.com/ + +# Model Description (recommended for UPnP) +# Long description for end user. Should be less than 128 characters. +#model_description=Wireless Access Point + +# Model URL (optional for UPnP) +#model_url=http://www.example.com/model/ + +# Universal Product Code (optional for UPnP) +# 12-digit, all-numeric code that identifies the consumer package. +#upc=123456789012 + +# WPS RF Bands (a = 5G, b = 2.4G, g = 2.4G, ag = dual band, ad = 60 GHz) +# This value should be set according to RF band(s) supported by the AP if +# hw_mode is not set. For dual band dual concurrent devices, this needs to be +# set to ag to allow both RF bands to be advertized. +#wps_rf_bands=ag + +# NFC password token for WPS +# These parameters can be used to configure a fixed NFC password token for the +# AP. This can be generated, e.g., with nfc_pw_token from wpa_supplicant. When +# these parameters are used, the AP is assumed to be deployed with a NFC tag +# that includes the matching NFC password token (e.g., written based on the +# NDEF record from nfc_pw_token). +# +#wps_nfc_dev_pw_id: Device Password ID (16..65535) +#wps_nfc_dh_pubkey: Hexdump of DH Public Key +#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 +#manage_p2p=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 + +# JSON node name of additional data for Enrollee's DPP Configuration Request +#dpp_extra_conf_req_name=org.example + +# JSON node data of additional data for Enrollee's DPP Configuration Request +#dpp_extra_conf_req_value="abc":123 + +#dpp_connector +#dpp_netaccesskey +#dpp_netaccesskey_expiry +#dpp_csign +#dpp_controller + +# DPP Relay port number +# TCP port to listen to for incoming connections from a Controller. This can be +# used to allow Controller initiated exchanges in addition to the +# Controller-as-responder cases covered by the dpp_controller parameter. +#dpp_relay_port=12345 + +# 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 +#tdls_prohibit=1 + +# Prohibit use of TDLS Channel Switching in this BSS +#tdls_prohibit_chan_switch=1 + +##### IEEE 802.11v-2011 ####################################################### + +# Time advertisement +# 0 = disabled (default) +# 2 = UTC time at which the TSF timer is 0 +#time_advertisement=2 + +# Local time zone as specified in 8.3 of IEEE Std 1003.1-2004: +# stdoffset[dst[offset][,start[/time],end[/time]]] +#time_zone=EST5 + +# WNM-Sleep Mode (extended sleep mode for stations) +# 0 = disabled (default) +# 1 = enabled (allow stations to use WNM-Sleep Mode) +#wnm_sleep_mode=1 + +# WNM-Sleep Mode GTK/IGTK workaround +# Normally, WNM-Sleep Mode exit with management frame protection negotiated +# would result in the current GTK/IGTK getting added into the WNM-Sleep Mode +# Response frame. Some station implementations may have a vulnerability that +# results in GTK/IGTK reinstallation based on this frame being replayed. This +# configuration parameter can be used to disable that behavior and use EAPOL-Key +# frames for GTK/IGTK update instead. This would likely be only used with +# wpa_disable_eapol_key_retries=1 that enables a workaround for similar issues +# with EAPOL-Key. This is related to station side vulnerabilities CVE-2017-13087 +# and CVE-2017-13088. To enable this AP-side workaround, set the parameter to 1. +#wnm_sleep_mode_no_keys=0 + +# BSS Transition Management +# 0 = disabled (default) +# 1 = enabled +#bss_transition=1 + +# Proxy ARP +# 0 = disabled (default) +# 1 = enabled +#proxy_arp=1 + +# IPv6 Neighbor Advertisement multicast-to-unicast conversion +# This can be used with Proxy ARP to allow multicast NAs to be forwarded to +# associated STAs using link layer unicast delivery. +# 0 = disabled (default) +# 1 = enabled +#na_mcast_to_ucast=0 + +##### IEEE 802.11u-2011 ####################################################### + +# Enable Interworking service +#interworking=1 + +# Access Network Type +# 0 = Private network +# 1 = Private network with guest access +# 2 = Chargeable public network +# 3 = Free public network +# 4 = Personal device network +# 5 = Emergency services only network +# 14 = Test or experimental +# 15 = Wildcard +#access_network_type=0 + +# Whether the network provides connectivity to the Internet +# 0 = Unspecified +# 1 = Network provides connectivity to the Internet +#internet=1 + +# Additional Step Required for Access +# Note: This is only used with open network, i.e., ASRA shall ne set to 0 if +# RSN is used. +#asra=0 + +# Emergency services reachable +#esr=0 + +# Unauthenticated emergency service accessible +#uesa=0 + +# Venue Info (optional) +# The available values are defined in IEEE Std 802.11u-2011, 7.3.1.34. +# Example values (group,type): +# 0,0 = Unspecified +# 1,7 = Convention Center +# 1,13 = Coffee Shop +# 2,0 = Unspecified Business +# 7,1 Private Residence +#venue_group=7 +#venue_type=1 + +# Homogeneous ESS identifier (optional; dot11HESSID) +# If set, this shall be identifical to one of the BSSIDs in the homogeneous +# ESS and this shall be set to the same value across all BSSs in homogeneous +# ESS. +#hessid=02:03:04:05:06:07 + +# Roaming Consortium List +# Arbitrary number of Roaming Consortium OIs can be configured with each line +# adding a new OI to the list. The first three entries are available through +# Beacon and Probe Response frames. Any additional entry will be available only +# through ANQP queries. Each OI is between 3 and 15 octets and is configured as +# a hexstring. +#roaming_consortium=021122 +#roaming_consortium=2233445566 + +# Venue Name information +# This parameter can be used to configure one or more Venue Name Duples for +# Venue Name ANQP information. Each entry has a two or three character language +# code (ISO-639) separated by colon from the venue name string. +# Note that venue_group and venue_type have to be set for Venue Name +# information to be complete. +#venue_name=eng:Example venue +#venue_name=fin:Esimerkkipaikka +# Alternative format for language:value strings: +# (double quoted string, printf-escaped string) +#venue_name=P"eng:Example\nvenue" + +# Venue URL information +# This parameter can be used to configure one or more Venue URL Duples to +# provide additional information corresponding to Venue Name information. +# Each entry has a Venue Number value separated by colon from the Venue URL +# string. Venue Number indicates the corresponding venue_name entry (1 = 1st +# venue_name, 2 = 2nd venue_name, and so on; 0 = no matching venue_name) +#venue_url=1:http://www.example.com/info-eng +#venue_url=2:http://www.example.com/info-fin + +# Network Authentication Type +# This parameter indicates what type of network authentication is used in the +# network. +# format: [redirect URL] +# Network Authentication Type Indicator values: +# 00 = Acceptance of terms and conditions +# 01 = On-line enrollment supported +# 02 = http/https redirection +# 03 = DNS redirection +#network_auth_type=00 +#network_auth_type=02http://www.example.com/redirect/me/here/ + +# IP Address Type Availability +# format: <1-octet encoded value as hex str> +# (ipv4_type & 0x3f) << 2 | (ipv6_type & 0x3) +# ipv4_type: +# 0 = Address type not available +# 1 = Public IPv4 address available +# 2 = Port-restricted IPv4 address available +# 3 = Single NATed private IPv4 address available +# 4 = Double NATed private IPv4 address available +# 5 = Port-restricted IPv4 address and single NATed IPv4 address available +# 6 = Port-restricted IPv4 address and double NATed IPv4 address available +# 7 = Availability of the address type is not known +# ipv6_type: +# 0 = Address type not available +# 1 = Address type available +# 2 = Availability of the address type not known +#ipaddr_type_availability=14 + +# Domain Name +# format: [,] +#domain_name=example.com,another.example.com,yet-another.example.com + +# 3GPP Cellular Network information +# format: [;][;...] +#anqp_3gpp_cell_net=244,91;310,026;234,56 + +# NAI Realm information +# One or more realm can be advertised. Each nai_realm line adds a new realm to +# the set. These parameters provide information for stations using Interworking +# network selection to allow automatic connection to a network based on +# credentials. +# format: ,[,][,][,...] +# encoding: +# 0 = Realm formatted in accordance with IETF RFC 4282 +# 1 = UTF-8 formatted character string that is not formatted in +# accordance with IETF RFC 4282 +# NAI Realm(s): Semi-colon delimited NAI Realm(s) +# EAP Method: [:<[AuthParam1:Val1]>][<[AuthParam2:Val2]>][...] +# EAP Method types, see: +# http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-4 +# AuthParam (Table 8-188 in IEEE Std 802.11-2012): +# ID 2 = Non-EAP Inner Authentication Type +# 1 = PAP, 2 = CHAP, 3 = MSCHAP, 4 = MSCHAPV2 +# ID 3 = Inner authentication EAP Method Type +# ID 5 = Credential Type +# 1 = SIM, 2 = USIM, 3 = NFC Secure Element, 4 = Hardware Token, +# 5 = Softoken, 6 = Certificate, 7 = username/password, 9 = Anonymous, +# 10 = Vendor Specific +#nai_realm=0,example.com;example.net +# EAP methods EAP-TLS with certificate and EAP-TTLS/MSCHAPv2 with +# username/password +#nai_realm=0,example.org,13[5:6],21[2:4][5:7] + +# Arbitrary ANQP-element configuration +# Additional ANQP-elements with arbitrary values can be defined by specifying +# their contents in raw format as a hexdump of the payload. Note that these +# values will override ANQP-element contents that may have been specified in the +# more higher layer configuration parameters listed above. +# format: anqp_elem=: +# For example, AP Geospatial Location ANQP-element with unknown location: +#anqp_elem=265:0000 +# For example, AP Civic Location ANQP-element with unknown location: +#anqp_elem=266:000000 + +# GAS Address 3 behavior +# 0 = P2P specification (Address3 = AP BSSID) workaround enabled by default +# based on GAS request Address3 +# 1 = IEEE 802.11 standard compliant regardless of GAS request Address3 +# 2 = Force non-compliant behavior (Address3 = AP BSSID for all cases) +#gas_address3=0 + +# QoS Map Set configuration +# +# Comma delimited QoS Map Set in decimal values +# (see IEEE Std 802.11-2012, 8.4.2.97) +# +# format: +# [,],... +# +# There can be up to 21 optional DSCP Exceptions which are pairs of DSCP Value +# (0..63 or 255) and User Priority (0..7). This is followed by eight DSCP Range +# descriptions with DSCP Low Value and DSCP High Value pairs (0..63 or 255) for +# each UP starting from 0. If both low and high value are set to 255, the +# corresponding UP is not used. +# +# default: not set +#qos_map_set=53,2,22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,255,255 + +##### Hotspot 2.0 ############################################################# + +# Enable Hotspot 2.0 support +#hs20=1 + +# Disable Downstream Group-Addressed Forwarding (DGAF) +# This can be used to configure a network where no group-addressed frames are +# allowed. The AP will not forward any group-address frames to the stations and +# random GTKs are issued for each station to prevent associated stations from +# forging such frames to other stations in the BSS. +#disable_dgaf=1 + +# OSU Server-Only Authenticated L2 Encryption Network +#osen=1 + +# ANQP Domain ID (0..65535) +# An identifier for a set of APs in an ESS that share the same common ANQP +# information. 0 = Some of the ANQP information is unique to this AP (default). +#anqp_domain_id=1234 + +# Deauthentication request timeout +# If the RADIUS server indicates that the station is not allowed to connect to +# the BSS/ESS, the AP can allow the station some time to download a +# notification page (URL included in the message). This parameter sets that +# timeout in seconds. If the RADIUS server provides no URL, this value is +# reduced to two seconds with an additional trigger for immediate +# deauthentication when the STA acknowledges reception of the deauthentication +# imminent indication. Note that setting this value to 0 will prevent delivery +# of the notification to the STA, so a value of at least 1 should be used here +# for normal use cases. +#hs20_deauth_req_timeout=60 + +# Operator Friendly Name +# This parameter can be used to configure one or more Operator Friendly Name +# Duples. Each entry has a two or three character language code (ISO-639) +# separated by colon from the operator friendly name string. +#hs20_oper_friendly_name=eng:Example operator +#hs20_oper_friendly_name=fin:Esimerkkioperaattori + +# Connection Capability +# This can be used to advertise what type of IP traffic can be sent through the +# hotspot (e.g., due to firewall allowing/blocking protocols/ports). +# format: :: +# IP Protocol: 1 = ICMP, 6 = TCP, 17 = UDP +# Port Number: 0..65535 +# Status: 0 = Closed, 1 = Open, 2 = Unknown +# Each hs20_conn_capab line is added to the list of advertised tuples. +#hs20_conn_capab=1:0:2 +#hs20_conn_capab=6:22:1 +#hs20_conn_capab=17:5060:0 + +# WAN Metrics +# format: :
      :
        :
        :
          : +# WAN Info: B0-B1: Link Status, B2: Symmetric Link, B3: At Capabity +# (encoded as two hex digits) +# Link Status: 1 = Link up, 2 = Link down, 3 = Link in test state +# Downlink Speed: Estimate of WAN backhaul link current downlink speed in kbps; +# 1..4294967295; 0 = unknown +# Uplink Speed: Estimate of WAN backhaul link current uplink speed in kbps +# 1..4294967295; 0 = unknown +# Downlink Load: Current load of downlink WAN connection (scaled to 255 = 100%) +# Uplink Load: Current load of uplink WAN connection (scaled to 255 = 100%) +# Load Measurement Duration: Duration for measuring downlink/uplink load in +# tenths of a second (1..65535); 0 if load cannot be determined +#hs20_wan_metrics=01:8000:1000:80:240:3000 + +# Operating Class Indication +# List of operating classes the BSSes in this ESS use. The Global operating +# classes in Table E-4 of IEEE Std 802.11-2012 Annex E define the values that +# can be used in this. +# format: hexdump of operating class octets +# for example, operating classes 81 (2.4 GHz channels 1-13) and 115 (5 GHz +# channels 36-48): +#hs20_operating_class=5173 + +# Terms and Conditions information +# +# hs20_t_c_filename contains the Terms and Conditions filename that the AP +# indicates in RADIUS Access-Request messages. +#hs20_t_c_filename=terms-and-conditions +# +# hs20_t_c_timestamp contains the Terms and Conditions timestamp that the AP +# indicates in RADIUS Access-Request messages. Usually, this contains the number +# of seconds since January 1, 1970 00:00 UTC showing the time when the file was +# last modified. +#hs20_t_c_timestamp=1234567 +# +# hs20_t_c_server_url contains a template for the Terms and Conditions server +# URL. This template is used to generate the URL for a STA that needs to +# acknowledge Terms and Conditions. Unlike the other hs20_t_c_* parameters, this +# parameter is used on the authentication server, not the AP. +# Macros: +# @1@ = MAC address of the STA (colon separated hex octets) +#hs20_t_c_server_url=https://example.com/t_and_c?addr=@1@&ap=123 + +# OSU and Operator icons +# ::::: +#hs20_icon=32:32:eng:image/png:icon32:/tmp/icon32.png +#hs20_icon=64:64:eng:image/png:icon64:/tmp/icon64.png + +# OSU SSID (see ssid2 for format description) +# This is the SSID used for all OSU connections to all the listed OSU Providers. +#osu_ssid="example" + +# OSU Providers +# One or more sets of following parameter. Each OSU provider is started by the +# mandatory osu_server_uri item. The other parameters add information for the +# last added OSU provider. osu_nai specifies the OSU_NAI value for OSEN +# authentication when using a standalone OSU BSS. osu_nai2 specifies the OSU_NAI +# value for OSEN authentication when using a shared BSS (Single SSID) for OSU. +# +#osu_server_uri=https://example.com/osu/ +#osu_friendly_name=eng:Example operator +#osu_friendly_name=fin:Esimerkkipalveluntarjoaja +#osu_nai=anonymous@example.com +#osu_nai2=anonymous@example.com +#osu_method_list=1 0 +#osu_icon=icon32 +#osu_icon=icon64 +#osu_service_desc=eng:Example services +#osu_service_desc=fin:Esimerkkipalveluja +# +#osu_server_uri=... + +# Operator Icons +# Operator icons are specified using references to the hs20_icon entries +# (Name subfield). This information, if present, is advertsised in the +# Operator Icon Metadata ANQO-element. +#operator_icon=icon32 +#operator_icon=icon64 + +##### Multiband Operation (MBO) ############################################### +# +# MBO enabled +# 0 = disabled (default) +# 1 = enabled +#mbo=1 +# +# Cellular data connection preference +# 0 = Excluded - AP does not want STA to use the cellular data connection +# 1 = AP prefers the STA not to use cellular data connection +# 255 = AP prefers the STA to use cellular data connection +#mbo_cell_data_conn_pref=1 + +##### Optimized Connectivity Experience (OCE) ################################# +# +# Enable OCE specific features (bitmap) +# BIT(0) - Reserved +# Set BIT(1) (= 2) to enable OCE in STA-CFON mode +# Set BIT(2) (= 4) to enable OCE in AP mode +# Default is 0 = OCE disabled +#oce=0 + +# 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) +# Note: This rejection happens based on a signal strength detected while +# receiving a single frame and as such, there is significant risk of the value +# not being accurate and this resulting in valid stations being rejected. As +# such, this functionality is not recommended to be used for purposes other than +# testing. +#rssi_reject_assoc_rssi=-75 +# +# Association retry delay in seconds allowed by the STA if RSSI has not met the +# 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 +# option CONFIG_FST is set while compiling hostapd. They allow this interface +# to be a part of FST setup. +# +# FST is the transfer of a session from a channel to another channel, in the +# same or different frequency bands. +# +# For detals, see IEEE Std 802.11ad-2012. + +# Identifier of an FST Group the interface belongs to. +#fst_group_id=bond0 + +# Interface priority within the FST Group. +# Announcing a higher priority for an interface means declaring it more +# preferable for FST switch. +# fst_priority is in 1..255 range with 1 being the lowest priority. +#fst_priority=100 + +# Default LLT value for this interface in milliseconds. The value used in case +# no value provided during session setup. Default is 50 ms. +# fst_llt is in 1..4294967 range (due to spec limitation, see 10.32.2.2 +# Transitioning between states). +#fst_llt=100 + +##### Radio measurements / location ########################################### + +# The content of a LCI measurement subelement +#lci= + +# The content of a location civic measurement subelement +#civic= + +# Enable neighbor report via radio measurements +#rrm_neighbor_report=1 + +# Enable link measurement report via radio measurements +#rrm_link_measurement_report=1 + +# Enable beacon report via radio measurements +#rrm_beacon_report=1 + +# Publish fine timing measurement (FTM) responder functionality +# This parameter only controls publishing via Extended Capabilities element. +# Actual functionality is managed outside hostapd. +#ftm_responder=0 + +# Publish fine timing measurement (FTM) initiator functionality +# This parameter only controls publishing via Extended Capabilities element. +# Actual functionality is managed outside hostapd. +#ftm_initiator=0 +# +# Stationary AP config indicates that the AP doesn't move hence location data +# can be considered as always up to date. If configured, LCI data will be sent +# as a radio measurement even if the request doesn't contain a max age element +# that allows sending of such data. Default: 0. +#stationary_ap=0 + +# Enable reduced neighbor reporting (RNR) +#rnr=0 + +##### Airtime policy configuration ########################################### + +# Set the airtime policy operating mode: +# 0 = disabled (default) +# 1 = static config +# 2 = per-BSS dynamic config +# 3 = per-BSS limit mode +#airtime_mode=0 + +# Interval (in milliseconds) to poll the kernel for updated station activity in +# dynamic and limit modes +#airtime_update_interval=200 + +# Static configuration of station weights (when airtime_mode=1). Kernel default +# weight is 256; set higher for larger airtime share, lower for smaller share. +# Each entry is a MAC address followed by a weight. +#airtime_sta_weight=02:01:02:03:04:05 256 +#airtime_sta_weight=02:01:02:03:04:06 512 + +# Per-BSS airtime weight. In multi-BSS mode, set for each BSS and hostapd will +# configure station weights to enforce the correct ratio between BSS weights +# depending on the number of active stations. The *ratios* between different +# BSSes is what's important, not the absolute numbers. +# Must be set for all BSSes if airtime_mode=2 or 3, has no effect otherwise. +#airtime_bss_weight=1 + +# Whether the current BSS should be limited (when airtime_mode=3). +# +# If set, the BSS weight ratio will be applied in the case where the current BSS +# would exceed the share defined by the BSS weight ratio. E.g., if two BSSes are +# set to the same weights, and one is set to limited, the limited BSS will get +# no more than half the available airtime, but if the non-limited BSS has more +# stations active, that *will* be allowed to exceed its half of the available +# 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 +# option CONFIG_TESTING_OPTIONS is set while compiling hostapd. They allow +# testing some scenarios that are otherwise difficult to reproduce. +# +# Ignore probe requests sent to hostapd with the given probability, must be a +# floating point number in the range [0, 1). +#ignore_probe_probability=0.0 +# +# Ignore authentication frames with the given probability +#ignore_auth_probability=0.0 +# +# Ignore association requests with the given probability +#ignore_assoc_probability=0.0 +# +# Ignore reassociation requests with the given probability +#ignore_reassoc_probability=0.0 +# +# Corrupt Key MIC in GTK rekey EAPOL-Key frames with the given probability +#corrupt_gtk_rekey_mic_probability=0.0 +# +# Include only ECSA IE without CSA IE where possible +# (channel switch operating class is needed) +#ecsa_ie_only=0 +# +# Delay EAPOL-Key messages 1/4 and 3/4 by not sending the frame until the last +# attempt (wpa_pairwise_update_count). This will trigger a timeout on all +# previous attempts and thus delays the frame. (testing only) +#delay_eapol_tx=0 +# +# Additional elements for Probe Response frames. +# This parameter can be used to add additional element(s) to the end of the +# Probe Response frames. The format for these element(s) is a hexdump of the +# raw information elements (id+len+payload for one or more elements). +# These elements are added after the 'vendor_elements'. +#presp_elements= + +##### Multiple BSSID support ################################################## +# +# Above configuration is using the default interface (wlan#, or multi-SSID VLAN +# interfaces). Other BSSIDs can be added by using separator 'bss' with +# default interface name to be allocated for the data packets of the new BSS. +# +# hostapd will generate BSSID mask based on the BSSIDs that are +# configured. hostapd will verify that dev_addr & MASK == dev_addr. If this is +# not the case, the MAC address of the radio must be changed before starting +# hostapd (ifconfig wlan0 hw ether ). If a BSSID is configured for +# every secondary BSS, this limitation is not applied at hostapd and other +# masks may be used if the driver supports them (e.g., swap the locally +# administered bit) +# +# BSSIDs are assigned in order to each BSS, unless an explicit BSSID is +# specified using the 'bssid' parameter. +# If an explicit BSSID is specified, it must be chosen such that it: +# - results in a valid MASK that covers it and the dev_addr +# - is not the same as the MAC address of the radio +# - is not the same as any other explicitly specified BSSID +# +# Alternatively, the 'use_driver_iface_addr' parameter can be used to request +# hostapd to use the driver auto-generated interface address (e.g., to use the +# exact MAC addresses allocated to the device). +# +# Not all drivers support multiple BSSes. The exact mechanism for determining +# the driver capabilities is driver specific. With the current (i.e., a recent +# kernel) drivers using nl80211, this information can be checked with "iw list" +# (search for "valid interface combinations"). +# +# Please note that hostapd uses some of the values configured for the first BSS +# as the defaults for the following BSSes. However, it is recommended that all +# BSSes include explicit configuration of all relevant configuration items. +# +#bss=wlan0_0 +#ssid=test2 +# most of the above items can be used here (apart from radio interface specific +# items, like channel) + +#bss=wlan0_1 +#bssid=00:13:10:95:fe:0b +# ... +# +# Multiple BSSID Advertisement in IEEE 802.11ax +# IEEE Std 802.11ax-2021 added a feature where instead of multiple interfaces +# on a common radio transmitting individual Beacon frames, those interfaces can +# form a set with a common Beacon frame transmitted for all. The interface +# which is brought up first is called the transmitting profile of the MBSSID +# set which transmits the Beacon frames. The remaining interfaces are called +# the non-transmitting profiles and these are advertised inside the Multiple +# BSSID element in the Beacon and Probe Response frames from the first +# interface. +# +# The transmitting interface is visible to all stations in the vicinity, however +# the stations that do not support parsing of the Multiple BSSID element will +# not be able to connect to the non-transmitting interfaces. +# +# Enhanced Multiple BSSID Advertisements (EMA) +# When enabled, the non-transmitting interfaces are split into multiple +# Beacon frames. The number of Beacon frames required to cover all the +# non-transmitting profiles is called the profile periodicity. +# +# Refer to IEEE Std 802.11-2020 for details regarding the procedure and +# required MAC address assignment. +# +# Following configuration is per radio. +# 0 = Disabled (default) +# 1 = Multiple BSSID advertisement enabled. +# 2 = Enhanced multiple BSSID advertisement enabled. +#mbssid=0 +# +# The transmitting interface should be added with the 'interface' option while +# the non-transmitting interfaces should be added using the 'bss' option. +# Security configuration should be added separately per interface, if required. +# +# Example: +#mbssid=2 +#interface=wlan2 +#ctrl_interface=/var/run/hostapd +#wpa_passphrase=0123456789 +#ieee80211w=2 +#sae_pwe=1 +#auth_algs=1 +#wpa=2 +#wpa_pairwise=CCMP +#ssid= +#bridge=br-lan +#wpa_key_mgmt=SAE +#bssid=00:03:7f:12:84:84 +# +#bss=wlan2-1 +#ctrl_interface=/var/run/hostapd +#wpa_passphrase=0123456789 +#ieee80211w=2 +#sae_pwe=1 +#auth_algs=1 +#wpa=2 +#wpa_pairwise=CCMP +#ssid= +#bridge=br-lan +#wpa_key_mgmt=SAE +#bssid=00:03:7f:12:84:85 diff --git a/hostapd/hostapd.deny b/hostapd/hostapd.deny new file mode 100644 index 000000000000..1616678f579e --- /dev/null +++ b/hostapd/hostapd.deny @@ -0,0 +1,5 @@ +# List of MAC addresses that are not allowed to authenticate (IEEE 802.11) +# with the AP. +00:20:30:40:50:60 +00:ab:cd:ef:12:34 +00:00:30:40:50:60 diff --git a/hostapd/hostapd.eap_user b/hostapd/hostapd.eap_user new file mode 100644 index 000000000000..61ef9375b276 --- /dev/null +++ b/hostapd/hostapd.eap_user @@ -0,0 +1,103 @@ +# hostapd user database for integrated EAP server + +# Each line must contain an identity, EAP method(s), and an optional password +# separated with whitespace (space or tab). The identity and password must be +# double quoted ("user"). Password can alternatively be stored as +# NtPasswordHash (16-byte MD4 hash of the unicode presentation of the password +# in unicode) if it is used for MSCHAP or MSCHAPv2 authentication. This means +# that the plaintext password does not need to be included in the user file. +# Password hash is stored as hash:<16-octets of hex data> without quotation +# marks. + +# [2] flag in the end of the line can be used to mark users for tunneled phase +# 2 authentication (e.g., within EAP-PEAP). In these cases, an anonymous +# identity can be used in the unencrypted phase 1 and the real user identity +# is transmitted only within the encrypted tunnel in phase 2. If non-anonymous +# access is needed, two user entries is needed, one for phase 1 and another +# with the same username for phase 2. +# +# EAP-TLS, EAP-PEAP, EAP-TTLS, EAP-FAST, EAP-SIM, and EAP-AKA do not use +# password option. +# EAP-MD5, EAP-MSCHAPV2, EAP-GTC, EAP-PAX, EAP-PSK, and EAP-SAKE require a +# password. +# EAP-PEAP, EAP-TTLS, and EAP-FAST require Phase 2 configuration. +# +# * can be used as a wildcard to match any user identity. The main purposes for +# this are to set anonymous phase 1 identity for EAP-PEAP and EAP-TTLS and to +# avoid having to configure every certificate for EAP-TLS authentication. The +# first matching entry is selected, so * should be used as the last phase 1 +# user entry. +# +# "prefix"* can be used to match the given prefix and anything after this. The +# main purpose for this is to be able to avoid EAP method negotiation when the +# method is using known prefix in identities (e.g., EAP-SIM and EAP-AKA). This +# is only allowed for phase 1 identities. +# +# Multiple methods can be configured to make the authenticator try them one by +# one until the peer accepts one. The method names are separated with a +# comma (,). +# +# [ver=0] and [ver=1] flags after EAP type PEAP can be used to force PEAP +# version based on the Phase 1 identity. Without this flag, the EAP +# authenticator advertises the highest supported version and select the version +# based on the first PEAP packet from the supplicant. +# +# EAP-TTLS supports both EAP and non-EAP authentication inside the tunnel. +# Tunneled EAP methods are configured with standard EAP method name and [2] +# flag. Non-EAP methods can be enabled by following method names: TTLS-PAP, +# TTLS-CHAP, TTLS-MSCHAP, TTLS-MSCHAPV2. TTLS-PAP and TTLS-CHAP require a +# plaintext password while TTLS-MSCHAP and TTLS-MSCHAPV2 can use NT password +# hash. +# +# Arbitrary RADIUS attributes can be added into Access-Accept packets similarly +# to the way radius_auth_req_attr is used for Access-Request packet in +# hostapd.conf. For EAP server, this is configured separately for each user +# entry with radius_accept_attr=[:] line(s) following +# the main user entry line. + +# Phase 1 users +"user" MD5 "password" +"test user" MD5 "secret" +"example user" TLS +"DOMAIN\user" MSCHAPV2 "password" +"gtc user" GTC "password" +"pax user" PAX "unknown" +"pax.user@example.com" PAX 0123456789abcdef0123456789abcdef +"psk user" PSK "unknown" +"psk.user@example.com" PSK 0123456789abcdef0123456789abcdef +"sake.user@example.com" SAKE 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef +"ttls" TTLS +"not anonymous" PEAP +# Default to EAP-SIM and EAP-AKA based on fixed identity prefixes +"0"* AKA,TTLS,TLS,PEAP,SIM +"1"* SIM,TTLS,TLS,PEAP,AKA +"2"* AKA,TTLS,TLS,PEAP,SIM +"3"* SIM,TTLS,TLS,PEAP,AKA +"4"* AKA,TTLS,TLS,PEAP,SIM +"5"* SIM,TTLS,TLS,PEAP,AKA +"6"* AKA' +"7"* AKA' +"8"* AKA' + +# Wildcard for all other identities +* PEAP,TTLS,TLS,SIM,AKA + +# Phase 2 (tunnelled within EAP-PEAP or EAP-TTLS) users +"t-md5" MD5 "password" [2] +"DOMAIN\t-mschapv2" MSCHAPV2 "password" [2] +"t-gtc" GTC "password" [2] +"not anonymous" MSCHAPV2 "password" [2] +"user" MD5,GTC,MSCHAPV2 "password" [2] +"test user" MSCHAPV2 hash:000102030405060708090a0b0c0d0e0f [2] +"ttls-user" TTLS-PAP,TTLS-CHAP,TTLS-MSCHAP,TTLS-MSCHAPV2 "password" [2] + +# Default to EAP-SIM and EAP-AKA based on fixed identity prefixes in phase 2 +"0"* AKA [2] +"1"* SIM [2] +"2"* AKA [2] +"3"* SIM [2] +"4"* AKA [2] +"5"* SIM [2] +"6"* AKA' [2] +"7"* AKA' [2] +"8"* AKA' [2] diff --git a/hostapd/hostapd.eap_user_sqlite b/hostapd/hostapd.eap_user_sqlite new file mode 100644 index 000000000000..411b9eafa264 --- /dev/null +++ b/hostapd/hostapd.eap_user_sqlite @@ -0,0 +1,42 @@ +CREATE TABLE users( + identity TEXT PRIMARY KEY, + methods TEXT, + password TEXT, + remediation TEXT, + phase2 INTEGER, + t_c_timestamp INTEGER +); + +CREATE TABLE wildcards( + identity TEXT PRIMARY KEY, + methods TEXT +); + +INSERT INTO users(identity,methods,password,phase2) VALUES ('user','TTLS-MSCHAPV2','password',1); +INSERT INTO users(identity,methods,password,phase2) VALUES ('DOMAIN\mschapv2 user','TTLS-MSCHAPV2','password',1); + +INSERT INTO wildcards(identity,methods) VALUES ('','TTLS,TLS'); +INSERT INTO wildcards(identity,methods) VALUES ('0','AKA'); + +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 +); diff --git a/hostapd/hostapd.radius_clients b/hostapd/hostapd.radius_clients new file mode 100644 index 000000000000..3980427253b4 --- /dev/null +++ b/hostapd/hostapd.radius_clients @@ -0,0 +1,4 @@ +# RADIUS client configuration for the RADIUS server +10.1.2.3 secret passphrase +192.168.1.0/24 another very secret passphrase +0.0.0.0/0 radius diff --git a/hostapd/hostapd.sim_db b/hostapd/hostapd.sim_db new file mode 100644 index 000000000000..01c593de8d2d --- /dev/null +++ b/hostapd/hostapd.sim_db @@ -0,0 +1,9 @@ +# Example GSM authentication triplet file for EAP-SIM authenticator +# IMSI:Kc:SRES:RAND +# IMSI: ASCII string (numbers) +# Kc: hex, 8 octets +# SRES: hex, 4 octets +# RAND: hex, 16 octets +234567898765432:A0A1A2A3A4A5A6A7:D1D2D3D4:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +234567898765432:B0B1B2B3B4B5B6B7:E1E2E3E4:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB +234567898765432:C0C1C2C3C4C5C6C7:F1F2F3F4:CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC diff --git a/hostapd/hostapd.vlan b/hostapd/hostapd.vlan new file mode 100644 index 000000000000..98254fa84f01 --- /dev/null +++ b/hostapd/hostapd.vlan @@ -0,0 +1,9 @@ +# VLAN ID to network interface mapping +1 vlan1 +2 vlan2 +3 vlan3 +100 guest +# Optional wildcard entry matching all VLAN IDs. The first # in the interface +# name will be replaced with the VLAN ID. The network interfaces are created +# (and removed) dynamically based on the use. +* vlan# diff --git a/hostapd/hostapd.wpa_psk b/hostapd/hostapd.wpa_psk new file mode 100644 index 000000000000..2ce5ff2346df --- /dev/null +++ b/hostapd/hostapd.wpa_psk @@ -0,0 +1,21 @@ +# List of WPA PSKs. Each line, except for empty lines and lines starting +# with #, must contain a MAC address and PSK separated with a space. +# Special MAC address 00:00:00:00:00:00 can be used to configure PSKs that +# anyone can use. PSK can be configured as an ASCII passphrase of 8..63 +# characters or as a 256-bit hex PSK (64 hex digits). +# An optional key identifier can be added by prefixing the line with +# 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/hostapd/hostapd_cli.1 b/hostapd/hostapd_cli.1 new file mode 100644 index 000000000000..218ea1588a4a --- /dev/null +++ b/hostapd/hostapd_cli.1 @@ -0,0 +1,89 @@ +.TH HOSTAPD_CLI 1 "April 7, 2005" hostapd_cli "hostapd command-line interface" +.SH NAME +hostapd_cli \- hostapd command-line interface +.SH SYNOPSIS +.B hostapd_cli +[\-p] [\-i] [\-a] [\-hvB] [command..] +.SH DESCRIPTION +This manual page documents briefly the +.B hostapd_cli +utility. +.PP +.B hostapd_cli +is a command-line interface for the +.B hostapd +daemon. + +.B hostapd +is a user space daemon for access point and authentication servers. +It implements IEEE 802.11 access point management, IEEE 802.1X/WPA/WPA2/EAP Authenticators and RADIUS authentication server. +For more information about +.B hostapd +refer to the +.BR hostapd (8) +man page. +.SH OPTIONS +A summary of options is included below. +For a complete description, run +.BR hostapd_cli +from the command line. +.TP +.B \-p +Path to find control sockets. + +Default: /var/run/hostapd +.TP +.B \-i +Interface to listen on. + +Default: first interface found in socket path. +.TP +.B \-a +Run in daemon mode executing the action file based on events from hostapd. +.TP +.B \-B +Run a daemon in the background. +.TP +.B \-h +Show usage. +.TP +.B \-v +Show hostapd_cli version. +.SH COMMANDS +A summary of commands is included below. +For a complete description, run +.BR hostapd_cli +from the command line. +.TP +.B mib +Get MIB variables (dot1x, dot11, radius). +.TP +.B sta +Get MIB variables for one station. +.TP +.B all_sta +Get MIB variables for all stations. +.TP +.B help +Get usage help. +.TP +.B interface [ifname] +Show interfaces/select interface. +.TP +.B level +Change debug level. +.TP +.B license +Show full +.B hostapd_cli +license. +.TP +.B quit +Exit hostapd_cli. +.SH SEE ALSO +.BR hostapd (8). +.SH AUTHOR +hostapd_cli was written by Jouni Malinen . +.PP +This manual page was written by Faidon Liambotis , +for the Debian project (but may be used by others). diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c new file mode 100644 index 000000000000..eb8a38350bd1 --- /dev/null +++ b/hostapd/hostapd_cli.c @@ -0,0 +1,2342 @@ +/* + * hostapd - command line interface for hostapd daemon + * Copyright (c) 2004-2022, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" +#include + +#include "common/wpa_ctrl.h" +#include "common/ieee802_11_defs.h" +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/edit.h" +#include "common/version.h" +#include "common/cli.h" + +#ifndef CONFIG_NO_CTRL_IFACE + +static const char *const hostapd_cli_version = +"hostapd_cli v" VERSION_STR "\n" +"Copyright (c) 2004-2024, Jouni Malinen and contributors"; + +static struct wpa_ctrl *ctrl_conn; +static int hostapd_cli_quit = 0; +static int hostapd_cli_attached = 0; + +#ifndef CONFIG_CTRL_IFACE_DIR +#define CONFIG_CTRL_IFACE_DIR "/var/run/hostapd" +#endif /* CONFIG_CTRL_IFACE_DIR */ +static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR; +static const char *client_socket_dir = NULL; + +static char *ctrl_ifname = NULL; +static const char *pid_file = NULL; +static const char *action_file = NULL; +static int ping_interval = 5; +static int interactive = 0; +static int event_handler_registered = 0; + +static DEFINE_DL_LIST(stations); /* struct cli_txt_entry */ + +static void print_help(FILE *stream, const char *cmd); +static char ** list_cmd_list(void); +static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx); +static void update_stations(struct wpa_ctrl *ctrl); +static void cli_event(const char *str); + + +static void usage(void) +{ + fprintf(stderr, "%s\n", hostapd_cli_version); + fprintf(stderr, + "\n" + "usage: hostapd_cli [-p] [-i] [-hvBr] " + "[-a] \\\n" + " [-P] [-G] [command..]\n" + "\n" + "Options:\n" + " -h help (show this usage text)\n" + " -v shown version information\n" + " -p path to find control sockets (default: " + "/var/run/hostapd)\n" + " -s dir path to open client sockets (default: " + CONFIG_CTRL_IFACE_DIR ")\n" + " -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" + " socket path)\n\n"); + print_help(stderr, NULL); +} + + +static void register_event_handler(struct wpa_ctrl *ctrl) +{ + if (!ctrl_conn) + return; + if (interactive) { + event_handler_registered = + !eloop_register_read_sock(wpa_ctrl_get_fd(ctrl), + hostapd_cli_receive, + NULL, NULL); + } +} + + +static void unregister_event_handler(struct wpa_ctrl *ctrl) +{ + if (!ctrl_conn) + return; + if (interactive && event_handler_registered) { + eloop_unregister_read_sock(wpa_ctrl_get_fd(ctrl)); + event_handler_registered = 0; + } +} + + +static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname) +{ +#ifndef CONFIG_CTRL_IFACE_UDP + char *cfile; + int flen; +#endif /* !CONFIG_CTRL_IFACE_UDP */ + + if (ifname == NULL) + return NULL; + +#ifdef CONFIG_CTRL_IFACE_UDP + ctrl_conn = wpa_ctrl_open(ifname); + return ctrl_conn; +#else /* CONFIG_CTRL_IFACE_UDP */ + flen = strlen(ctrl_iface_dir) + strlen(ifname) + 2; + cfile = malloc(flen); + if (cfile == NULL) + return NULL; + snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname); + + if (client_socket_dir && client_socket_dir[0] && + access(client_socket_dir, F_OK) < 0) { + perror(client_socket_dir); + free(cfile); + return NULL; + } + + ctrl_conn = wpa_ctrl_open2(cfile, client_socket_dir); + free(cfile); + return ctrl_conn; +#endif /* CONFIG_CTRL_IFACE_UDP */ +} + + +static void hostapd_cli_close_connection(void) +{ + if (ctrl_conn == NULL) + return; + + unregister_event_handler(ctrl_conn); + if (hostapd_cli_attached) { + wpa_ctrl_detach(ctrl_conn); + hostapd_cli_attached = 0; + } + wpa_ctrl_close(ctrl_conn); + ctrl_conn = NULL; +} + + +static int hostapd_cli_reconnect(const char *ifname) +{ + char *next_ctrl_ifname; + + hostapd_cli_close_connection(); + + if (!ifname) + return -1; + + next_ctrl_ifname = os_strdup(ifname); + os_free(ctrl_ifname); + ctrl_ifname = next_ctrl_ifname; + if (!ctrl_ifname) + return -1; + + ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); + if (!ctrl_conn) + return -1; + if (!interactive && !action_file) + return 0; + if (wpa_ctrl_attach(ctrl_conn) == 0) { + hostapd_cli_attached = 1; + register_event_handler(ctrl_conn); + update_stations(ctrl_conn); + } else { + printf("Warning: Failed to attach to hostapd.\n"); + } + return 0; +} + + +static void hostapd_cli_msg_cb(char *msg, size_t len) +{ + cli_event(msg); + printf("%s\n", msg); +} + + +static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print) +{ + char buf[4096]; + size_t len; + int ret; + + if (ctrl_conn == NULL) { + printf("Not connected to hostapd - command dropped.\n"); + return -1; + } + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, + hostapd_cli_msg_cb); + if (ret == -2) { + printf("'%s' command timed out.\n", cmd); + return -2; + } else if (ret < 0) { + printf("'%s' command failed.\n", cmd); + return -1; + } + if (print) { + buf[len] = '\0'; + printf("%s", buf); + } + return 0; +} + + +static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd) +{ + return _wpa_ctrl_command(ctrl, cmd, 1); +} + + +static int hostapd_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd, + int min_args, int argc, char *argv[]) +{ + char buf[4096]; + + if (argc < min_args) { + printf("Invalid %s command - at least %d argument%s required.\n", + cmd, min_args, min_args > 1 ? "s are" : " is"); + return -1; + } + if (write_cmd(buf, sizeof(buf), cmd, argc, argv) < 0) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PING"); +} + + +static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RELOG"); +} + + +static int hostapd_cli_cmd_close_log(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "CLOSE_LOG"); +} + + +static int hostapd_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc > 0 && os_strcmp(argv[0], "driver") == 0) + return wpa_ctrl_command(ctrl, "STATUS-DRIVER"); + return wpa_ctrl_command(ctrl, "STATUS"); +} + + +static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc > 0) { + char buf[100]; + os_snprintf(buf, sizeof(buf), "MIB %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); + } + return wpa_ctrl_command(ctrl, "MIB"); +} + + +static int hostapd_cli_exec(const char *program, const char *arg1, + const char *arg2) +{ + char *arg; + size_t len; + int res; + + len = os_strlen(arg1) + os_strlen(arg2) + 2; + arg = os_malloc(len); + if (arg == NULL) + return -1; + os_snprintf(arg, len, "%s %s", arg1, arg2); + res = os_exec(program, arg, 1); + os_free(arg); + + return res; +} + + +static void hostapd_cli_action_process(char *msg, size_t len) +{ + const char *pos; + + pos = msg; + if (*pos == '<') { + pos = os_strchr(pos, '>'); + if (pos) + pos++; + else + pos = msg; + } + + hostapd_cli_exec(action_file, ctrl_ifname, pos); +} + + +static void hostapd_cli_action_cb(char *msg, size_t len) +{ + hostapd_cli_action_process(msg, len); +} + + +static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char buf[64]; + if (argc < 1) { + printf("Invalid 'sta' command - at least one argument, STA " + "address, is required.\n"); + return -1; + } + if (argc > 1) + snprintf(buf, sizeof(buf), "STA %s %s", argv[0], argv[1]); + else + snprintf(buf, sizeof(buf), "STA %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} + + +static char ** hostapd_complete_stations(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = cli_txt_list_array(&stations); + break; + } + + return res; +} + + +static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[64]; + if (argc != 1) { + printf("Invalid 'new_sta' command - exactly one argument, STA " + "address, is required.\n"); + return -1; + } + snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[64]; + if (argc < 1) { + printf("Invalid 'deauthenticate' command - exactly one " + "argument, STA address, is required.\n"); + return -1; + } + if (argc > 1) + os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s %s", + argv[0], argv[1]); + else + os_snprintf(buf, sizeof(buf), "DEAUTHENTICATE %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[64]; + if (argc < 1) { + printf("Invalid 'disassociate' command - exactly one " + "argument, STA address, is required.\n"); + return -1; + } + if (argc > 1) + os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s %s", + argv[0], argv[1]); + else + os_snprintf(buf, sizeof(buf), "DISASSOCIATE %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} + + +#ifdef CONFIG_TAXONOMY +static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[64]; + + if (argc != 1) { + printf("Invalid 'signature' command - exactly one argument, STA address, is required.\n"); + return -1; + } + os_snprintf(buf, sizeof(buf), "SIGNATURE %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} +#endif /* CONFIG_TAXONOMY */ + + +static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[64]; + if (argc != 1) { + printf("Invalid 'sa_query' command - exactly one argument, " + "STA address, is required.\n"); + return -1; + } + snprintf(buf, sizeof(buf), "SA_QUERY %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} + + +#ifdef CONFIG_WPS +static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[256]; + if (argc < 2) { + printf("Invalid 'wps_pin' command - at least two arguments, " + "UUID and PIN, are required.\n"); + return -1; + } + if (argc > 3) + snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s", + argv[0], argv[1], argv[2], argv[3]); + else if (argc > 2) + snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s", + argv[0], argv[1], argv[2]); + else + snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]); + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1 && argc != 2) { + printf("Invalid WPS_CHECK_PIN command: needs one argument:\n" + "- PIN to be verified\n"); + return -1; + } + + if (argc == 2) + res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s", + argv[0], argv[1]); + else + res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s", + argv[0]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long WPS_CHECK_PIN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "WPS_PBC"); +} + + +static int hostapd_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "WPS_CANCEL"); +} + + +#ifdef CONFIG_WPS_NFC +static int hostapd_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + int ret; + char *buf; + size_t buflen; + + if (argc != 1) { + printf("Invalid 'wps_nfc_tag_read' command - one argument " + "is required.\n"); + return -1; + } + + buflen = 18 + os_strlen(argv[0]); + buf = os_malloc(buflen); + if (buf == NULL) + return -1; + os_snprintf(buf, buflen, "WPS_NFC_TAG_READ %s", argv[0]); + + ret = wpa_ctrl_command(ctrl, buf); + os_free(buf); + + return ret; +} + + +static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[64]; + int res; + + if (argc != 1) { + printf("Invalid 'wps_nfc_config_token' command - one argument " + "is required.\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s", + argv[0]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long WPS_NFC_CONFIG_TOKEN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[64]; + int res; + + if (argc != 1) { + printf("Invalid 'wps_nfc_token' command - one argument is " + "required.\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long WPS_NFC_TOKEN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[64]; + int res; + + if (argc != 2) { + printf("Invalid 'nfc_get_handover_sel' command - two arguments " + "are required.\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %s %s", + argv[0], argv[1]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long NFC_GET_HANDOVER_SEL command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + +#endif /* CONFIG_WPS_NFC */ + + +static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[64]; + if (argc < 1) { + printf("Invalid 'wps_ap_pin' command - at least one argument " + "is required.\n"); + return -1; + } + if (argc > 2) + snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s", + argv[0], argv[1], argv[2]); + else if (argc > 1) + snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s", + argv[0], argv[1]); + else + snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_wps_get_status(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "WPS_GET_STATUS"); +} + + +static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[256]; + char ssid_hex[2 * SSID_MAX_LEN + 1]; + char key_hex[2 * 64 + 1]; + int i; + + if (argc < 1) { + printf("Invalid 'wps_config' command - at least two arguments " + "are required.\n"); + return -1; + } + + ssid_hex[0] = '\0'; + for (i = 0; i < SSID_MAX_LEN; i++) { + if (argv[0][i] == '\0') + break; + os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[0][i]); + } + + key_hex[0] = '\0'; + if (argc > 3) { + for (i = 0; i < 64; i++) { + if (argv[3][i] == '\0') + break; + os_snprintf(&key_hex[i * 2], 3, "%02x", + argv[3][i]); + } + } + + if (argc > 3) + snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s %s", + ssid_hex, argv[1], argv[2], key_hex); + else if (argc > 2) + snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s %s", + ssid_hex, argv[1], argv[2]); + else + snprintf(buf, sizeof(buf), "WPS_CONFIG %s %s", + ssid_hex, argv[1]); + return wpa_ctrl_command(ctrl, buf); +} +#endif /* CONFIG_WPS */ + + +static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[300]; + int res; + + if (argc < 2) { + printf("Invalid 'disassoc_imminent' command - two arguments " + "(STA addr and Disassociation Timer) are needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s", + argv[0], argv[1]); + if (os_snprintf_error(sizeof(buf), res)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[300]; + int res; + + if (argc < 3) { + printf("Invalid 'ess_disassoc' command - three arguments (STA " + "addr, disassoc timer, and URL) are needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s %s", + argv[0], argv[1], argv[2]); + if (os_snprintf_error(sizeof(buf), res)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_bss_tm_req(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[2000], *tmp; + int res, i, total; + + if (argc < 1) { + printf("Invalid 'bss_tm_req' command - at least one argument (STA addr) is needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "BSS_TM_REQ %s", argv[0]); + if (os_snprintf_error(sizeof(buf), res)) + return -1; + + total = res; + for (i = 1; i < argc; i++) { + tmp = &buf[total]; + res = os_snprintf(tmp, sizeof(buf) - total, " %s", argv[i]); + if (os_snprintf_error(sizeof(buf) - total, res)) + return -1; + total += res; + } + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_get_config(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "GET_CONFIG"); +} + + +static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd, + char *addr, size_t addr_len, int print) +{ + char buf[4096], *pos; + size_t len; + int ret; + + if (ctrl_conn == NULL) { + printf("Not connected to hostapd - command dropped.\n"); + return -1; + } + len = sizeof(buf) - 1; + ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, + hostapd_cli_msg_cb); + if (ret == -2) { + printf("'%s' command timed out.\n", cmd); + return -2; + } else if (ret < 0) { + printf("'%s' command failed.\n", cmd); + return -1; + } + + buf[len] = '\0'; + if (memcmp(buf, "FAIL", 4) == 0) + return -1; + if (print) + printf("%s", buf); + + pos = buf; + while (*pos != '\0' && *pos != '\n') + pos++; + *pos = '\0'; + os_strlcpy(addr, buf, addr_len); + return 0; +} + + +static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char addr[32], cmd[64]; + + if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 1)) + return 0; + do { + snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); + } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 1) == 0); + + return -1; +} + + +static int hostapd_cli_cmd_list_sta(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char addr[32], cmd[64]; + + if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0)) + return 0; + do { + if (os_strcmp(addr, "") != 0) + printf("%s\n", addr); + os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); + } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0); + + return 0; +} + + +static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + print_help(stdout, argc > 0 ? argv[0] : NULL); + return 0; +} + + +static char ** hostapd_cli_complete_help(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + + switch (arg) { + case 1: + res = list_cmd_list(); + break; + } + + return res; +} + + +static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + printf("%s\n\n%s\n", hostapd_cli_version, cli_full_license); + return 0; +} + + +static int hostapd_cli_cmd_set_qos_map_set(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char buf[200]; + int res; + + if (argc != 1) { + printf("Invalid 'set_qos_map_set' command - " + "one argument (comma delimited QoS map set) " + "is needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]); + if (os_snprintf_error(sizeof(buf), res)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char buf[50]; + int res; + + if (argc != 1) { + printf("Invalid 'send_qos_map_conf' command - " + "one argument (STA addr) is needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]); + if (os_snprintf_error(sizeof(buf), res)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_hs20_wnm_notif(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[300]; + int res; + + if (argc < 2) { + printf("Invalid 'hs20_wnm_notif' command - two arguments (STA " + "addr and URL) are needed\n"); + return -1; + } + + res = os_snprintf(buf, sizeof(buf), "HS20_WNM_NOTIF %s %s", + argv[0], argv[1]); + if (os_snprintf_error(sizeof(buf), res)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_hs20_deauth_req(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[300]; + int res; + + if (argc < 3) { + printf("Invalid 'hs20_deauth_req' command - at least three arguments (STA addr, Code, Re-auth Delay) are needed\n"); + return -1; + } + + if (argc > 3) + res = os_snprintf(buf, sizeof(buf), + "HS20_DEAUTH_REQ %s %s %s %s", + argv[0], argv[1], argv[2], argv[3]); + else + res = os_snprintf(buf, sizeof(buf), + "HS20_DEAUTH_REQ %s %s %s", + argv[0], argv[1], argv[2]); + if (os_snprintf_error(sizeof(buf), res)) + return -1; + return wpa_ctrl_command(ctrl, buf); +} + + +static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + hostapd_cli_quit = 1; + if (interactive) + eloop_terminate(); + return 0; +} + + +static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + if (argc != 1) { + printf("Invalid LEVEL command: needs one argument (debug " + "level)\n"); + return 0; + } + snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]); + return wpa_ctrl_command(ctrl, cmd); +} + + +static void update_stations(struct wpa_ctrl *ctrl) +{ + char addr[32], cmd[64]; + + if (!ctrl || !interactive) + return; + + cli_txt_list_flush(&stations); + + if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0)) + return; + do { + if (os_strcmp(addr, "") != 0) + cli_txt_list_add(&stations, addr); + os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); + } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0); +} + + +static void hostapd_cli_get_interfaces(struct wpa_ctrl *ctrl, + struct dl_list *interfaces) +{ + struct dirent *dent; + DIR *dir; + + if (!ctrl || !interfaces) + return; + dir = opendir(ctrl_iface_dir); + if (dir == NULL) + return; + + while ((dent = readdir(dir))) { + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + cli_txt_list_add(interfaces, dent->d_name); + } + closedir(dir); +} + + +static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl) +{ + struct dirent *dent; + DIR *dir; + + dir = opendir(ctrl_iface_dir); + if (dir == NULL) { + printf("Control interface directory '%s' could not be " + "opened.\n", ctrl_iface_dir); + return; + } + + printf("Available interfaces:\n"); + while ((dent = readdir(dir))) { + if (strcmp(dent->d_name, ".") == 0 || + strcmp(dent->d_name, "..") == 0) + continue; + printf("%s\n", dent->d_name); + } + closedir(dir); +} + + +static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + if (argc < 1) { + hostapd_cli_list_interfaces(ctrl); + return 0; + } + if (hostapd_cli_reconnect(argv[0]) != 0) { + printf("Could not connect to interface '%s' - re-trying\n", + ctrl_ifname); + } + return 0; +} + + +static char ** hostapd_complete_interface(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + char **res = NULL; + DEFINE_DL_LIST(interfaces); + + switch (arg) { + case 1: + hostapd_cli_get_interfaces(ctrl_conn, &interfaces); + res = cli_txt_list_array(&interfaces); + cli_txt_list_flush(&interfaces); + break; + } + + return res; +} + + +static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[2048]; + int res; + + if (argc != 2) { + printf("Invalid SET command: needs two arguments (variable " + "name and value)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long SET command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static char ** hostapd_complete_set(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + const char *fields[] = { +#ifdef CONFIG_WPS_TESTING + "wps_version_number", "wps_testing_stub_cred", + "wps_corrupt_pkhash", +#endif /* CONFIG_WPS_TESTING */ +#ifdef CONFIG_INTERWORKING + "gas_frag_limit", +#endif /* CONFIG_INTERWORKING */ +#ifdef CONFIG_TESTING_OPTIONS + "ext_mgmt_frame_handling", "ext_eapol_frame_io", +#endif /* CONFIG_TESTING_OPTIONS */ +#ifdef CONFIG_MBO + "mbo_assoc_disallow", +#endif /* CONFIG_MBO */ + "deny_mac_file", "accept_mac_file", + }; + int i, num_fields = ARRAY_SIZE(fields); + + if (arg == 1) { + char **res; + + res = os_calloc(num_fields + 1, sizeof(char *)); + if (!res) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(fields[i]); + if (!res[i]) + return res; + } + return res; + } + return NULL; +} + + +static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid GET command: needs one argument (variable " + "name)\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long GET command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static char ** hostapd_complete_get(const char *str, int pos) +{ + int arg = get_cmd_arg_num(str, pos); + const char *fields[] = { + "version", "tls_library", + }; + int i, num_fields = ARRAY_SIZE(fields); + + if (arg == 1) { + char **res; + + res = os_calloc(num_fields + 1, sizeof(char *)); + if (!res) + return NULL; + for (i = 0; i < num_fields; i++) { + res[i] = os_strdup(fields[i]); + if (!res[i]) + return res; + } + return res; + } + return NULL; +} + + +#ifdef CONFIG_FST +static int hostapd_cli_cmd_fst(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + int i; + int total; + + if (argc <= 0) { + printf("FST command: parameters are required.\n"); + return -1; + } + + total = os_snprintf(cmd, sizeof(cmd), "FST-MANAGER"); + + for (i = 0; i < argc; i++) { + res = os_snprintf(cmd + total, sizeof(cmd) - total, " %s", + argv[i]); + if (os_snprintf_error(sizeof(cmd) - total, res)) { + printf("Too long fst command.\n"); + return -1; + } + total += res; + } + return wpa_ctrl_command(ctrl, cmd); +} +#endif /* CONFIG_FST */ + + +#ifdef CONFIG_IEEE80211AX +static int hostapd_cli_cmd_color_change(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "COLOR_CHANGE", 1, argc, argv); +} +#endif /* CONFIG_IEEE80211AX */ + + +static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + char cmd[256]; + int res; + int i; + char *tmp; + int total; + + if (argc < 2) { + printf("Invalid chan_switch command: needs at least two " + "arguments (count and freq)\n" + "usage: [sec_channel_offset=] " + "[center_freq1=] [center_freq2=] [bandwidth=] " + "[blocktx] [ht|vht|he|eht]\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "CHAN_SWITCH %s %s", + argv[0], argv[1]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long CHAN_SWITCH command.\n"); + return -1; + } + + total = res; + for (i = 2; i < argc; i++) { + tmp = cmd + total; + res = os_snprintf(tmp, sizeof(cmd) - total, " %s", argv[i]); + if (os_snprintf_error(sizeof(cmd) - total, res)) { + printf("Too long CHAN_SWITCH command.\n"); + return -1; + } + total += res; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_notify_cw_change(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "NOTIFY_CW_CHANGE", 1, argc, argv); +} + + +static int hostapd_cli_cmd_enable(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "ENABLE"); +} + + +static int hostapd_cli_cmd_reload(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RELOAD"); +} + + +static int hostapd_cli_cmd_reload_bss(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RELOAD_BSS"); +} + + +static int hostapd_cli_cmd_reload_config(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RELOAD_CONFIG"); +} + + +static int hostapd_cli_cmd_disable(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DISABLE"); +} + + +static int hostapd_cli_cmd_enable_mld(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "ENABLE_MLD"); +} + + +static int hostapd_cli_cmd_disable_mld(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DISABLE_MLD"); +} + + +static int hostapd_cli_cmd_update_beacon(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "UPDATE_BEACON"); +} + + +static int hostapd_cli_cmd_stop_ap(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "STOP_AP"); +} + + +static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc < 2 || argc > 4) { + printf("Invalid vendor command\n" + "usage: [] [nested=<0|1>]\n"); + return -1; + } + + 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; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "ERP_FLUSH"); +} + + +static int hostapd_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + res = os_snprintf(cmd, sizeof(cmd), "LOG_LEVEL%s%s%s%s", + argc >= 1 ? " " : "", + argc >= 1 ? argv[0] : "", + argc == 2 ? " " : "", + argc == 2 ? argv[1] : ""); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long option\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + if (argc == 0) + return -1; + return hostapd_cli_cmd(ctrl, argv[0], 0, argc - 1, &argv[1]); +} + + +static int hostapd_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PMKSA"); +} + + +static int hostapd_cli_cmd_pmksa_flush(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "PMKSA_FLUSH"); +} + + +static int hostapd_cli_cmd_set_neighbor(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[2048]; + int res; + + if (argc < 3 || argc > 6) { + printf("Invalid set_neighbor command: needs 3-6 arguments\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "SET_NEIGHBOR %s %s %s %s %s %s", + argv[0], argv[1], argv[2], argc >= 4 ? argv[3] : "", + argc >= 5 ? argv[4] : "", argc == 6 ? argv[5] : ""); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long SET_NEIGHBOR command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +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[]) +{ + return hostapd_cli_cmd(ctrl, "REMOVE_NEIGHBOR", 1, argc, argv); +} + + +static int hostapd_cli_cmd_req_lci(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 1) { + printf("Invalid req_lci command - requires destination address\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "REQ_LCI %s", argv[0]); + if (os_snprintf_error(sizeof(cmd), res)) { + printf("Too long REQ_LCI command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int hostapd_cli_cmd_req_range(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + if (argc < 4) { + printf("Invalid req_range command: needs at least 4 arguments - dest address, randomization interval, min AP count, and 1 to 16 AP addresses\n"); + return -1; + } + + return hostapd_cli_cmd(ctrl, "REQ_RANGE", 4, argc, argv); +} + + +static int hostapd_cli_cmd_driver_flags(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DRIVER_FLAGS"); +} + + +static int hostapd_cli_cmd_driver_flags2(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DRIVER_FLAGS2"); +} + + +#ifdef CONFIG_DPP + +static int hostapd_cli_cmd_dpp_qr_code(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_QR_CODE", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_bootstrap_gen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GEN", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_bootstrap_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_REMOVE", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_bootstrap_get_uri(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_GET_URI", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_bootstrap_info(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_BOOTSTRAP_INFO", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_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[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_AUTH_INIT", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_listen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_LISTEN", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_stop_listen(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "DPP_STOP_LISTEN"); +} + + +static int hostapd_cli_cmd_dpp_configurator_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_ADD", 0, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_configurator_remove(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_REMOVE", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_configurator_get_key(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_GET_KEY", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_configurator_sign(struct wpa_ctrl *ctrl, + int argc, char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_CONFIGURATOR_SIGN", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_pkex_add(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_PKEX_ADD", 1, argc, argv); +} + + +static int hostapd_cli_cmd_dpp_pkex_remove(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_PKEX_REMOVE", 1, argc, argv); +} + + +#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", 0, 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 */ + + +#ifdef CONFIG_DPP3 +static int hostapd_cli_cmd_dpp_push_button(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DPP_PUSH_BUTTON", 0, argc, argv); +} +#endif /* CONFIG_DPP3 */ +#endif /* CONFIG_DPP */ + + +static int hostapd_cli_cmd_accept_macacl(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "ACCEPT_ACL", 1, argc, argv); +} + + +static int hostapd_cli_cmd_deny_macacl(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "DENY_ACL", 1, argc, argv); +} + + +static int hostapd_cli_cmd_poll_sta(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "POLL_STA", 1, argc, argv); +} + + +static int hostapd_cli_cmd_req_beacon(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "REQ_BEACON", 2, argc, argv); +} + + +static int hostapd_cli_cmd_req_link_measurement(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return hostapd_cli_cmd(ctrl, "REQ_LINK_MEASUREMENT", 1, argc, argv); +} + + +static int hostapd_cli_cmd_reload_wpa_psk(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RELOAD_WPA_PSK"); +} + + +#ifdef CONFIG_IEEE80211R_AP + +static int hostapd_cli_cmd_get_rxkhs(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "GET_RXKHS"); +} + + +static int hostapd_cli_cmd_reload_rxkhs(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + return wpa_ctrl_command(ctrl, "RELOAD_RXKHS"); +} + +#endif /* CONFIG_IEEE80211R_AP */ + + +#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[]); + char ** (*completion)(const char *str, int pos); + const char *usage; +}; + +static const struct hostapd_cli_cmd hostapd_cli_commands[] = { + { "ping", hostapd_cli_cmd_ping, NULL, + "= pings hostapd" }, + { "mib", hostapd_cli_cmd_mib, NULL, + "= get MIB variables (dot1x, dot11, radius)" }, + { "relog", hostapd_cli_cmd_relog, NULL, + "= reload/truncate debug log output file" }, + { "close_log", hostapd_cli_cmd_close_log, NULL, + "= disable debug log output file" }, + { "status", hostapd_cli_cmd_status, NULL, + "= show interface status info" }, + { "sta", hostapd_cli_cmd_sta, hostapd_complete_stations, + " = get MIB variables for one station" }, + { "all_sta", hostapd_cli_cmd_all_sta, NULL, + "= get MIB variables for all stations" }, + { "list_sta", hostapd_cli_cmd_list_sta, NULL, + "= list all stations" }, + { "new_sta", hostapd_cli_cmd_new_sta, NULL, + " = add a new station" }, + { "deauthenticate", hostapd_cli_cmd_deauthenticate, + hostapd_complete_stations, + " = deauthenticate a station" }, + { "disassociate", hostapd_cli_cmd_disassociate, + hostapd_complete_stations, + " = disassociate a station" }, +#ifdef CONFIG_TAXONOMY + { "signature", hostapd_cli_cmd_signature, hostapd_complete_stations, + " = get taxonomy signature for a station" }, +#endif /* CONFIG_TAXONOMY */ + { "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations, + " = send SA Query to a station" }, +#ifdef CONFIG_WPS + { "wps_pin", hostapd_cli_cmd_wps_pin, NULL, + " [timeout] [addr] = add WPS Enrollee PIN" }, + { "wps_check_pin", hostapd_cli_cmd_wps_check_pin, NULL, + " = verify PIN checksum" }, + { "wps_pbc", hostapd_cli_cmd_wps_pbc, NULL, + "= indicate button pushed to initiate PBC" }, + { "wps_cancel", hostapd_cli_cmd_wps_cancel, NULL, + "= cancel the pending WPS operation" }, +#ifdef CONFIG_WPS_NFC + { "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read, NULL, + " = report read NFC tag with WPS data" }, + { "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token, NULL, + " = build NFC configuration token" }, + { "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token, NULL, + " = manager NFC password token" }, + { "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel, NULL, + NULL }, +#endif /* CONFIG_WPS_NFC */ + { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin, NULL, + " [params..] = enable/disable AP PIN" }, + { "wps_config", hostapd_cli_cmd_wps_config, NULL, + " = configure AP" }, + { "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL, + "= show current WPS status" }, +#endif /* CONFIG_WPS */ + { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL, + "= send Disassociation Imminent notification" }, + { "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL, + "= send ESS Dissassociation Imminent notification" }, + { "bss_tm_req", hostapd_cli_cmd_bss_tm_req, NULL, + "= send BSS Transition Management Request" }, + { "get_config", hostapd_cli_cmd_get_config, NULL, + "= show current configuration" }, + { "help", hostapd_cli_cmd_help, hostapd_cli_complete_help, + "= show this usage help" }, + { "interface", hostapd_cli_cmd_interface, hostapd_complete_interface, + "[ifname] = show interfaces/select interface" }, +#ifdef CONFIG_FST + { "fst", hostapd_cli_cmd_fst, NULL, + " = send FST-MANAGER control interface command" }, +#endif /* CONFIG_FST */ + { "raw", hostapd_cli_cmd_raw, NULL, + " = send unprocessed command" }, + { "level", hostapd_cli_cmd_level, NULL, + " = change debug level" }, + { "license", hostapd_cli_cmd_license, NULL, + "= show full hostapd_cli license" }, + { "quit", hostapd_cli_cmd_quit, NULL, + "= exit hostapd_cli" }, + { "set", hostapd_cli_cmd_set, hostapd_complete_set, + " = set runtime variables" }, + { "get", hostapd_cli_cmd_get, hostapd_complete_get, + " = get runtime info" }, + { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set, NULL, + " = set QoS Map set element" }, + { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf, + hostapd_complete_stations, + " = send QoS Map Configure frame" }, + { "chan_switch", hostapd_cli_cmd_chan_switch, NULL, + " [sec_channel_offset=] [center_freq1=]\n" + " [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n" + " = initiate channel switch announcement" }, +#ifdef CONFIG_IEEE80211AX + { "color_change", hostapd_cli_cmd_color_change, NULL, + " = initiate BSS color change to set the specified color\n" + "Value 0 will disable the color.\n"}, +#endif /* CONFIG_IEEE80211AX */ + { "notify_cw_change", hostapd_cli_cmd_notify_cw_change, NULL, + " = 0 - 20 MHz, 1 - 40 MHz, 2 - 80 MHz, 3 - 160 MHz" }, + { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL, + " \n" + " = send WNM-Notification Subscription Remediation Request" }, + { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req, NULL, + " [url]\n" + " = send WNM-Notification imminent deauthentication indication" }, + { "vendor", hostapd_cli_cmd_vendor, NULL, + " []\n" + " = send vendor driver command" }, + { "enable", hostapd_cli_cmd_enable, NULL, + "= enable hostapd on current interface" }, + { "reload", hostapd_cli_cmd_reload, NULL, + "= reload configuration for current interface" }, + { "reload_bss", hostapd_cli_cmd_reload_bss, NULL, + "= reload configuration for current BSS" }, + { "reload_config", hostapd_cli_cmd_reload_config, NULL, + "= reload configuration for current interface" }, + { "disable", hostapd_cli_cmd_disable, NULL, + "= disable hostapd on current interface" }, + { "enable_mld", hostapd_cli_cmd_enable_mld, NULL, + "= enable AP MLD to which the interface is affiliated" }, + { "disable_mld", hostapd_cli_cmd_disable_mld, NULL, + "= disable AP MLD to which the interface is affiliated" }, + { "update_beacon", hostapd_cli_cmd_update_beacon, NULL, + "= update Beacon frame contents\n"}, + { "stop_ap", hostapd_cli_cmd_stop_ap, NULL, + "= stop AP\n"}, + { "erp_flush", hostapd_cli_cmd_erp_flush, NULL, + "= drop all ERP keys"}, + { "log_level", hostapd_cli_cmd_log_level, NULL, + "[level] = show/change log verbosity level" }, + { "pmksa", hostapd_cli_cmd_pmksa, NULL, + " = show PMKSA cache entries" }, + { "pmksa_flush", hostapd_cli_cmd_pmksa_flush, NULL, + " = flush PMKSA cache" }, + { "set_neighbor", hostapd_cli_cmd_set_neighbor, NULL, + " [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, + " [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, + " = send FTM range request"}, + { "driver_flags", hostapd_cli_cmd_driver_flags, NULL, + " = show supported driver flags"}, + { "driver_flags2", hostapd_cli_cmd_driver_flags2, NULL, + " = show supported driver flags2"}, +#ifdef CONFIG_DPP + { "dpp_qr_code", hostapd_cli_cmd_dpp_qr_code, NULL, + "report a scanned DPP URI from a QR Code" }, + { "dpp_bootstrap_gen", hostapd_cli_cmd_dpp_bootstrap_gen, NULL, + "type= [chan=..] [mac=..] [info=..] [curve=..] [key=..] = generate DPP bootstrap information" }, + { "dpp_bootstrap_remove", hostapd_cli_cmd_dpp_bootstrap_remove, NULL, + "*| = remove DPP bootstrap information" }, + { "dpp_bootstrap_get_uri", hostapd_cli_cmd_dpp_bootstrap_get_uri, NULL, + " = 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, + " = start DPP listen" }, + { "dpp_stop_listen", hostapd_cli_cmd_dpp_stop_listen, NULL, + "= stop DPP listen" }, + { "dpp_configurator_add", hostapd_cli_cmd_dpp_configurator_add, NULL, + "[curve=..] [key=..] = add DPP configurator" }, + { "dpp_configurator_remove", hostapd_cli_cmd_dpp_configurator_remove, + NULL, + "*| = remove DPP configurator" }, + { "dpp_configurator_get_key", hostapd_cli_cmd_dpp_configurator_get_key, + NULL, + " = Get DPP configurator's private key" }, + { "dpp_configurator_sign", hostapd_cli_cmd_dpp_configurator_sign, NULL, + "conf= configurator= = generate self DPP configuration" }, + { "dpp_pkex_add", hostapd_cli_cmd_dpp_pkex_add, NULL, + "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 */ +#ifdef CONFIG_DPP3 + { "dpp_push_button", hostapd_cli_cmd_dpp_push_button, NULL, + "= press DPP push button" }, +#endif /* CONFIG_DPP3 */ +#endif /* CONFIG_DPP */ + { "accept_acl", hostapd_cli_cmd_accept_macacl, NULL, + "=Add/Delete/Show/Clear accept MAC ACL" }, + { "deny_acl", hostapd_cli_cmd_deny_macacl, NULL, + "=Add/Delete/Show/Clear deny MAC ACL" }, + { "poll_sta", hostapd_cli_cmd_poll_sta, hostapd_complete_stations, + " = poll a STA to check connectivity with a QoS null frame" }, + { "req_beacon", hostapd_cli_cmd_req_beacon, NULL, + " [req_mode=] = send a Beacon report request to a station" }, + { "req_link_measurement", hostapd_cli_cmd_req_link_measurement, NULL, + " = send a link measurement report request to a station"}, + { "reload_wpa_psk", hostapd_cli_cmd_reload_wpa_psk, NULL, + "= reload wpa_psk_file only" }, +#ifdef CONFIG_IEEE80211R_AP + { "reload_rxkhs", hostapd_cli_cmd_reload_rxkhs, NULL, + "= reload R0KHs and R1KHs" }, + { "get_rxkhs", hostapd_cli_cmd_get_rxkhs, NULL, + "= get R0KHs and R1KHs" }, +#endif /* CONFIG_IEEE80211R_AP */ +#ifdef ANDROID + { "driver", hostapd_cli_cmd_driver, NULL, + " [] = send driver command data" }, +#endif /* ANDROID */ + { NULL, NULL, NULL, NULL } +}; + + +/* + * Prints command usage, lines are padded with the specified string. + */ +static void print_cmd_help(FILE *stream, const struct hostapd_cli_cmd *cmd, + const char *pad) +{ + char c; + size_t n; + + if (cmd->usage == NULL) + return; + fprintf(stream, "%s%s ", pad, cmd->cmd); + for (n = 0; (c = cmd->usage[n]); n++) { + fprintf(stream, "%c", c); + if (c == '\n') + fprintf(stream, "%s", pad); + } + fprintf(stream, "\n"); +} + + +static void print_help(FILE *stream, const char *cmd) +{ + int n; + + fprintf(stream, "commands:\n"); + for (n = 0; hostapd_cli_commands[n].cmd; n++) { + if (cmd == NULL || str_starts(hostapd_cli_commands[n].cmd, cmd)) + print_cmd_help(stream, &hostapd_cli_commands[n], " "); + } +} + + +static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + const struct hostapd_cli_cmd *cmd, *match = NULL; + int count; + + count = 0; + cmd = hostapd_cli_commands; + while (cmd->cmd) { + if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == 0) { + match = cmd; + if (os_strcasecmp(cmd->cmd, argv[0]) == 0) { + /* we have an exact match */ + count = 1; + break; + } + count++; + } + cmd++; + } + + if (count > 1) { + printf("Ambiguous command '%s'; possible commands:", argv[0]); + cmd = hostapd_cli_commands; + while (cmd->cmd) { + if (strncasecmp(cmd->cmd, argv[0], strlen(argv[0])) == + 0) { + printf(" %s", cmd->cmd); + } + cmd++; + } + printf("\n"); + } else if (count == 0) { + printf("Unknown command '%s'\n", argv[0]); + } else { + match->handler(ctrl, argc - 1, &argv[1]); + } +} + + +static void cli_event(const char *str) +{ + const char *start, *s; + + start = os_strchr(str, '>'); + if (start == NULL) + return; + + start++; + + if (str_starts(start, AP_STA_CONNECTED)) { + s = os_strchr(start, ' '); + if (s == NULL) + return; + cli_txt_list_add(&stations, s + 1); + return; + } + + if (str_starts(start, AP_STA_DISCONNECTED)) { + s = os_strchr(start, ' '); + if (s == NULL) + return; + cli_txt_list_del_addr(&stations, s + 1); + return; + } +} + + +static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read, + int action_monitor) +{ + int first = 1; + if (ctrl_conn == NULL) + return; + while (wpa_ctrl_pending(ctrl)) { + char buf[4096]; + size_t len = sizeof(buf) - 1; + if (wpa_ctrl_recv(ctrl, buf, &len) == 0) { + buf[len] = '\0'; + if (action_monitor) + hostapd_cli_action_process(buf, len); + else { + cli_event(buf); + if (in_read && first) + printf("\n"); + first = 0; + printf("%s\n", buf); + } + } else { + printf("Could not read pending message.\n"); + break; + } + } +} + + +static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + hostapd_cli_recv_pending(ctrl_conn, 0, 0); +} + + +static void hostapd_cli_ping(void *eloop_ctx, void *timeout_ctx) +{ + if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) { + printf("Connection to hostapd lost - trying to reconnect\n"); + hostapd_cli_close_connection(); + } + if (!ctrl_conn && hostapd_cli_reconnect(ctrl_ifname) == 0) + printf("Connection to hostapd re-established\n"); + if (ctrl_conn) + hostapd_cli_recv_pending(ctrl_conn, 1, 0); + eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); +} + + +static void hostapd_cli_eloop_terminate(int sig, void *signal_ctx) +{ + eloop_terminate(); +} + + +static void hostapd_cli_edit_cmd_cb(void *ctx, char *cmd) +{ + char *argv[max_args]; + int argc; + argc = tokenize_cmd(cmd, argv); + if (argc) + wpa_request(ctrl_conn, argc, argv); +} + + +static void hostapd_cli_edit_eof_cb(void *ctx) +{ + eloop_terminate(); +} + + +static char ** list_cmd_list(void) +{ + char **res; + int i, count; + + count = ARRAY_SIZE(hostapd_cli_commands); + res = os_calloc(count + 1, sizeof(char *)); + if (res == NULL) + return NULL; + + for (i = 0; hostapd_cli_commands[i].cmd; i++) { + res[i] = os_strdup(hostapd_cli_commands[i].cmd); + if (res[i] == NULL) + break; + } + + return res; +} + + +static char ** hostapd_cli_cmd_completion(const char *cmd, const char *str, + int pos) +{ + int i; + + for (i = 0; hostapd_cli_commands[i].cmd; i++) { + if (os_strcasecmp(hostapd_cli_commands[i].cmd, cmd) != 0) + continue; + if (hostapd_cli_commands[i].completion) + return hostapd_cli_commands[i].completion(str, pos); + if (!hostapd_cli_commands[i].usage) + return NULL; + edit_clear_line(); + printf("\r%s\n", hostapd_cli_commands[i].usage); + edit_redraw(); + break; + } + + return NULL; +} + + +static char ** hostapd_cli_edit_completion_cb(void *ctx, const char *str, + int pos) +{ + char **res; + const char *end; + char *cmd; + + end = os_strchr(str, ' '); + if (end == NULL || str + pos < end) + return list_cmd_list(); + + cmd = os_malloc(pos + 1); + if (cmd == NULL) + return NULL; + os_memcpy(cmd, str, pos); + cmd[end - str] = '\0'; + res = hostapd_cli_cmd_completion(cmd, str, pos); + os_free(cmd); + return res; +} + + +static void hostapd_cli_interactive(void) +{ + char *hfile = NULL; + char *home; + + printf("\nInteractive mode\n\n"); + +#ifdef CONFIG_HOSTAPD_CLI_HISTORY_DIR + home = CONFIG_HOSTAPD_CLI_HISTORY_DIR; +#else /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */ + home = getenv("HOME"); +#endif /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */ + if (home) { + const char *fname = ".hostapd_cli_history"; + int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1; + hfile = os_malloc(hfile_len); + if (hfile) + os_snprintf(hfile, hfile_len, "%s/%s", home, fname); + } + + edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb, + hostapd_cli_edit_completion_cb, NULL, hfile, NULL); + eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL); + + eloop_run(); + + cli_txt_list_flush(&stations); + edit_deinit(hfile, NULL); + os_free(hfile); + eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL); +} + + +static void hostapd_cli_cleanup(void) +{ + hostapd_cli_close_connection(); + if (pid_file) + os_daemonize_terminate(pid_file); + + os_program_deinit(); +} + + +static void hostapd_cli_action_ping(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_ctrl *ctrl = eloop_ctx; + char buf[256]; + size_t len; + + /* verify that connection is still working */ + len = sizeof(buf) - 1; + if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len, + hostapd_cli_action_cb) < 0 || + len < 4 || os_memcmp(buf, "PONG", 4) != 0) { + printf("hostapd did not reply to PING command - exiting\n"); + eloop_terminate(); + return; + } + eloop_register_timeout(ping_interval, 0, hostapd_cli_action_ping, + ctrl, NULL); +} + + +static void hostapd_cli_action_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_ctrl *ctrl = eloop_ctx; + + hostapd_cli_recv_pending(ctrl, 0, 1); +} + + +static void hostapd_cli_action(struct wpa_ctrl *ctrl) +{ + int fd; + + fd = wpa_ctrl_get_fd(ctrl); + eloop_register_timeout(ping_interval, 0, hostapd_cli_action_ping, + ctrl, NULL); + eloop_register_read_sock(fd, hostapd_cli_action_receive, ctrl, NULL); + eloop_run(); + eloop_cancel_timeout(hostapd_cli_action_ping, ctrl, NULL); + eloop_unregister_read_sock(fd); +} + + +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:rs:v"); + if (c < 0) + break; + switch (c) { + case 'a': + action_file = optarg; + break; + case 'B': + daemonize = 1; + break; + case 'G': + ping_interval = atoi(optarg); + break; + case 'h': + usage(); + return 0; + case 'v': + printf("%s\n", hostapd_cli_version); + return 0; + case 'i': + os_free(ctrl_ifname); + ctrl_ifname = os_strdup(optarg); + break; + case 'p': + ctrl_iface_dir = optarg; + break; + case 'P': + pid_file = optarg; + break; + case 'r': + reconnect = 1; + break; + case 's': + client_socket_dir = optarg; + break; + default: + usage(); + return -1; + } + } + + interactive = (argc == optind) && (action_file == NULL); + + if (interactive) { + printf("%s\n\n%s\n\n", hostapd_cli_version, cli_license); + } + + if (eloop_init()) + return -1; + + for (;;) { + if (ctrl_ifname == NULL) { + struct dirent *dent; + DIR *dir = opendir(ctrl_iface_dir); + if (dir) { + while ((dent = readdir(dir))) { + if (os_strcmp(dent->d_name, ".") == 0 + || + os_strcmp(dent->d_name, "..") == 0) + continue; + printf("Selected interface '%s'\n", + dent->d_name); + ctrl_ifname = os_strdup(dent->d_name); + break; + } + closedir(dir); + } + } + hostapd_cli_reconnect(ctrl_ifname); + if (ctrl_conn) { + if (warning_displayed) + printf("Connection established.\n"); + break; + } + if (!interactive && !reconnect) { + perror("Failed to connect to hostapd - " + "wpa_ctrl_open"); + return -1; + } + + if (!warning_displayed) { + printf("Could not connect to hostapd - re-trying\n"); + warning_displayed = 1; + } + os_sleep(1, 0); + continue; + } + + eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL); + + if (action_file && !hostapd_cli_attached) + return -1; + if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue()) + return -1; + 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); + else + wpa_request(ctrl_conn, argc - optind, &argv[optind]); + + unregister_event_handler(ctrl_conn); + os_free(ctrl_ifname); + eloop_destroy(); + hostapd_cli_cleanup(); + return 0; +} + +#else /* CONFIG_NO_CTRL_IFACE */ + +int main(int argc, char *argv[]) +{ + return -1; +} + +#endif /* CONFIG_NO_CTRL_IFACE */ diff --git a/hostapd/logwatch/README b/hostapd/logwatch/README new file mode 100644 index 000000000000..3cba51190963 --- /dev/null +++ b/hostapd/logwatch/README @@ -0,0 +1,9 @@ +Logwatch is a utility for analyzing system logs and provide a human +readable summary. This directory has a configuration file and a log +analyzer script for parsing hostapd system log entries for logwatch. +These files can be installed by copying them to following locations: + +/etc/log.d/conf/services/hostapd.conf +/etc/log.d/scripts/services/hostapd + +More information about logwatch is available from http://www.logwatch.org/ diff --git a/hostapd/logwatch/hostapd.conf b/hostapd/logwatch/hostapd.conf new file mode 100644 index 000000000000..5bebe6ad2c1b --- /dev/null +++ b/hostapd/logwatch/hostapd.conf @@ -0,0 +1,10 @@ +# Logwatch configuration for hostapd +# +# Copyright 2005 Henrik Brix Andersen +# Distributed under the terms of the GNU General Public License v2 +# Alternatively, this file may be distributed under the terms of the BSD License + +Title = "hostapd" +LogFile = messages +*OnlyService = hostapd +*RemoveHeaders diff --git a/hostapd/main.c b/hostapd/main.c new file mode 100644 index 000000000000..00e02bb034c9 --- /dev/null +++ b/hostapd/main.c @@ -0,0 +1,1070 @@ +/* + * hostapd / main() + * Copyright (c) 2002-2022, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "utils/includes.h" +#ifndef CONFIG_NATIVE_WINDOWS +#include +#include +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/uuid.h" +#include "crypto/crypto.h" +#include "crypto/random.h" +#include "crypto/tls.h" +#include "common/version.h" +#include "common/dpp.h" +#include "drivers/driver.h" +#include "eap_server/eap.h" +#include "eap_server/tncs.h" +#include "ap/hostapd.h" +#include "ap/ap_config.h" +#include "ap/ap_drv_ops.h" +#include "ap/dpp_hostapd.h" +#include "fst/fst.h" +#include "config_file.h" +#include "eap_register.h" +#include "ctrl_iface.h" + + +struct hapd_global { + void **drv_priv; + size_t drv_count; +}; + +static struct hapd_global global; + + +#ifndef CONFIG_NO_HOSTAPD_LOGGER +static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module, + int level, const char *txt, size_t len) +{ + struct hostapd_data *hapd = ctx; + char *format, *module_str; + int maxlen; + int conf_syslog_level, conf_stdout_level; + unsigned int conf_syslog, conf_stdout; + + maxlen = len + 100; + format = os_malloc(maxlen); + if (!format) + return; + + if (hapd && hapd->conf) { + conf_syslog_level = hapd->conf->logger_syslog_level; + conf_stdout_level = hapd->conf->logger_stdout_level; + conf_syslog = hapd->conf->logger_syslog; + conf_stdout = hapd->conf->logger_stdout; + } else { + conf_syslog_level = conf_stdout_level = 0; + conf_syslog = conf_stdout = (unsigned int) -1; + } + + switch (module) { + case HOSTAPD_MODULE_IEEE80211: + module_str = "IEEE 802.11"; + break; + case HOSTAPD_MODULE_IEEE8021X: + module_str = "IEEE 802.1X"; + break; + case HOSTAPD_MODULE_RADIUS: + module_str = "RADIUS"; + break; + case HOSTAPD_MODULE_WPA: + module_str = "WPA"; + break; + case HOSTAPD_MODULE_DRIVER: + module_str = "DRIVER"; + break; + case HOSTAPD_MODULE_MLME: + module_str = "MLME"; + break; + default: + module_str = NULL; + break; + } + + if (hapd && hapd->conf && addr) + os_snprintf(format, maxlen, "%s: STA " MACSTR "%s%s: %s", + hapd->conf->iface, MAC2STR(addr), + module_str ? " " : "", module_str ? module_str : "", + txt); + else if (hapd && hapd->conf) + os_snprintf(format, maxlen, "%s:%s%s %s", + hapd->conf->iface, module_str ? " " : "", + module_str ? module_str : "", txt); + else if (addr) + os_snprintf(format, maxlen, "STA " MACSTR "%s%s: %s", + MAC2STR(addr), module_str ? " " : "", + module_str ? module_str : "", txt); + else + os_snprintf(format, maxlen, "%s%s%s", + module_str ? module_str : "", + module_str ? ": " : "", txt); + +#ifdef CONFIG_DEBUG_SYSLOG + if (wpa_debug_syslog) + conf_stdout = 0; +#endif /* CONFIG_DEBUG_SYSLOG */ + if ((conf_stdout & module) && level >= conf_stdout_level) { + wpa_debug_print_timestamp(); + wpa_printf(MSG_INFO, "%s", format); + } + +#ifndef CONFIG_NATIVE_WINDOWS + if ((conf_syslog & module) && level >= conf_syslog_level) { + int priority; + switch (level) { + case HOSTAPD_LEVEL_DEBUG_VERBOSE: + case HOSTAPD_LEVEL_DEBUG: + priority = LOG_DEBUG; + break; + case HOSTAPD_LEVEL_INFO: + priority = LOG_INFO; + break; + case HOSTAPD_LEVEL_NOTICE: + priority = LOG_NOTICE; + break; + case HOSTAPD_LEVEL_WARNING: + priority = LOG_WARNING; + break; + default: + priority = LOG_INFO; + break; + } + syslog(priority, "%s", format); + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + os_free(format); +} +#endif /* CONFIG_NO_HOSTAPD_LOGGER */ + + +/** + * hostapd_driver_init - Preparate driver interface + */ +static int hostapd_driver_init(struct hostapd_iface *iface) +{ + struct wpa_init_params params; + size_t i; + struct hostapd_data *hapd = iface->bss[0]; + struct hostapd_bss_config *conf = hapd->conf; + u8 *b = conf->bssid; + struct wpa_driver_capa capa; +#ifdef CONFIG_IEEE80211BE + struct hostapd_data *h_hapd = NULL; +#endif /* CONFIG_IEEE80211BE */ + + if (hapd->driver == NULL || hapd->driver->hapd_init == NULL) { + wpa_printf(MSG_ERROR, "No hostapd driver wrapper available"); + return -1; + } + +#ifdef CONFIG_IEEE80211BE + if (conf->mld_ap) + h_hapd = hostapd_mld_get_first_bss(hapd); + + if (h_hapd) { + hapd->drv_priv = h_hapd->drv_priv; + hapd->interface_added = h_hapd->interface_added; + + /* + * All interfaces participating in the AP MLD would have + * the same MLD address, which is the interface hardware + * address, while the interface address would be + * derived from the original interface address if BSSID + * is not configured, and otherwise it would be the + * configured BSSID. + */ + if (is_zero_ether_addr(b)) { + os_memcpy(hapd->own_addr, h_hapd->mld->mld_addr, + ETH_ALEN); + random_mac_addr_keep_oui(hapd->own_addr); + } else { + os_memcpy(hapd->own_addr, b, ETH_ALEN); + } + + hostapd_mld_add_link(hapd); + wpa_printf(MSG_DEBUG, + "Setup of non first link (%d) BSS of MLD %s", + hapd->mld_link_id, hapd->conf->iface); + + goto setup_mld; + } +#endif /* CONFIG_IEEE80211BE */ + + /* Initialize the driver interface */ + if (is_zero_ether_addr(b)) + b = NULL; + + os_memset(¶ms, 0, sizeof(params)); + for (i = 0; wpa_drivers[i]; i++) { + if (wpa_drivers[i] != hapd->driver) + continue; + + if (global.drv_priv[i] == NULL && + wpa_drivers[i]->global_init) { + global.drv_priv[i] = + wpa_drivers[i]->global_init(iface->interfaces); + if (global.drv_priv[i] == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize " + "driver '%s'", + wpa_drivers[i]->name); + return -1; + } + } + + params.global_priv = global.drv_priv[i]; + break; + } + params.bssid = b; +#ifdef CONFIG_IEEE80211BE + /* + * Use the configured MLD MAC address as the interface hardware address + * if this AP is a part of an AP MLD. + */ + if (hapd->conf->mld_ap) { + if (!is_zero_ether_addr(hapd->conf->mld_addr)) + params.bssid = hapd->conf->mld_addr; + else + params.bssid = NULL; + } +#endif /* CONFIG_IEEE80211BE */ + + params.ifname = hapd->conf->iface; + params.driver_params = hapd->iconf->driver_params; + params.use_pae_group_addr = hapd->conf->use_pae_group_addr; + + params.num_bridge = hapd->iface->num_bss; + params.bridge = os_calloc(hapd->iface->num_bss, sizeof(char *)); + if (params.bridge == NULL) + return -1; + for (i = 0; i < hapd->iface->num_bss; i++) { + struct hostapd_data *bss = hapd->iface->bss[i]; + if (bss->conf->bridge[0]) + params.bridge[i] = bss->conf->bridge; + } + + params.own_addr = hapd->own_addr; + + hapd->drv_priv = hapd->driver->hapd_init(hapd, ¶ms); + os_free(params.bridge); + if (hapd->drv_priv == NULL) { + wpa_printf(MSG_ERROR, "%s driver initialization failed.", + hapd->driver->name); + hapd->driver = NULL; + return -1; + } + +#ifdef CONFIG_IEEE80211BE + /* + * This is the first interface added to the AP MLD, so have the + * interface hardware address be the MLD address, while the link address + * would be derived from the original interface address if BSSID is not + * configured, and otherwise it would be the configured BSSID. + */ + if (hapd->conf->mld_ap) { + os_memcpy(hapd->mld->mld_addr, hapd->own_addr, ETH_ALEN); + + if (!b) + random_mac_addr_keep_oui(hapd->own_addr); + else + os_memcpy(hapd->own_addr, b, ETH_ALEN); + + hostapd_mld_add_link(hapd); + wpa_printf(MSG_DEBUG, "Setup of first link (%d) BSS of MLD %s", + hapd->mld_link_id, hapd->conf->iface); + } + +setup_mld: +#endif /* CONFIG_IEEE80211BE */ + + if (hapd->driver->get_capa && + hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) { + struct wowlan_triggers *triggs; + + iface->drv_flags = capa.flags; + iface->drv_flags2 = capa.flags2; + iface->drv_rrm_flags = capa.rrm_flags; + iface->probe_resp_offloads = capa.probe_resp_offloads; + /* + * Use default extended capa values from per-radio information + */ + iface->extended_capa = capa.extended_capa; + iface->extended_capa_mask = capa.extended_capa_mask; + iface->extended_capa_len = capa.extended_capa_len; + iface->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs; + + /* + * Override extended capa with per-interface type (AP), if + * available from the driver. + */ + hostapd_get_ext_capa(iface); + + hostapd_get_mld_capa(iface); + + triggs = wpa_get_wowlan_triggers(conf->wowlan_triggers, &capa); + if (triggs && hapd->driver->set_wowlan) { + if (hapd->driver->set_wowlan(hapd->drv_priv, triggs)) + wpa_printf(MSG_ERROR, "set_wowlan failed"); + } + os_free(triggs); + + iface->mbssid_max_interfaces = capa.mbssid_max_interfaces; + iface->ema_max_periodicity = capa.ema_max_periodicity; + } + +#ifdef CONFIG_IEEE80211BE + if (hapd->conf->mld_ap) { + if (!(iface->drv_flags2 & WPA_DRIVER_FLAGS2_MLO)) { + wpa_printf(MSG_INFO, + "MLD: Not supported by the driver"); + return -1; + } + + /* Initialize the BSS parameter change to 1 */ + hapd->eht_mld_bss_param_change = 1; + + wpa_printf(MSG_DEBUG, + "MLD: Set link_id=%u, mld_addr=" MACSTR + ", own_addr=" MACSTR, + hapd->mld_link_id, MAC2STR(hapd->mld->mld_addr), + MAC2STR(hapd->own_addr)); + + hostapd_drv_link_add(hapd, hapd->mld_link_id, + hapd->own_addr); + } +#endif /* CONFIG_IEEE80211BE */ + + return 0; +} + + +/** + * hostapd_interface_init - Read configuration file and init BSS data + * + * This function is used to parse configuration file for a full interface (one + * or more BSSes sharing the same radio) and allocate memory for the BSS + * interfaces. No actual driver operations are started. + */ +static struct hostapd_iface * +hostapd_interface_init(struct hapd_interfaces *interfaces, const char *if_name, + const char *config_fname, int debug) +{ + struct hostapd_iface *iface; + int k; + + wpa_printf(MSG_DEBUG, "Configuration file: %s", config_fname); + iface = hostapd_init(interfaces, config_fname); + if (!iface) + return NULL; + + if (if_name) { + os_strlcpy(iface->conf->bss[0]->iface, if_name, + sizeof(iface->conf->bss[0]->iface)); + } + + iface->interfaces = interfaces; + + for (k = 0; k < debug; k++) { + if (iface->bss[0]->conf->logger_stdout_level > 0) + iface->bss[0]->conf->logger_stdout_level--; + } + + if (iface->conf->bss[0]->iface[0] == '\0' && + !hostapd_drv_none(iface->bss[0])) { + wpa_printf(MSG_ERROR, + "Interface name not specified in %s, nor by '-i' parameter", + config_fname); + hostapd_interface_deinit_free(iface); + return NULL; + } + + return iface; +} + + +/** + * handle_term - SIGINT and SIGTERM handler to terminate hostapd process + */ +static void handle_term(int sig, void *signal_ctx) +{ + wpa_printf(MSG_DEBUG, "Signal %d received - terminating", sig); + eloop_terminate(); +} + + +#ifndef CONFIG_NATIVE_WINDOWS + +static int handle_reload_iface(struct hostapd_iface *iface, void *ctx) +{ + if (hostapd_reload_config(iface) < 0) { + wpa_printf(MSG_WARNING, "Failed to read new configuration " + "file - continuing with old."); + } + return 0; +} + + +/** + * handle_reload - SIGHUP handler to reload configuration + */ +static void handle_reload(int sig, void *signal_ctx) +{ + struct hapd_interfaces *interfaces = signal_ctx; + wpa_printf(MSG_DEBUG, "Signal %d received - reloading configuration", + sig); + hostapd_for_each_interface(interfaces, handle_reload_iface, NULL); +} + + +static void handle_dump_state(int sig, void *signal_ctx) +{ + /* Not used anymore - ignore signal */ +} +#endif /* CONFIG_NATIVE_WINDOWS */ + + +static int hostapd_global_init(struct hapd_interfaces *interfaces, + const char *entropy_file) +{ + int i; + + os_memset(&global, 0, sizeof(global)); + + hostapd_logger_register_cb(hostapd_logger_cb); + + if (eap_server_register_methods()) { + wpa_printf(MSG_ERROR, "Failed to register EAP methods"); + return -1; + } + + if (eloop_init()) { + wpa_printf(MSG_ERROR, "Failed to initialize event loop"); + return -1; + } + interfaces->eloop_initialized = 1; + + random_init(entropy_file); + +#ifndef CONFIG_NATIVE_WINDOWS + eloop_register_signal(SIGHUP, handle_reload, interfaces); + eloop_register_signal(SIGUSR1, handle_dump_state, interfaces); +#endif /* CONFIG_NATIVE_WINDOWS */ + eloop_register_signal_terminate(handle_term, interfaces); + +#ifndef CONFIG_NATIVE_WINDOWS + openlog("hostapd", 0, LOG_DAEMON); +#endif /* CONFIG_NATIVE_WINDOWS */ + + for (i = 0; wpa_drivers[i]; i++) + global.drv_count++; + if (global.drv_count == 0) { + wpa_printf(MSG_ERROR, "No drivers enabled"); + return -1; + } + global.drv_priv = os_calloc(global.drv_count, sizeof(void *)); + if (global.drv_priv == NULL) + return -1; + + return 0; +} + + +static void hostapd_global_deinit(const char *pid_file, int eloop_initialized) +{ + int i; + + for (i = 0; wpa_drivers[i] && global.drv_priv; i++) { + if (!global.drv_priv[i]) + continue; + wpa_drivers[i]->global_deinit(global.drv_priv[i]); + } + os_free(global.drv_priv); + global.drv_priv = NULL; + +#ifdef EAP_SERVER_TNC + tncs_global_deinit(); +#endif /* EAP_SERVER_TNC */ + + random_deinit(); + + if (eloop_initialized) + eloop_destroy(); + +#ifndef CONFIG_NATIVE_WINDOWS + closelog(); +#endif /* CONFIG_NATIVE_WINDOWS */ + + eap_server_unregister_methods(); + + os_daemonize_terminate(pid_file); +} + + +static int hostapd_global_run(struct hapd_interfaces *ifaces, int daemonize, + const char *pid_file) +{ +#ifdef EAP_SERVER_TNC + int tnc = 0; + size_t i, k; + + for (i = 0; !tnc && i < ifaces->count; i++) { + for (k = 0; k < ifaces->iface[i]->num_bss; k++) { + if (ifaces->iface[i]->bss[0]->conf->tnc) { + tnc++; + break; + } + } + } + + if (tnc && tncs_global_init() < 0) { + wpa_printf(MSG_ERROR, "Failed to initialize TNCS"); + return -1; + } +#endif /* EAP_SERVER_TNC */ + + if (daemonize) { + if (os_daemonize(pid_file)) { + wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno)); + return -1; + } + if (eloop_sock_requeue()) { + wpa_printf(MSG_ERROR, "eloop_sock_requeue: %s", + strerror(errno)); + return -1; + } + } + + eloop_run(); + + return 0; +} + + +static void show_version(void) +{ + fprintf(stderr, + "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-2024, Jouni Malinen " + "and contributors\n", + VERSION_STR); +} + + +static void usage(void) +{ + show_version(); + fprintf(stderr, + "\n" + "usage: hostapd [-hdBKtvq] [-P ] [-e ] " + "\\\n" + " [-g ] [-G ]\\\n" + " [-i ]\\\n" + " \n" + "\n" + "options:\n" + " -h show this usage\n" + " -d show more debug messages (-dd for even more)\n" + " -B run daemon in the background\n" + " -e entropy file\n" + " -g global control interface path\n" + " -G group for control interfaces\n" + " -P PID file\n" + " -K include key data in debug messages\n" +#ifdef CONFIG_DEBUG_FILE + " -f log output to debug file instead of stdout\n" +#endif /* CONFIG_DEBUG_FILE */ +#ifdef CONFIG_DEBUG_LINUX_TRACING + " -T record to Linux tracing in addition to logging\n" + " (records all messages regardless of debug verbosity)\n" +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + " -i list of interface names to use\n" +#ifdef CONFIG_DEBUG_SYSLOG + " -s log output to syslog instead of stdout\n" +#endif /* CONFIG_DEBUG_SYSLOG */ + " -S start all the interfaces synchronously\n" + " -t include timestamps in some debug messages\n" + " -v show hostapd version\n" + " -q show less debug messages (-qq for even less)\n"); + + exit(1); +} + + +static const char * hostapd_msg_ifname_cb(void *ctx) +{ + struct hostapd_data *hapd = ctx; + if (hapd && hapd->conf) + return hapd->conf->iface; + return NULL; +} + + +static int hostapd_get_global_ctrl_iface(struct hapd_interfaces *interfaces, + const char *path) +{ +#ifndef CONFIG_CTRL_IFACE_UDP + char *pos; +#endif /* !CONFIG_CTRL_IFACE_UDP */ + + os_free(interfaces->global_iface_path); + interfaces->global_iface_path = os_strdup(path); + if (interfaces->global_iface_path == NULL) + return -1; + +#ifndef CONFIG_CTRL_IFACE_UDP + pos = os_strrchr(interfaces->global_iface_path, '/'); + if (pos == NULL) { + wpa_printf(MSG_ERROR, "No '/' in the global control interface " + "file"); + os_free(interfaces->global_iface_path); + interfaces->global_iface_path = NULL; + return -1; + } + + *pos = '\0'; + interfaces->global_iface_name = pos + 1; +#endif /* !CONFIG_CTRL_IFACE_UDP */ + + return 0; +} + + +static int hostapd_get_ctrl_iface_group(struct hapd_interfaces *interfaces, + const char *group) +{ +#ifndef CONFIG_NATIVE_WINDOWS + struct group *grp; + grp = getgrnam(group); + if (grp == NULL) { + wpa_printf(MSG_ERROR, "Unknown group '%s'", group); + return -1; + } + interfaces->ctrl_iface_group = grp->gr_gid; +#endif /* CONFIG_NATIVE_WINDOWS */ + return 0; +} + + +static int hostapd_get_interface_names(char ***if_names, + size_t *if_names_size, + char *arg) +{ + char *if_name, *tmp, **nnames; + size_t i; + + if (!arg) + return -1; + if_name = strtok_r(arg, ",", &tmp); + + while (if_name) { + nnames = os_realloc_array(*if_names, 1 + *if_names_size, + sizeof(char *)); + if (!nnames) + goto fail; + *if_names = nnames; + + (*if_names)[*if_names_size] = os_strdup(if_name); + if (!(*if_names)[*if_names_size]) + goto fail; + (*if_names_size)++; + if_name = strtok_r(NULL, ",", &tmp); + } + + return 0; + +fail: + for (i = 0; i < *if_names_size; i++) + os_free((*if_names)[i]); + os_free(*if_names); + *if_names = NULL; + *if_names_size = 0; + return -1; +} + + +#ifdef CONFIG_WPS +static int gen_uuid(const char *txt_addr) +{ + u8 addr[ETH_ALEN]; + u8 uuid[UUID_LEN]; + char buf[100]; + + if (hwaddr_aton(txt_addr, addr) < 0) + return -1; + + uuid_gen_mac_addr(addr, uuid); + if (uuid_bin2str(uuid, buf, sizeof(buf)) < 0) + return -1; + + printf("%s\n", buf); + + return 0; +} +#endif /* CONFIG_WPS */ + + +#ifndef HOSTAPD_CLEANUP_INTERVAL +#define HOSTAPD_CLEANUP_INTERVAL 10 +#endif /* HOSTAPD_CLEANUP_INTERVAL */ + +static int hostapd_periodic_call(struct hostapd_iface *iface, void *ctx) +{ + hostapd_periodic_iface(iface); + return 0; +} + + +/* Periodic cleanup tasks */ +static void hostapd_periodic(void *eloop_ctx, void *timeout_ctx) +{ + struct hapd_interfaces *interfaces = eloop_ctx; + + eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0, + hostapd_periodic, interfaces, NULL); + hostapd_for_each_interface(interfaces, hostapd_periodic_call, NULL); +} + + +static void hostapd_global_cleanup_mld(struct hapd_interfaces *interfaces) +{ +#ifdef CONFIG_IEEE80211BE + size_t i; + + if (!interfaces || !interfaces->mld) + return; + + for (i = 0; i < interfaces->mld_count; i++) { + if (!interfaces->mld[i]) + continue; + + os_free(interfaces->mld[i]); + interfaces->mld[i] = NULL; + } + + os_free(interfaces->mld); + interfaces->mld = NULL; + interfaces->mld_count = 0; +#endif /* CONFIG_IEEE80211BE */ +} + + +int main(int argc, char *argv[]) +{ + struct hapd_interfaces interfaces; + int ret = 1; + size_t i, j; + int c, debug = 0, daemonize = 0; + char *pid_file = NULL; + const char *log_file = NULL; + const char *entropy_file = NULL; + char **bss_config = NULL, **tmp_bss; + size_t num_bss_configs = 0; +#ifdef CONFIG_DEBUG_LINUX_TRACING + int enable_trace_dbg = 0; +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + int start_ifaces_in_sync = 0; + char **if_names = NULL; + size_t if_names_size = 0; +#ifdef CONFIG_DPP + struct dpp_global_config dpp_conf; +#endif /* CONFIG_DPP */ + + if (os_program_init()) + return -1; + + os_memset(&interfaces, 0, sizeof(interfaces)); + interfaces.reload_config = hostapd_reload_config; + interfaces.config_read_cb = hostapd_config_read; + interfaces.for_each_interface = hostapd_for_each_interface; + interfaces.ctrl_iface_init = hostapd_ctrl_iface_init; + interfaces.ctrl_iface_deinit = hostapd_ctrl_iface_deinit; + interfaces.driver_init = hostapd_driver_init; + interfaces.global_iface_path = NULL; + interfaces.global_iface_name = NULL; + interfaces.global_ctrl_sock = -1; + dl_list_init(&interfaces.global_ctrl_dst); +#ifdef CONFIG_ETH_P_OUI + dl_list_init(&interfaces.eth_p_oui); +#endif /* CONFIG_ETH_P_OUI */ +#ifdef CONFIG_DPP + os_memset(&dpp_conf, 0, sizeof(dpp_conf)); + 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; +#endif /* CONFIG_DPP */ + + for (;;) { + c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:q"); + if (c < 0) + break; + switch (c) { + case 'h': + usage(); + break; + case 'd': + debug++; + if (wpa_debug_level > 0) + wpa_debug_level--; + break; + case 'B': + daemonize++; + break; + case 'e': + entropy_file = optarg; + break; + case 'f': + log_file = optarg; + break; + case 'K': + wpa_debug_show_keys++; + break; + case 'P': + os_free(pid_file); + pid_file = os_rel2abs_path(optarg); + break; + case 't': + wpa_debug_timestamp++; + break; +#ifdef CONFIG_DEBUG_LINUX_TRACING + case 'T': + enable_trace_dbg = 1; + break; +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + case 'v': + show_version(); + exit(1); + case 'g': + if (hostapd_get_global_ctrl_iface(&interfaces, optarg)) + return -1; + break; + case 'G': + if (hostapd_get_ctrl_iface_group(&interfaces, optarg)) + return -1; + break; + case 'b': + tmp_bss = os_realloc_array(bss_config, + num_bss_configs + 1, + sizeof(char *)); + if (tmp_bss == NULL) + goto out; + bss_config = tmp_bss; + bss_config[num_bss_configs++] = optarg; + break; +#ifdef CONFIG_DEBUG_SYSLOG + case 's': + wpa_debug_syslog = 1; + break; +#endif /* CONFIG_DEBUG_SYSLOG */ + case 'S': + start_ifaces_in_sync = 1; + break; +#ifdef CONFIG_WPS + case 'u': + return gen_uuid(optarg); +#endif /* CONFIG_WPS */ + case 'i': + if (hostapd_get_interface_names(&if_names, + &if_names_size, optarg)) + goto out; + break; + case 'q': + wpa_debug_level++; + break; + default: + usage(); + break; + } + } + + if (optind == argc && interfaces.global_iface_path == NULL && + num_bss_configs == 0) + usage(); + + wpa_msg_register_ifname_cb(hostapd_msg_ifname_cb); + + if (log_file) + wpa_debug_open_file(log_file); + if (!log_file && !wpa_debug_syslog) + wpa_debug_setup_stdout(); +#ifdef CONFIG_DEBUG_SYSLOG + if (wpa_debug_syslog) + wpa_debug_open_syslog(); +#endif /* CONFIG_DEBUG_SYSLOG */ +#ifdef CONFIG_DEBUG_LINUX_TRACING + if (enable_trace_dbg) { + int tret = wpa_debug_open_linux_tracing(); + if (tret) { + wpa_printf(MSG_ERROR, "Failed to enable trace logging"); + return -1; + } + } +#endif /* CONFIG_DEBUG_LINUX_TRACING */ + + interfaces.count = argc - optind; + if (interfaces.count || num_bss_configs) { + interfaces.iface = os_calloc(interfaces.count + num_bss_configs, + sizeof(struct hostapd_iface *)); + if (interfaces.iface == NULL) { + wpa_printf(MSG_ERROR, "malloc failed"); + return -1; + } + } + + if (hostapd_global_init(&interfaces, entropy_file)) { + wpa_printf(MSG_ERROR, "Failed to initialize global context"); + return -1; + } + + eloop_register_timeout(HOSTAPD_CLEANUP_INTERVAL, 0, + hostapd_periodic, &interfaces, NULL); + + if (fst_global_init()) { + wpa_printf(MSG_ERROR, + "Failed to initialize global FST context"); + goto out; + } + +#if defined(CONFIG_FST) && defined(CONFIG_CTRL_IFACE) + if (!fst_global_add_ctrl(fst_ctrl_cli)) + wpa_printf(MSG_WARNING, "Failed to add CLI FST ctrl"); +#endif /* CONFIG_FST && CONFIG_CTRL_IFACE */ + + /* Allocate and parse configuration for full interface files */ + for (i = 0; i < interfaces.count; i++) { + char *if_name = NULL; + + if (i < if_names_size) + if_name = if_names[i]; + + interfaces.iface[i] = hostapd_interface_init(&interfaces, + if_name, + argv[optind + i], + debug); + if (!interfaces.iface[i]) { + wpa_printf(MSG_ERROR, "Failed to initialize interface"); + goto out; + } + if (start_ifaces_in_sync) + interfaces.iface[i]->need_to_start_in_sync = 1; + } + + /* Allocate and parse configuration for per-BSS files */ + for (i = 0; i < num_bss_configs; i++) { + struct hostapd_iface *iface; + char *fname; + + wpa_printf(MSG_INFO, "BSS config: %s", bss_config[i]); + fname = os_strchr(bss_config[i], ':'); + if (fname == NULL) { + wpa_printf(MSG_ERROR, + "Invalid BSS config identifier '%s'", + bss_config[i]); + goto out; + } + *fname++ = '\0'; + iface = hostapd_interface_init_bss(&interfaces, bss_config[i], + fname, debug); + if (iface == NULL) + goto out; + for (j = 0; j < interfaces.count; j++) { + if (interfaces.iface[j] == iface) + break; + } + if (j == interfaces.count) { + struct hostapd_iface **tmp; + tmp = os_realloc_array(interfaces.iface, + interfaces.count + 1, + sizeof(struct hostapd_iface *)); + if (tmp == NULL) { + hostapd_interface_deinit_free(iface); + goto out; + } + interfaces.iface = tmp; + interfaces.iface[interfaces.count++] = iface; + } + } + + /* + * Enable configured interfaces. Depending on channel configuration, + * this may complete full initialization before returning or use a + * callback mechanism to complete setup in case of operations like HT + * co-ex scans, ACS, or DFS are needed to determine channel parameters. + * In such case, the interface will be enabled from eloop context within + * hostapd_global_run(). + */ + interfaces.terminate_on_error = interfaces.count; + for (i = 0; i < interfaces.count; i++) { + if (hostapd_driver_init(interfaces.iface[i]) || + hostapd_setup_interface(interfaces.iface[i])) + goto out; + } + + hostapd_global_ctrl_iface_init(&interfaces); + + if (hostapd_global_run(&interfaces, daemonize, pid_file)) { + wpa_printf(MSG_ERROR, "Failed to start eloop"); + goto out; + } + + ret = 0; + + out: + hostapd_global_ctrl_iface_deinit(&interfaces); + /* Deinitialize all interfaces */ + for (i = 0; i < interfaces.count; i++) { + if (!interfaces.iface[i]) + continue; + interfaces.iface[i]->driver_ap_teardown = + !!(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; + + hostapd_global_cleanup_mld(&interfaces); + +#ifdef CONFIG_DPP + dpp_global_deinit(interfaces.dpp); +#endif /* CONFIG_DPP */ + + if (interfaces.eloop_initialized) + eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL); + hostapd_global_deinit(pid_file, interfaces.eloop_initialized); + os_free(pid_file); + + wpa_debug_close_syslog(); + if (log_file) + wpa_debug_close_file(); + wpa_debug_close_linux_tracing(); + + os_free(bss_config); + + for (i = 0; i < if_names_size; i++) + os_free(if_names[i]); + os_free(if_names); + + fst_global_deinit(); + + crypto_unload(); + os_program_deinit(); + + return ret; +} diff --git a/hostapd/nt_password_hash.c b/hostapd/nt_password_hash.c new file mode 100644 index 000000000000..7064b9cb6b6a --- /dev/null +++ b/hostapd/nt_password_hash.c @@ -0,0 +1,47 @@ +/* + * hostapd - Plaintext password to NtPasswordHash + * Copyright (c) 2005, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/ms_funcs.h" + + +int main(int argc, char *argv[]) +{ + unsigned char password_hash[16]; + size_t i; + char *password, buf[64], *pos; + + if (argc > 1) + password = argv[1]; + else { + if (fgets(buf, sizeof(buf), stdin) == NULL) { + printf("Failed to read password\n"); + return 1; + } + buf[sizeof(buf) - 1] = '\0'; + pos = buf; + while (*pos != '\0') { + if (*pos == '\r' || *pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + password = buf; + } + + if (nt_password_hash((u8 *) password, strlen(password), password_hash)) + return -1; + for (i = 0; i < sizeof(password_hash); i++) + printf("%02x", password_hash[i]); + printf("\n"); + + return 0; +} diff --git a/hostapd/sae_pk_gen.c b/hostapd/sae_pk_gen.c new file mode 100644 index 000000000000..c31eff75b538 --- /dev/null +++ b/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/hostapd/wired.conf b/hostapd/wired.conf new file mode 100644 index 000000000000..956f8c53c540 --- /dev/null +++ b/hostapd/wired.conf @@ -0,0 +1,40 @@ +##### hostapd configuration file ############################################## +# Empty lines and lines starting with # are ignored + +# Example configuration file for wired authenticator. See hostapd.conf for +# more details. + +interface=eth0 +driver=wired +logger_stdout=-1 +logger_stdout_level=1 +debug=2 +dump_file=/tmp/hostapd.dump + +ieee8021x=1 +eap_reauth_period=3600 + +use_pae_group_addr=1 + + +##### RADIUS configuration #################################################### +# for IEEE 802.1X with external Authentication Server, IEEE 802.11 +# authentication with external ACL for MAC addresses, and accounting + +# The own IP address of the access point (used as NAS-IP-Address) +own_ip_addr=127.0.0.1 + +# Optional NAS-Identifier string for RADIUS messages. When used, this should be +# a unique to the NAS within the scope of the RADIUS server. For example, a +# fully qualified domain name can be used here. +nas_identifier=ap.example.com + +# RADIUS authentication server +auth_server_addr=127.0.0.1 +auth_server_port=1812 +auth_server_shared_secret=radius + +# RADIUS accounting server +acct_server_addr=127.0.0.1 +acct_server_port=1813 +acct_server_shared_secret=radius diff --git a/hostapd/wps-ap-nfc.py b/hostapd/wps-ap-nfc.py new file mode 100755 index 000000000000..258d84148fe1 --- /dev/null +++ b/hostapd/wps-ap-nfc.py @@ -0,0 +1,342 @@ +#!/usr/bin/python +# +# Example nfcpy to hostapd wrapper for WPS NFC operations +# Copyright (c) 2012-2013, Jouni Malinen +# +# This software may be distributed under the terms of the BSD license. +# See README for more details. + +import os +import sys +import time +import argparse + +import nfc +import nfc.ndef +import nfc.llcp +import nfc.handover + +import logging + +import wpaspy + +wpas_ctrl = '/var/run/hostapd' +continue_loop = True +summary_file = None +success_file = None + +def summary(txt): + print(txt) + if summary_file: + with open(summary_file, 'a') as f: + f.write(txt + "\n") + +def success_report(txt): + summary(txt) + if success_file: + with open(success_file, 'a') as f: + f.write(txt + "\n") + +def wpas_connect(): + ifaces = [] + if os.path.isdir(wpas_ctrl): + try: + ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)] + except OSError as error: + print("Could not find hostapd: ", error) + return None + + if len(ifaces) < 1: + print("No hostapd control interface found") + return None + + for ctrl in ifaces: + try: + wpas = wpaspy.Ctrl(ctrl) + return wpas + except Exception as e: + pass + return None + + +def wpas_tag_read(message): + wpas = wpas_connect() + if (wpas == None): + return False + if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")): + return False + return True + + +def wpas_get_config_token(): + wpas = wpas_connect() + if (wpas == None): + return None + ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF") + if "FAIL" in ret: + return None + return ret.rstrip().decode("hex") + + +def wpas_get_password_token(): + wpas = wpas_connect() + if (wpas == None): + return None + ret = wpas.request("WPS_NFC_TOKEN NDEF") + if "FAIL" in ret: + return None + return ret.rstrip().decode("hex") + + +def wpas_get_handover_sel(): + wpas = wpas_connect() + if (wpas == None): + return None + ret = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR") + if "FAIL" in ret: + return None + return ret.rstrip().decode("hex") + + +def wpas_report_handover(req, sel): + wpas = wpas_connect() + if (wpas == None): + return None + return wpas.request("NFC_REPORT_HANDOVER RESP WPS " + + str(req).encode("hex") + " " + + str(sel).encode("hex")) + + +class HandoverServer(nfc.handover.HandoverServer): + def __init__(self, llc): + super(HandoverServer, self).__init__(llc) + self.ho_server_processing = False + self.success = False + + # override to avoid parser error in request/response.pretty() in nfcpy + # due to new WSC handover format + def _process_request(self, request): + summary("received handover request {}".format(request.type)) + response = nfc.ndef.Message("\xd1\x02\x01Hs\x12") + if not request.type == 'urn:nfc:wkt:Hr': + summary("not a handover request") + else: + try: + request = nfc.ndef.HandoverRequestMessage(request) + except nfc.ndef.DecodeError as e: + summary("error decoding 'Hr' message: {}".format(e)) + else: + response = self.process_request(request) + summary("send handover response {}".format(response.type)) + return response + + def process_request(self, request): + summary("HandoverServer - request received") + try: + print("Parsed handover request: " + request.pretty()) + except Exception as e: + print(e) + print(str(request).encode("hex")) + + sel = nfc.ndef.HandoverSelectMessage(version="1.2") + + for carrier in request.carriers: + print("Remote carrier type: " + carrier.type) + if carrier.type == "application/vnd.wfa.wsc": + summary("WPS carrier type match - add WPS carrier record") + data = wpas_get_handover_sel() + if data is None: + summary("Could not get handover select carrier record from hostapd") + continue + print("Handover select carrier record from hostapd:") + print(data.encode("hex")) + if "OK" in wpas_report_handover(carrier.record, data): + success_report("Handover reported successfully") + else: + summary("Handover report rejected") + + message = nfc.ndef.Message(data); + sel.add_carrier(message[0], "active", message[1:]) + + print("Handover select:") + try: + print(sel.pretty()) + except Exception as e: + print(e) + print(str(sel).encode("hex")) + + summary("Sending handover select") + self.success = True + return sel + + +def wps_tag_read(tag): + success = False + if len(tag.ndef.message): + for record in tag.ndef.message: + print("record type " + record.type) + if record.type == "application/vnd.wfa.wsc": + summary("WPS tag - send to hostapd") + success = wpas_tag_read(tag.ndef.message) + break + else: + summary("Empty tag") + + if success: + success_report("Tag read succeeded") + + return success + + +def rdwr_connected_write(tag): + summary("Tag found - writing - " + str(tag)) + global write_data + tag.ndef.message = str(write_data) + success_report("Tag write succeeded") + print("Done - remove tag") + global only_one + if only_one: + global continue_loop + continue_loop = False + global write_wait_remove + while write_wait_remove and tag.is_present: + time.sleep(0.1) + +def wps_write_config_tag(clf, wait_remove=True): + summary("Write WPS config token") + global write_data, write_wait_remove + write_wait_remove = wait_remove + write_data = wpas_get_config_token() + if write_data == None: + summary("Could not get WPS config token from hostapd") + return + + print("Touch an NFC tag") + clf.connect(rdwr={'on-connect': rdwr_connected_write}) + + +def wps_write_password_tag(clf, wait_remove=True): + summary("Write WPS password token") + global write_data, write_wait_remove + write_wait_remove = wait_remove + write_data = wpas_get_password_token() + if write_data == None: + summary("Could not get WPS password token from hostapd") + return + + print("Touch an NFC tag") + clf.connect(rdwr={'on-connect': rdwr_connected_write}) + + +def rdwr_connected(tag): + global only_one, no_wait + summary("Tag connected: " + str(tag)) + + if tag.ndef: + print("NDEF tag: " + tag.type) + try: + print(tag.ndef.message.pretty()) + except Exception as e: + print(e) + success = wps_tag_read(tag) + if only_one and success: + global continue_loop + continue_loop = False + else: + summary("Not an NDEF tag - remove tag") + return True + + return not no_wait + + +def llcp_startup(clf, llc): + print("Start LLCP server") + global srv + srv = HandoverServer(llc) + return llc + +def llcp_connected(llc): + print("P2P LLCP connected") + global wait_connection + wait_connection = False + global srv + srv.start() + return True + + +def main(): + clf = nfc.ContactlessFrontend() + + parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations') + parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO, + action='store_const', dest='loglevel', + help='verbose debug output') + parser.add_argument('-q', const=logging.WARNING, action='store_const', + dest='loglevel', help='be quiet') + parser.add_argument('--only-one', '-1', action='store_true', + help='run only one operation and exit') + parser.add_argument('--no-wait', action='store_true', + help='do not wait for tag to be removed before exiting') + parser.add_argument('--summary', + help='summary file for writing status updates') + parser.add_argument('--success', + help='success file for writing success update') + parser.add_argument('command', choices=['write-config', + 'write-password'], + nargs='?') + args = parser.parse_args() + + global only_one + only_one = args.only_one + + global no_wait + no_wait = args.no_wait + + if args.summary: + global summary_file + summary_file = args.summary + + if args.success: + global success_file + success_file = args.success + + logging.basicConfig(level=args.loglevel) + + try: + if not clf.open("usb"): + print("Could not open connection with an NFC device") + raise SystemExit + + if args.command == "write-config": + wps_write_config_tag(clf, wait_remove=not args.no_wait) + raise SystemExit + + if args.command == "write-password": + wps_write_password_tag(clf, wait_remove=not args.no_wait) + raise SystemExit + + global continue_loop + while continue_loop: + print("Waiting for a tag or peer to be touched") + wait_connection = True + try: + if not clf.connect(rdwr={'on-connect': rdwr_connected}, + llcp={'on-startup': llcp_startup, + 'on-connect': llcp_connected}): + break + except Exception as e: + print("clf.connect failed") + + global srv + if only_one and srv and srv.success: + raise SystemExit + + except KeyboardInterrupt: + raise SystemExit + finally: + clf.close() + + raise SystemExit + +if __name__ == '__main__': + main()